From acfe5bd33ba24d14b557ef38a9ac398052449d6d Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 18 May 2022 10:40:04 -0700 Subject: [PATCH] net/dns{., resolver}: time out DNS queries after 10 seconds (#4690) Fixes https://github.com/tailscale/corp/issues/5198 The upstream forwarder will block indefinitely on `udpconn.ReadFrom` if no reply is recieved, due to the lack of deadline on the connection object. There still isn't a deadline on the connection object, but the automatic closing of the context on deadline expiry will close the connection via `closeOnCtxDone`, unblocking the read and resulting in a normal teardown. Signed-off-by: Tom DNetto --- net/dns/manager.go | 5 ++++- net/dns/resolver/tsdns.go | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/net/dns/manager.go b/net/dns/manager.go index 59bcdcf17..82dd5d47b 100644 --- a/net/dns/manager.go +++ b/net/dns/manager.go @@ -331,6 +331,9 @@ func (m *Manager) NextPacket() ([]byte, error) { return buf, nil } +// Query executes a DNS query recieved from the given address. The query is +// provided in bs as a wire-encoded DNS query without any transport header. +// This method is called for requests arriving over UDP and TCP. func (m *Manager) Query(ctx context.Context, bs []byte, from netaddr.IPPort) ([]byte, error) { select { case <-m.ctx.Done(): @@ -460,7 +463,7 @@ func (m *Manager) HandleTCPConn(conn net.Conn, srcAddr netaddr.IPPort) { responses: make(chan []byte), readClosing: make(chan struct{}), } - s.ctx, s.closeCtx = context.WithCancel(context.Background()) + s.ctx, s.closeCtx = context.WithCancel(m.ctx) go s.handleReads() s.handleWrites() } diff --git a/net/dns/resolver/tsdns.go b/net/dns/resolver/tsdns.go index 27aa87e04..b1ee65142 100644 --- a/net/dns/resolver/tsdns.go +++ b/net/dns/resolver/tsdns.go @@ -256,6 +256,12 @@ func (r *Resolver) Close() { r.forwarder.Close() } +// dnsQueryTimeout is not intended to be user-visible (the users +// DNS resolver will retry well before that), just put an upper +// bound on per-query resource usage. +const dnsQueryTimeout = 10 * time.Second + + func (r *Resolver) Query(ctx context.Context, bs []byte, from netaddr.IPPort) ([]byte, error) { metricDNSQueryLocal.Add(1) select { @@ -268,7 +274,7 @@ func (r *Resolver) Query(ctx context.Context, bs []byte, from netaddr.IPPort) ([ out, err := r.respond(bs) if err == errNotOurName { responses := make(chan packet, 1) - ctx, cancel := context.WithCancel(ctx) + ctx, cancel := context.WithTimeout(ctx, dnsQueryTimeout) defer close(responses) defer cancel() err = r.forwarder.forwardWithDestChan(ctx, packet{bs, from}, responses)