fix(backend): 修复转发监听 IP 为 IPv6 时报missing port in address (#397)

* fix(backend): handle IPv6 forward bind IP ports

* test(handler): add IPv6 bindIP test cases for forward service config

---------

Co-authored-by: sagit <36596628+Sagit-chu@users.noreply.github.com>
Co-authored-by: sagitchu <sagitchu@gmail.com>
This commit is contained in:
qimaoww
2026-03-31 11:24:56 +08:00
committed by GitHub
parent 5efe790937
commit 841d43344a
2 changed files with 61 additions and 3 deletions
@@ -1577,10 +1577,11 @@ func buildForwardServiceConfigs(baseName string, forward *forwardRecord, tunnel
}
var serviceAddr string
if bindIP != "" {
if strings.Contains(bindIP, ":") {
serviceAddr = processServerAddress(bindIP)
trimmedBindIP := strings.TrimSpace(bindIP)
if _, _, err := net.SplitHostPort(trimmedBindIP); err == nil {
serviceAddr = processServerAddress(trimmedBindIP)
} else {
serviceAddr = processServerAddress(fmt.Sprintf("%s:%d", bindIP, port))
serviceAddr = processServerAddress(net.JoinHostPort(strings.Trim(trimmedBindIP, "[]"), strconv.Itoa(port)))
}
} else {
serviceAddr = processServerAddress(fmt.Sprintf("%s:%d", listenerAddr, port))
@@ -421,6 +421,63 @@ func TestBuildForwardServiceConfigs_BindIPAlreadyContainsPort(t *testing.T) {
}
}
func TestBuildForwardServiceConfigs_IPv6BindIP(t *testing.T) {
tests := []struct {
name string
bindIP string
port int
wantAddr string
}{
{
name: "pure ipv6 without port",
bindIP: "2001:db8::1",
port: 22000,
wantAddr: "[2001:db8::1]:22000",
},
{
name: "bracketed ipv6 without port",
bindIP: "[2001:db8::2]",
port: 22001,
wantAddr: "[2001:db8::2]:22001",
},
{
name: "bracketed ipv6 with port",
bindIP: "[2001:db8::3]:8080",
port: 55555,
wantAddr: "[2001:db8::3]:8080",
},
{
name: "ipv6 link-local with zone",
bindIP: "fe80::1%eth0",
port: 22002,
wantAddr: "[fe80::1%eth0]:22002",
},
{
name: "ipv6 localhost",
bindIP: "::1",
port: 22003,
wantAddr: "[::1]:22003",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
forward := &forwardRecord{RemoteAddr: "1.2.3.4:80", Strategy: "fifo", TunnelID: 7}
node := &nodeRecord{TCPListenAddr: "[::]", UDPListenAddr: "[::]"}
services := buildForwardServiceConfigs("1_2_0", forward, nil, node, tt.port, tt.bindIP, nil, false)
if len(services) != 2 {
t.Fatalf("expected 2 services, got %d", len(services))
}
for _, svc := range services {
addr, _ := svc["addr"].(string)
if addr != tt.wantAddr {
t.Fatalf("expected addr %q, got %q", tt.wantAddr, addr)
}
}
})
}
}
func TestProcessServerAddress_StripsURLSchemeAndPath(t *testing.T) {
tests := []struct {
name string