net/dns/resolver: use SystemDial in DoH forwarder

This ensures that we close the underlying connection(s) when a major
link change happens. If we don't do this, on mobile platforms switching
between WiFi and cellular can result in leftover connections in the
http.Client's connection pool which are bound to the "wrong" interface.

Updates #10821
Updates tailscale/corp#19124

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: Ibd51ce2efcaf4bd68e14f6fdeded61d4e99f9a01
This commit is contained in:
Andrew Dunham 2024-04-11 14:47:30 -04:00
parent 82394debb7
commit b85c2b2313
1 changed files with 14 additions and 3 deletions

View File

@ -28,7 +28,6 @@ import (
"tailscale.com/net/dnscache"
"tailscale.com/net/neterror"
"tailscale.com/net/netmon"
"tailscale.com/net/netns"
"tailscale.com/net/sockstats"
"tailscale.com/net/tsdial"
"tailscale.com/types/dnstype"
@ -394,8 +393,20 @@ func (f *forwarder) getKnownDoHClientForProvider(urlBase string) (c *http.Client
if err != nil {
return nil, false
}
nsDialer := netns.NewDialer(f.logf, f.netMon)
dialer := dnscache.Dialer(nsDialer.DialContext, &dnscache.Resolver{
// NOTE: use f.dialer.SystemDial so we close connections on a link
// change; on mobile devices when switching between WiFi and cellular,
// we need to ensure we don't retain a connection on the old interface
// or we can block DNS resolution.
//
// NOTE: if we ever support arbitrary user-defined DoH providers, this
// isn't sufficient; we'd need a dialer that dial a DoH server on the
// internet, without going through Tailscale (as SystemDial does), but
// also can dial a node on the tailnet (e.g. a PiHole).
//
// As of the time of writing (2024-02-11), this isn't a problem because
// we only support a restricted set of public DoH providers that aren't
// on a user's tailnet.
dialer := dnscache.Dialer(f.dialer.SystemDial, &dnscache.Resolver{
SingleHost: dohURL.Hostname(),
SingleHostStaticResult: allIPs,
Logf: f.logf,