diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 4ab9ef761..e5f29d807 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -586,10 +586,7 @@ func (s *DefaultServer) registerFallback(config HostDNSConfig) { continue } - ns = fmt.Sprintf("%s:%d", ns, defaultPort) - if ip, err := netip.ParseAddr(ns); err == nil && ip.Is6() { - ns = fmt.Sprintf("[%s]:%d", ns, defaultPort) - } + ns = formatAddr(ns, defaultPort) handler.upstreamServers = append(handler.upstreamServers, ns) } @@ -774,7 +771,15 @@ func (s *DefaultServer) updateMux(muxUpdates []handlerWrapper) { } func getNSHostPort(ns nbdns.NameServer) string { - return fmt.Sprintf("%s:%d", ns.IP.String(), ns.Port) + return formatAddr(ns.IP.String(), ns.Port) +} + +// formatAddr formats a nameserver address with port, handling IPv6 addresses properly +func formatAddr(address string, port int) string { + if ip, err := netip.ParseAddr(address); err == nil && ip.Is6() { + return fmt.Sprintf("[%s]:%d", address, port) + } + return fmt.Sprintf("%s:%d", address, port) } // upstreamCallbacks returns two functions, the first one is used to deactivate diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index 3cab4517a..50444a86f 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -2053,3 +2053,56 @@ func TestLocalResolverPriorityConstants(t *testing.T) { assert.Equal(t, PriorityLocal, localMuxUpdates[0].priority, "Local handler should use PriorityLocal") assert.Equal(t, "local.example.com", localMuxUpdates[0].domain) } + +func TestFormatAddr(t *testing.T) { + tests := []struct { + name string + address string + port int + expected string + }{ + { + name: "IPv4 address", + address: "8.8.8.8", + port: 53, + expected: "8.8.8.8:53", + }, + { + name: "IPv4 address with custom port", + address: "1.1.1.1", + port: 5353, + expected: "1.1.1.1:5353", + }, + { + name: "IPv6 address", + address: "fd78:94bf:7df8::1", + port: 53, + expected: "[fd78:94bf:7df8::1]:53", + }, + { + name: "IPv6 address with custom port", + address: "2001:db8::1", + port: 5353, + expected: "[2001:db8::1]:5353", + }, + { + name: "IPv6 localhost", + address: "::1", + port: 53, + expected: "[::1]:53", + }, + { + name: "Invalid address treated as hostname", + address: "dns.example.com", + port: 53, + expected: "dns.example.com:53", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := formatAddr(tt.address, tt.port) + assert.Equal(t, tt.expected, result) + }) + } +}