diff --git a/proxy/cache_internal_test.go b/proxy/cache_internal_test.go index 3fd91fdd6..fd6a43c91 100644 --- a/proxy/cache_internal_test.go +++ b/proxy/cache_internal_test.go @@ -75,7 +75,9 @@ func TestServeCached(t *testing.T) { dnsProxy.cache.set(reply, upstreamWithAddr, testLogger) // Create a DNS-over-UDP client connection. - addr := dnsProxy.Addr(ProtoUDP) + addr, err := dnsProxy.Addr(ProtoUDP) + require.NoError(t, err) + client := &dns.Client{ Net: string(ProtoUDP), Timeout: testTimeout, diff --git a/proxy/dns64_internal_test.go b/proxy/dns64_internal_test.go index 8fcc58611..a85b84347 100644 --- a/proxy/dns64_internal_test.go +++ b/proxy/dns64_internal_test.go @@ -65,11 +65,13 @@ func TestDNS64Race(t *testing.T) { g := &sync.WaitGroup{} g.Add(testMessagesCount) - addr := dnsProxy.Addr(ProtoTCP).String() + addr, err := dnsProxy.Addr(ProtoTCP) + require.NoError(t, err) + for range testMessagesCount { // The [dns.Conn] isn't safe for concurrent use despite the requirements // from the [net.Conn] documentation. - conn, err := dns.Dial("tcp", addr) + conn, err := dns.Dial("tcp", addr.String()) require.NoError(t, err) go sendTestAAAAMessageAsync(conn, g, ipv4OnlyFqdn, syncCh) diff --git a/proxy/handler_internal_test.go b/proxy/handler_internal_test.go index 82d56a18b..86b96c54e 100644 --- a/proxy/handler_internal_test.go +++ b/proxy/handler_internal_test.go @@ -51,7 +51,9 @@ func TestFilteringHandler(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) // Create a DNS-over-UDP client connection - addr := dnsProxy.Addr(ProtoUDP) + addr, err := dnsProxy.Addr(ProtoUDP) + require.NoError(t, err) + client := &dns.Client{ Net: string(ProtoUDP), Timeout: testTimeout, diff --git a/proxy/pending_test.go b/proxy/pending_test.go index fa75c6f62..0e9e743da 100644 --- a/proxy/pending_test.go +++ b/proxy/pending_test.go @@ -123,7 +123,9 @@ func TestPendingRequests(t *testing.T) { servicetest.RequireRun(t, p, testTimeout) - addr := p.Addr(proxy.ProtoTCP).String() + addr, err := p.Addr(proxy.ProtoTCP) + require.NoError(t, err) + client := &dns.Client{ Net: string(proxy.ProtoTCP), Timeout: testTimeout, @@ -142,7 +144,7 @@ func TestPendingRequests(t *testing.T) { defer resolveWG.Done() reqCtx := testutil.ContextWithTimeout(t, testTimeout) - responses[i], _, errs[i] = client.ExchangeContext(reqCtx, req, addr) + responses[i], _, errs[i] = client.ExchangeContext(reqCtx, req, addr.String()) }() } diff --git a/proxy/proxy.go b/proxy/proxy.go index c5ac9b4ac..d34d754a0 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -479,21 +479,21 @@ func collectAddrs[A any](listeners []A, af addrFunc[A]) (addrs []net.Addr) { // Addrs returns all listen addresses for the specified proto or nil if the // proxy does not listen to it. proto must be one of [Proto]: [ProtoTCP], // [ProtoUDP], [ProtoTLS], [ProtoHTTPS], [ProtoQUIC], or [ProtoDNSCrypt]. -func (p *Proxy) Addrs(proto Proto) (addrs []net.Addr) { +func (p *Proxy) Addrs(proto Proto) (addrs []net.Addr, err error) { p.RLock() defer p.RUnlock() switch proto { case ProtoTCP: - return collectAddrs(p.tcpListen, net.Listener.Addr) + return collectAddrs(p.tcpListen, net.Listener.Addr), nil case ProtoTLS: - return collectAddrs(p.tlsListen, net.Listener.Addr) + return collectAddrs(p.tlsListen, net.Listener.Addr), nil case ProtoHTTPS: - return collectAddrs(p.httpsListen, net.Listener.Addr) + return collectAddrs(p.httpsListen, net.Listener.Addr), nil case ProtoUDP: - return collectAddrs(p.udpListen, (*net.UDPConn).LocalAddr) + return collectAddrs(p.udpListen, (*net.UDPConn).LocalAddr), nil case ProtoQUIC: - return collectAddrs(p.quicListen, (*quic.EarlyListener).Addr) + return collectAddrs(p.quicListen, (*quic.EarlyListener).Addr), nil case ProtoDNSCrypt: // Using only UDP addrs here // @@ -501,10 +501,9 @@ func (p *Proxy) Addrs(proto Proto) (addrs []net.Addr) { // ProtoDNSCryptTCP/ProtoDNSCryptUDP or we should change the // configuration so that it was not possible to set different ports for // TCP/UDP listeners. - return collectAddrs(p.dnsCryptUDPListen, (*net.UDPConn).LocalAddr) + return collectAddrs(p.dnsCryptUDPListen, (*net.UDPConn).LocalAddr), nil default: - // TODO(e.burkov): Use [errors.ErrBadEnumValue]. - panic("proto must be 'tcp', 'tls', 'https', 'quic', 'dnscrypt' or 'udp'") + return nil, fmt.Errorf("proto: %w: %q", errors.ErrBadEnumValue, proto) } } @@ -521,25 +520,25 @@ func firstAddr[A any](listeners []A, af addrFunc[A]) (addr net.Addr) { // Addr returns the first listen address for the specified proto or nil if the // proxy does not listen to it. proto must be one of [Proto]: [ProtoTCP], // [ProtoUDP], [ProtoTLS], [ProtoHTTPS], [ProtoQUIC], or [ProtoDNSCrypt]. -func (p *Proxy) Addr(proto Proto) (addr net.Addr) { +func (p *Proxy) Addr(proto Proto) (addr net.Addr, err error) { p.RLock() defer p.RUnlock() switch proto { case ProtoTCP: - return firstAddr(p.tcpListen, net.Listener.Addr) + return firstAddr(p.tcpListen, net.Listener.Addr), nil case ProtoTLS: - return firstAddr(p.tlsListen, net.Listener.Addr) + return firstAddr(p.tlsListen, net.Listener.Addr), nil case ProtoHTTPS: - return firstAddr(p.httpsListen, net.Listener.Addr) + return firstAddr(p.httpsListen, net.Listener.Addr), nil case ProtoUDP: - return firstAddr(p.udpListen, (*net.UDPConn).LocalAddr) + return firstAddr(p.udpListen, (*net.UDPConn).LocalAddr), nil case ProtoQUIC: - return firstAddr(p.quicListen, (*quic.EarlyListener).Addr) + return firstAddr(p.quicListen, (*quic.EarlyListener).Addr), nil case ProtoDNSCrypt: - return firstAddr(p.dnsCryptUDPListen, (*net.UDPConn).LocalAddr) + return firstAddr(p.dnsCryptUDPListen, (*net.UDPConn).LocalAddr), nil default: - panic("proto must be 'tcp', 'tls', 'https', 'quic', 'dnscrypt' or 'udp'") + return nil, fmt.Errorf("proto: %w: %q", errors.ErrBadEnumValue, proto) } } diff --git a/proxy/proxy_internal_test.go b/proxy/proxy_internal_test.go index f4368f86c..100ba7fb8 100644 --- a/proxy/proxy_internal_test.go +++ b/proxy/proxy_internal_test.go @@ -18,6 +18,7 @@ import ( "github.com/AdguardTeam/dnsproxy/internal/dnsproxytest" "github.com/AdguardTeam/dnsproxy/upstream" glcache "github.com/AdguardTeam/golibs/cache" + "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" @@ -313,7 +314,9 @@ func TestProxyRace(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) // Create a DNS-over-UDP client connection - addr := dnsProxy.Addr(ProtoUDP) + addr, err := dnsProxy.Addr(ProtoUDP) + require.NoError(t, err) + conn, err := dns.Dial("udp", addr.String()) require.NoError(t, err) @@ -610,7 +613,9 @@ func TestExchangeWithReservedDomains(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) // Create a DNS-over-TCP client connection. - addr := dnsProxy.Addr(ProtoTCP) + addr, err := dnsProxy.Addr(ProtoTCP) + require.NoError(t, err) + conn, err := dns.Dial("tcp", addr.String()) require.NoError(t, err) @@ -675,7 +680,9 @@ func TestOneByOneUpstreamsExchange(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) // create a DNS-over-TCP client connection - addr := dnsProxy.Addr(ProtoTCP) + addr, err := dnsProxy.Addr(ProtoTCP) + require.NoError(t, err) + conn, err := dns.Dial("tcp", addr.String()) require.NoError(t, err) @@ -771,7 +778,10 @@ func TestFallback(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) - conn, err := dns.Dial("tcp", dnsProxy.Addr(ProtoTCP).String()) + addr, err := dnsProxy.Addr(ProtoTCP) + require.NoError(t, err) + + conn, err := dns.Dial("tcp", addr.String()) require.NoError(t, err) testCases := []struct { @@ -856,7 +866,9 @@ func TestFallbackFromInvalidBootstrap(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) // Create a DNS-over-UDP client connection - addr := dnsProxy.Addr(ProtoUDP) + addr, err := dnsProxy.Addr(ProtoUDP) + require.NoError(t, err) + conn, err := dns.Dial("udp", addr.String()) require.NoError(t, err) @@ -878,7 +890,9 @@ func TestFallbackFromInvalidBootstrap(t *testing.T) { func TestResponseInRequest(t *testing.T) { dnsProxy := mustStartDefaultProxy(t) - addr := dnsProxy.Addr(ProtoUDP) + addr, err := dnsProxy.Addr(ProtoUDP) + require.NoError(t, err) + client := &dns.Client{ Net: string(ProtoUDP), Timeout: testTimeout, @@ -1508,3 +1522,21 @@ func TestProxy_validateRequest(t *testing.T) { }) } } + +func TestProxy_Addr_InvalidProto(t *testing.T) { + t.Parallel() + + p := &Proxy{} + _, err := p.Addr(Proto("invalid")) + require.Error(t, err) + require.ErrorIs(t, err, errors.ErrBadEnumValue) +} + +func TestProxy_Addrs_InvalidProto(t *testing.T) { + t.Parallel() + + p := &Proxy{} + _, err := p.Addrs(Proto("invalid")) + require.Error(t, err) + require.ErrorIs(t, err, errors.ErrBadEnumValue) +} diff --git a/proxy/serverdnscrypt_internal_test.go b/proxy/serverdnscrypt_internal_test.go index 40b2215e9..0c1d84eb6 100644 --- a/proxy/serverdnscrypt_internal_test.go +++ b/proxy/serverdnscrypt_internal_test.go @@ -11,6 +11,7 @@ import ( "github.com/ameshkov/dnscrypt/v2" "github.com/ameshkov/dnsstamps" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // TODO(d.kolyshev): Remove this after quic-go has migrated to slog. @@ -66,7 +67,10 @@ func TestDNSCryptProxy(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) // Generate a DNS stamp - port := testutil.RequireTypeAssert[*net.UDPAddr](t, dnsProxy.Addr(ProtoDNSCrypt)).Port + addrProto, err := dnsProxy.Addr(ProtoDNSCrypt) + require.NoError(t, err) + + port := testutil.RequireTypeAssert[*net.UDPAddr](t, addrProto).Port addr := netutil.JoinHostPort(listenIP, uint16(port)) stamp, err := rc.CreateStamp(addr) assert.Nil(t, err) diff --git a/proxy/serverhttps_internal_test.go b/proxy/serverhttps_internal_test.go index a0e555220..9fccfb1be 100644 --- a/proxy/serverhttps_internal_test.go +++ b/proxy/serverhttps_internal_test.go @@ -435,8 +435,12 @@ func createTestHTTPClient(dnsProxy *Proxy, caPem []byte, http3Enabled bool) (cli tlsCfg *tls.Config, cfg *quic.Config, ) (*quic.Conn, error) { - addr := dnsProxy.Addr(ProtoHTTPS).String() - return quic.DialAddrEarly(ctx, addr, tlsCfg, cfg) + addr, err := dnsProxy.Addr(ProtoHTTPS) + if err != nil { + return nil, err + } + + return quic.DialAddrEarly(ctx, addr.String(), tlsCfg, cfg) }, TLSClientConfig: tlsClientConfig, QUICConfig: &quic.Config{}, @@ -448,7 +452,12 @@ func createTestHTTPClient(dnsProxy *Proxy, caPem []byte, http3Enabled bool) (cli } dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) { // Route request to the DNS-over-HTTPS server address. - return dialer.DialContext(ctx, network, dnsProxy.Addr(ProtoHTTPS).String()) + hAddr, err := dnsProxy.Addr(ProtoHTTPS) + if err != nil { + return nil, err + } + + return dialer.DialContext(ctx, network, hAddr.String()) } tlsClientConfig.NextProtos = []string{"h2", "http/1.1"} diff --git a/proxy/serverquic_internal_test.go b/proxy/serverquic_internal_test.go index 2ae952c88..6b507812f 100644 --- a/proxy/serverquic_internal_test.go +++ b/proxy/serverquic_internal_test.go @@ -44,7 +44,10 @@ func TestProxy_quic(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) - addr = testutil.RequireTypeAssert[*net.UDPAddr](t, dnsProxy.Addr(ProtoQUIC)) + addrProto, err := dnsProxy.Addr(ProtoQUIC) + require.NoError(t, err) + + addr = testutil.RequireTypeAssert[*net.UDPAddr](t, addrProto) conn, err := quic.DialAddrEarly(context.Background(), addr.String(), tlsConfig, nil) require.NoError(t, err) @@ -113,7 +116,8 @@ func TestProxy_quicLargePackets(t *testing.T) { } // Create a DNS-over-QUIC client connection. - addr := dnsProxy.Addr(ProtoQUIC) + addr, err := dnsProxy.Addr(ProtoQUIC) + require.NoError(t, err) // Open a QUIC connection. conn, err := quic.DialAddrEarly(context.Background(), addr.String(), tlsConfig, nil) @@ -181,7 +185,8 @@ func TestProxy_quicTruncatedRequest(t *testing.T) { servicetest.RequireRun(t, dnsProxy, testTimeout) - addr := dnsProxy.Addr(ProtoQUIC) + addr, err := dnsProxy.Addr(ProtoQUIC) + require.NoError(t, err) roots := x509.NewCertPool() require.True(t, roots.AppendCertsFromPEM(caPem)) diff --git a/proxy/servertcp_internal_test.go b/proxy/servertcp_internal_test.go index 39992f11f..bdd0dcb40 100644 --- a/proxy/servertcp_internal_test.go +++ b/proxy/servertcp_internal_test.go @@ -15,7 +15,9 @@ func TestProxy_tcp(t *testing.T) { dnsProxy := mustStartDefaultProxy(t) // Create a DNS-over-TCP client connection - addr := dnsProxy.Addr(ProtoTCP) + addr, err := dnsProxy.Addr(ProtoTCP) + require.NoError(t, err) + conn, err := dns.Dial("tcp", addr.String()) require.NoError(t, err) @@ -40,7 +42,9 @@ func TestProxy_tls(t *testing.T) { tlsConfig := &tls.Config{ServerName: tlsServerName, RootCAs: roots} // Create a DNS-over-TLS client connection - addr := dnsProxy.Addr(ProtoTLS) + addr, err := dnsProxy.Addr(ProtoTLS) + require.NoError(t, err) + conn, err := dns.DialWithTLS("tcp-tls", addr.String(), tlsConfig) require.NoError(t, err) diff --git a/proxy/serverudp_internal_test.go b/proxy/serverudp_internal_test.go index 7c01b48f4..b535105fc 100644 --- a/proxy/serverudp_internal_test.go +++ b/proxy/serverudp_internal_test.go @@ -11,7 +11,9 @@ func TestUdpProxy(t *testing.T) { dnsProxy := mustStartDefaultProxy(t) // Create a DNS-over-UDP client connection - addr := dnsProxy.Addr(ProtoUDP) + addr, err := dnsProxy.Addr(ProtoUDP) + require.NoError(t, err) + conn, err := dns.Dial("udp", addr.String()) require.NoError(t, err)