From 3f6fe1167d754d0b2472d58390a668b94c8bdea9 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sun, 31 May 2026 03:34:17 +0200 Subject: [PATCH] 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. --- sub/subService.go | 32 ++++++++++++++++++++++++++++++++ sub/subService_test.go | 15 +++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/sub/subService.go b/sub/subService.go index bd81f185..12dca3a5 100644 --- a/sub/subService.go +++ b/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) diff --git a/sub/subService_test.go b/sub/subService_test.go index 671a919b..284b230e 100644 --- a/sub/subService_test.go +++ b/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" {