From 64f783f2b0ce5086c7d355e3ef3dc31d86f73c52 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Apr 2026 16:39:27 +0000 Subject: [PATCH] fix: allow same-address browser dialer port reuse across outbounds Agent-Logs-Url: https://github.com/XTLS/Xray-core/sessions/b21c3fc4-8476-4107-975a-9d921d55ffea Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com> --- transport/internet/browser_dialer/dialer.go | 10 +++++ .../internet/browser_dialer/dialer_test.go | 42 ++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/transport/internet/browser_dialer/dialer.go b/transport/internet/browser_dialer/dialer.go index 7ab7b037..9885edbb 100644 --- a/transport/internet/browser_dialer/dialer.go +++ b/transport/internet/browser_dialer/dialer.go @@ -173,6 +173,10 @@ func getDialerByAddress(addr string) (*dialerInstance, error) { if !ok { return nil, errors.New("invalid sockopt.browserDialer: ", addr) } + _, port, err := net.SplitHostPort(listenAddr) + if err != nil { + return nil, errors.New("invalid sockopt.browserDialer: ", addr) + } key := listenAddr + path @@ -191,6 +195,12 @@ func getDialerByAddress(addr string) (*dialerInstance, error) { server, found := dialerServers[listenAddr] if !found { + for existingAddr := range dialerServers { + _, existingPort, splitErr := net.SplitHostPort(existingAddr) + if splitErr == nil && existingPort == port { + return nil, errors.New("sockopt.browserDialer cannot use the same port with a different listen address: ", existingAddr, " and ", listenAddr) + } + } newServer, serverErr := newDialerServer(listenAddr) if serverErr != nil { return nil, serverErr diff --git a/transport/internet/browser_dialer/dialer_test.go b/transport/internet/browser_dialer/dialer_test.go index 9384306f..e2bdb66d 100644 --- a/transport/internet/browser_dialer/dialer_test.go +++ b/transport/internet/browser_dialer/dialer_test.go @@ -1,6 +1,10 @@ package browser_dialer -import "testing" +import ( + "net" + "strconv" + "testing" +) func TestParseBrowserDialerAddressRequireUUIDPath(t *testing.T) { valid := "127.0.0.1:8080/123e4567-e89b-12d3-a456-426614174000" @@ -20,3 +24,39 @@ func TestParseBrowserDialerAddressRequireUUIDPath(t *testing.T) { } } } + +func TestEnsureDialerWithAddressReusesSameListenAddress(t *testing.T) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + port := listener.Addr().(*net.TCPAddr).Port + listener.Close() + + addr1 := net.JoinHostPort("127.0.0.1", strconv.Itoa(port)) + "/123e4567-e89b-12d3-a456-426614174000" + addr2 := net.JoinHostPort("127.0.0.1", strconv.Itoa(port)) + "/123e4567-e89b-12d3-a456-426614174001" + if err := EnsureDialerWithAddress(addr1); err != nil { + t.Fatalf("failed to ensure first browser dialer: %v", err) + } + if err := EnsureDialerWithAddress(addr2); err != nil { + t.Fatalf("failed to reuse browser dialer listener on same address: %v", err) + } +} + +func TestEnsureDialerWithAddressRejectsSamePortDifferentAddress(t *testing.T) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + port := listener.Addr().(*net.TCPAddr).Port + listener.Close() + + addr1 := net.JoinHostPort("127.0.0.1", strconv.Itoa(port)) + "/123e4567-e89b-12d3-a456-426614174010" + addr2 := net.JoinHostPort("127.0.0.2", strconv.Itoa(port)) + "/123e4567-e89b-12d3-a456-426614174011" + if err := EnsureDialerWithAddress(addr1); err != nil { + t.Fatalf("failed to ensure first browser dialer: %v", err) + } + if err := EnsureDialerWithAddress(addr2); err == nil { + t.Fatal("expected error for same port with different listen address") + } +}