mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-06 21:25:04 +03:00
fix(server): bound the public-IP lookup and accept only an address
getPublicIP read a third-party IP echo service's body whole and cached whatever came back as the panel's address, so one misbehaving service could make the panel inflate an endless response at startup, and an error page became the address shown in every status sample. The read is capped at 64 bytes and only a parsable IP is accepted; anything else counts as unavailable. The GitHub release lookups are capped as well.
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"mime/multipart"
|
||||
stdnet "net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -402,6 +403,10 @@ type MLKEM768Response struct {
|
||||
Client string `json:"client" example:"mlkem768-client"`
|
||||
}
|
||||
|
||||
// maxPublicIPBytes caps an IP echo service's answer; a textual IPv6 address
|
||||
// with its line ending fits well within it.
|
||||
const maxPublicIPBytes = 64
|
||||
|
||||
func getPublicIP(url string) string {
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
@@ -425,17 +430,19 @@ func getPublicIP(url string) string {
|
||||
return "N/A"
|
||||
}
|
||||
|
||||
ip, err := io.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, maxPublicIPBytes))
|
||||
if err != nil {
|
||||
return "N/A"
|
||||
}
|
||||
|
||||
ipString := strings.TrimSpace(string(ip))
|
||||
if ipString == "" {
|
||||
// The answer is cached and sent with every status sample, so only a bare
|
||||
// address is accepted; an over-long one is cut above and fails to parse.
|
||||
ip, err := netip.ParseAddr(strings.TrimSpace(string(body)))
|
||||
if err != nil {
|
||||
return "N/A"
|
||||
}
|
||||
|
||||
return ipString
|
||||
return ip.WithZone("").String()
|
||||
}
|
||||
|
||||
var publicIPv4Services = []string{
|
||||
@@ -921,6 +928,9 @@ const (
|
||||
// maxXrayDigestBytes caps the .dgst checksum sidecar read; it is a few
|
||||
// hundred bytes in practice.
|
||||
maxXrayDigestBytes = 64 << 10
|
||||
// maxXrayReleasesBytes caps the GitHub release list read; a page of
|
||||
// releases with all their assets is a few MiB.
|
||||
maxXrayReleasesBytes = 32 << 20
|
||||
)
|
||||
|
||||
func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||
@@ -941,7 +951,7 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||
|
||||
// Check HTTP status code - GitHub API returns object instead of array on error
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
bodyBytes, _ := io.ReadAll(io.LimitReader(resp.Body, maxXrayReleasesBytes))
|
||||
var errorResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
@@ -953,7 +963,7 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||
|
||||
buffer := bytes.NewBuffer(make([]byte, bufferSize))
|
||||
buffer.Reset()
|
||||
if _, err := buffer.ReadFrom(resp.Body); err != nil {
|
||||
if _, err := buffer.ReadFrom(io.LimitReader(resp.Body, maxXrayReleasesBytes)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user