From 916daea4ef8692325a9912210285c9ea8d899a46 Mon Sep 17 00:00:00 2001 From: Stanislav Chzhen Date: Mon, 8 Apr 2024 13:15:06 +0300 Subject: [PATCH 1/2] Pull request 337: 6875-fastest-unspecified-ip Updates AdguardTeam/AdGuardHome#6875. Squashed commit of the following: commit ca462e8fa0e5dcb11f81009f3af56df80e3fd97d Author: Stanislav Chzhen Date: Fri Apr 5 16:12:28 2024 +0300 fastip: unspecified ip --- fastip/fastest.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastip/fastest.go b/fastip/fastest.go index 741df1528..e6368c7b0 100644 --- a/fastip/fastest.go +++ b/fastip/fastest.go @@ -78,7 +78,7 @@ func (f *FastestAddr) ExchangeFastest(req *dns.Msg, ups []upstream.Upstream) ( for _, r := range replies { for _, rr := range r.Resp.Answer { ip := ipFromRR(rr) - if !ipSet.Has(ip) && ip != (netip.Addr{}) { + if ip.IsValid() && !ip.IsUnspecified() { ipSet.Add(ip) } } From 0e2cfca51d95532e6555d6f5d42235daf23158af Mon Sep 17 00:00:00 2001 From: Eugene Burkov Date: Mon, 8 Apr 2024 16:49:06 +0300 Subject: [PATCH 2/2] Pull request 339: Upd quic-go Squashed commit of the following: commit b4ae5ccb2fb6a20dd813533a52037ef5b74583c2 Author: Eugene Burkov Date: Mon Apr 8 16:30:40 2024 +0300 all: imp code, docs, rm replacement commit 0d5637b5b7d3208eb6938e2381f69750054a4fc0 Author: Eugene Burkov Date: Mon Apr 8 16:20:16 2024 +0300 all: upd quic-go --- go.mod | 2 +- go.sum | 6 +- proxy/server_quic.go | 25 ++++--- upstream/doh_internal_test.go | 129 +++++++++++++++++----------------- upstream/doq_internal_test.go | 96 +++++++++++++------------ 5 files changed, 141 insertions(+), 117 deletions(-) diff --git a/go.mod b/go.mod index fb266e4a4..dc7f67481 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/jessevdk/go-flags v1.5.0 github.com/miekg/dns v1.1.58 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/quic-go/quic-go v0.41.0 + github.com/quic-go/quic-go v0.42.0 github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 golang.org/x/net v0.23.0 diff --git a/go.sum b/go.sum index b5a60885c..7bc0c259d 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k= -github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= +github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= +github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= @@ -69,6 +69,8 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= diff --git a/proxy/server_quic.go b/proxy/server_quic.go index 6695d3f55..080e5b288 100644 --- a/proxy/server_quic.go +++ b/proxy/server_quic.go @@ -9,6 +9,7 @@ import ( "net" "time" + "github.com/AdguardTeam/dnsproxy/internal/bootstrap" "github.com/AdguardTeam/dnsproxy/proxyutil" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" @@ -64,10 +65,21 @@ const ( func (p *Proxy) createQUICListeners() error { for _, a := range p.QUICListenAddr { log.Info("Creating a QUIC listener") + + conn, err := net.ListenUDP(bootstrap.NetworkUDP, a) + if err != nil { + return fmt.Errorf("listening to %s: %w", a, err) + } + + v := newQUICAddrValidator(quicAddrValidatorCacheSize, quicAddrValidatorCacheTTL) + transport := &quic.Transport{ + Conn: conn, + VerifySourceAddress: v.requiresValidation, + } + tlsConfig := p.TLSConfig.Clone() tlsConfig.NextProtos = compatProtoDQ - quicListen, err := quic.ListenAddrEarly( - a.String(), + quicListen, err := transport.ListenEarly( tlsConfig, newServerQUICConfig(), ) @@ -393,13 +405,10 @@ func closeQUICConn(conn quic.Connection, code quic.ApplicationErrorCode) { // newServerQUICConfig creates *quic.Config populated with the default settings. // This function is supposed to be used for both DoQ and DoH3 server. func newServerQUICConfig() (conf *quic.Config) { - v := newQUICAddrValidator(quicAddrValidatorCacheSize, quicAddrValidatorCacheTTL) - return &quic.Config{ - MaxIdleTimeout: maxQUICIdleTimeout, - MaxIncomingStreams: math.MaxUint16, - MaxIncomingUniStreams: math.MaxUint16, - RequireAddressValidation: v.requiresValidation, + MaxIdleTimeout: maxQUICIdleTimeout, + MaxIncomingStreams: math.MaxUint16, + MaxIncomingUniStreams: math.MaxUint16, // Enable 0-RTT by default for all connections on the server-side. Allow0RTT: true, } diff --git a/upstream/doh_internal_test.go b/upstream/doh_internal_test.go index 4cfd4c0f9..3240aed34 100644 --- a/upstream/doh_internal_test.go +++ b/upstream/doh_internal_test.go @@ -8,7 +8,8 @@ import ( "fmt" "net" "net/http" - "strconv" + "net/netip" + "net/url" "sync/atomic" "testing" "time" @@ -197,74 +198,69 @@ func TestUpstreamDoH_serverRestart(t *testing.T) { testCases := []struct { name string httpVersions []HTTPVersion - }{ - { - name: "http2", - httpVersions: []HTTPVersion{HTTPVersion11, HTTPVersion2}, - }, - { - name: "http3", - httpVersions: []HTTPVersion{HTTPVersion3}, - }, - } + }{{ + name: "http2", + httpVersions: []HTTPVersion{HTTPVersion11, HTTPVersion2}, + }, { + name: "http3", + httpVersions: []HTTPVersion{HTTPVersion3}, + }} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - // Run the first server instance. - srv := startDoHServer(t, testDoHServerOptions{ - http3Enabled: true, - }) - - // Create a DNS-over-HTTPS upstream. - address := fmt.Sprintf("https://%s/dns-query", srv.addr) - u, err := AddressToUpstream( - address, - &Options{ + var addr netip.AddrPort + var upsAddr string + var u Upstream + + t.Run("first_try", func(t *testing.T) { + srv := startDoHServer(t, testDoHServerOptions{ + http3Enabled: true, + }) + t.Cleanup(srv.Shutdown) + + addr = netip.MustParseAddrPort(srv.addr) + upsAddr = (&url.URL{ + Scheme: "https", + Host: addr.String(), + Path: "dns-query", + }).String() + + var err error + u, err = AddressToUpstream(upsAddr, &Options{ InsecureSkipVerify: true, HTTPVersions: tc.httpVersions, Timeout: time.Second, - }, - ) - require.NoError(t, err) - testutil.CleanupAndRequireSuccess(t, u.Close) - - // Test that the upstream works properly. - checkUpstream(t, u, address) + }) + require.NoError(t, err) - // Now let's restart the server on the same address. - _, portStr, err := net.SplitHostPort(srv.addr) - require.NoError(t, err) - port, err := strconv.Atoi(portStr) - require.NoError(t, err) + checkUpstream(t, u, upsAddr) + }) + require.False(t, t.Failed()) + testutil.CleanupAndRequireSuccess(t, u.Close) - // Shutdown the first server. - srv.Shutdown() + t.Run("second_try", func(t *testing.T) { + srv := startDoHServer(t, testDoHServerOptions{ + http3Enabled: true, + port: int(addr.Port()), + }) + t.Cleanup(srv.Shutdown) - // Start the new one on the same port. - srv = startDoHServer(t, testDoHServerOptions{ - http3Enabled: true, - port: port, + checkUpstream(t, u, upsAddr) }) + require.False(t, t.Failed()) - // Check that everything works after restart. - checkUpstream(t, u, address) - - // Stop the server again. - srv.Shutdown() + t.Run("retry", func(t *testing.T) { + _, err := u.Exchange(createTestMessage()) + require.Error(t, err) - // Now try to send a message and make sure that it returns an error. - _, err = u.Exchange(createTestMessage()) - require.Error(t, err) + srv := startDoHServer(t, testDoHServerOptions{ + http3Enabled: true, + port: int(addr.Port()), + }) + t.Cleanup(srv.Shutdown) - // Start the server one more time. - srv = startDoHServer(t, testDoHServerOptions{ - http3Enabled: true, - port: port, + checkUpstream(t, u, upsAddr) }) - defer srv.Shutdown() - - // Check that everything works after the second restart. - checkUpstream(t, u, address) }) } } @@ -429,17 +425,24 @@ func startDoHServer( // Listen UDP for the H3 server. Reuse the same port as was used for the // TCP listener. - udpAddr, uErr := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", tcpAddr.Port)) - require.NoError(t, uErr) + var udpAddr *net.UDPAddr + udpAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", tcpAddr.Port)) + require.NoError(t, err) + + var conn net.PacketConn + conn, err = net.ListenUDP("udp", udpAddr) + require.NoError(t, err) + testutil.CleanupAndRequireSuccess(t, conn.Close) + + transport := &quic.Transport{ + Conn: conn, + VerifySourceAddress: func(net.Addr) bool { return false }, + } // QUIC configuration with the 0-RTT support enabled by default. - quicConfig := &quic.Config{ - RequireAddressValidation: func(net.Addr) (ok bool) { - return true - }, + listenerH3, err = transport.ListenEarly(tlsConfigH3, &quic.Config{ Allow0RTT: true, - } - listenerH3, err = quic.ListenAddrEarly(udpAddr.String(), tlsConfigH3, quicConfig) + }) require.NoError(t, err) // Run the H3 server. diff --git a/upstream/doq_internal_test.go b/upstream/doq_internal_test.go index 9f4bf10c6..9327b55ce 100644 --- a/upstream/doq_internal_test.go +++ b/upstream/doq_internal_test.go @@ -7,7 +7,8 @@ import ( "fmt" "io" "net" - "strconv" + "net/netip" + "net/url" "sync" "testing" "time" @@ -81,51 +82,50 @@ func TestUpstreamDoQ_serverRestart(t *testing.T) { // 0-RTT connections. tlsConf, rootCAs := createServerTLSConfig(t, "127.0.0.1") - // Run the first server instance. - srv := startDoQServer(t, tlsConf, 0) - - // Create a DNS-over-QUIC upstream. - address := fmt.Sprintf("quic://%s", srv.addr) - u, err := AddressToUpstream(address, &Options{ - InsecureSkipVerify: true, - Timeout: 250 * time.Millisecond, - RootCAs: rootCAs, - }) - require.NoError(t, err) - testutil.CleanupAndRequireSuccess(t, u.Close) - - // Test that the upstream works properly. - checkUpstream(t, u, address) + var addr netip.AddrPort + var upsStr string + var u Upstream - // Now let's restart the server on the same address. - _, portStr, err := net.SplitHostPort(srv.addr) - require.NoError(t, err) + t.Run("first_try", func(t *testing.T) { + srv := startDoQServer(t, tlsConf, 0) + testutil.CleanupAndRequireSuccess(t, srv.Shutdown) - port, err := strconv.Atoi(portStr) - require.NoError(t, err) + addr = netip.MustParseAddrPort(srv.addr) + upsStr = (&url.URL{ + Scheme: "quic", + Host: addr.String(), + }).String() - // Shutdown the first server. - require.NoError(t, srv.Shutdown()) + var err error + u, err = AddressToUpstream(upsStr, &Options{ + InsecureSkipVerify: true, + Timeout: 250 * time.Millisecond, + RootCAs: rootCAs, + }) + require.NoError(t, err) - // Start the new one on the same port. - srv = startDoQServer(t, tlsConf, port) + checkUpstream(t, u, upsStr) + }) + require.False(t, t.Failed()) + testutil.CleanupAndRequireSuccess(t, u.Close) - // Check that everything works after restart. - checkUpstream(t, u, address) + t.Run("second_try", func(t *testing.T) { + srv := startDoQServer(t, tlsConf, int(addr.Port())) + testutil.CleanupAndRequireSuccess(t, srv.Shutdown) - // Stop the server again. - require.NoError(t, srv.Shutdown()) + checkUpstream(t, u, upsStr) + }) + require.False(t, t.Failed()) - // Now try to send a message and make sure that it returns an error. - _, err = u.Exchange(createTestMessage()) - require.Error(t, err) + t.Run("retry", func(t *testing.T) { + _, err := u.Exchange(createTestMessage()) + require.Error(t, err) - // Start the server one more time. - srv = startDoQServer(t, tlsConf, port) - testutil.CleanupAndRequireSuccess(t, srv.Shutdown) + srv := startDoQServer(t, tlsConf, int(addr.Port())) + testutil.CleanupAndRequireSuccess(t, srv.Shutdown) - // Check that everything works after the second restart. - checkUpstream(t, u, address) + checkUpstream(t, u, upsStr) + }) } func TestUpstreamDoQ_0RTT(t *testing.T) { @@ -275,14 +275,24 @@ func (s *testDoQServer) handleQUICStream(stream quic.Stream) (err error) { func startDoQServer(t *testing.T, tlsConf *tls.Config, port int) (s *testDoQServer) { tlsConf.NextProtos = []string{NextProtoDQ} - listen, err := quic.ListenAddrEarly( - fmt.Sprintf("127.0.0.1:%d", port), + udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", port)) + require.NoError(t, err) + + conn, err := net.ListenUDP("udp", udpAddr) + require.NoError(t, err) + testutil.CleanupAndRequireSuccess(t, conn.Close) + + transport := &quic.Transport{ + Conn: conn, + // Necessary for 0-RTT. + VerifySourceAddress: func(a net.Addr) bool { + return true + }, + } + + listen, err := transport.ListenEarly( tlsConf, &quic.Config{ - // Necessary for 0-RTT. - RequireAddressValidation: func(net.Addr) (ok bool) { - return false - }, Allow0RTT: true, }, )