Browse Source

fix(sub): don't leak loopback bind IP into link host

When the sub server is reached on a loopback/unspecified host (e.g. 127.0.0.2 from its Listen IP bind), the request host was used as the link address. Substitute the configured Subscription/Web Domain, or normalize loopback to localhost, so the sub link address matches the panel's Client Information.
MHSanaei 3 giờ trước cách đây
mục cha
commit
3f6fe1167d
2 tập tin đã thay đổi với 47 bổ sung0 xóa
  1. 32 0
      sub/subService.go
  2. 15 0
      sub/subService_test.go

+ 32 - 0
sub/subService.go

@@ -52,10 +52,42 @@ func NewSubService(showInfo bool, remarkModel string) *SubService {
 // freshly-loaded node map regardless of which sub flavour the client
 // hit.
 func (s *SubService) PrepareForRequest(host string) {
+	if !isRoutableHost(host) {
+		if d := s.configuredPublicHost(); d != "" {
+			host = d
+		} else if isLoopbackHost(host) {
+			host = "localhost"
+		}
+	}
 	s.address = host
 	s.loadNodes()
 }
 
+func (s *SubService) configuredPublicHost() string {
+	if d, err := s.settingService.GetSubDomain(); err == nil && d != "" {
+		return d
+	}
+	if d, err := s.settingService.GetWebDomain(); err == nil && d != "" {
+		return d
+	}
+	return ""
+}
+
+func isRoutableHost(host string) bool {
+	if host == "" {
+		return false
+	}
+	if ip := net.ParseIP(strings.Trim(host, "[]")); ip != nil {
+		return !ip.IsLoopback() && !ip.IsUnspecified()
+	}
+	return true
+}
+
+func isLoopbackHost(host string) bool {
+	ip := net.ParseIP(strings.Trim(host, "[]"))
+	return ip != nil && ip.IsLoopback()
+}
+
 // GetSubs retrieves subscription links for a given subscription ID and host.
 func (s *SubService) GetSubs(subId string, host string) ([]string, []string, int64, xray.ClientTraffic, error) {
 	s.PrepareForRequest(host)

+ 15 - 0
sub/subService_test.go

@@ -46,6 +46,21 @@ func TestFindClientIndex(t *testing.T) {
 	}
 }
 
+func TestIsRoutableHost(t *testing.T) {
+	routable := []string{"example.com", "sub.example.com", "10.0.0.1", "192.168.1.5", "1.2.3.4", "2001:db8::1"}
+	for _, v := range routable {
+		if !isRoutableHost(v) {
+			t.Fatalf("isRoutableHost(%q) = false, want true", v)
+		}
+	}
+	notRoutable := []string{"", "0.0.0.0", "::", "::0", "127.0.0.1", "127.0.0.2", "::1", "[::1]"}
+	for _, v := range notRoutable {
+		if isRoutableHost(v) {
+			t.Fatalf("isRoutableHost(%q) = true, want false", v)
+		}
+	}
+}
+
 func TestUnmarshalStreamSettings(t *testing.T) {
 	got := unmarshalStreamSettings(`{"network":"ws","wsSettings":{"path":"/api"}}`)
 	if got["network"] != "ws" {