From 66b4a363bdae217b0ad41924e13e12e67eccea02 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 11 Nov 2022 08:57:36 -0800 Subject: [PATCH] net/dns/resolver: add yet another 4via6 DNS form that's hopefully more robust $ dig +short @100.100.100.100 aaaa 10-2-5-3-via-7.foo-bar.ts.net fd7a:115c:a1e0:b1a:0:7:a02:503 $ dig +short @100.100.100.100 aaaa 10-2-5-3-via-7 fd7a:115c:a1e0:b1a:0:7:a02:503 $ ping 10-2-5-3-via-7 PING 10-2-5-3-via-7(fd7a:115c:a1e0:b1a:0:7:a02:503 (fd7a:115c:a1e0:b1a:0:7:a02:503)) 56 data bytes ... Change-Id: Ice8f954518a6a2fca8b2c04da7f31f61d78cdec4 Signed-off-by: Brad Fitzpatrick --- net/dns/resolver/tsdns.go | 28 ++++++++++++++++++++++------ net/dns/resolver/tsdns_test.go | 12 ++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/net/dns/resolver/tsdns.go b/net/dns/resolver/tsdns.go index e6a0b3660..74d7fd0e7 100644 --- a/net/dns/resolver/tsdns.go +++ b/net/dns/resolver/tsdns.go @@ -550,7 +550,7 @@ func (r *Resolver) resolveLocal(domain dnsname.FQDN, typ dns.Type) (netip.Addr, return tsaddr.TailscaleServiceIPv6(), dns.RCodeSuccess } } - // Special-case: 'via-.' queries. + // Special-case: 4via6 DNS names. if ip, ok := r.parseViaDomain(domain, typ); ok { return ip, dns.RCodeSuccess } @@ -630,7 +630,9 @@ func (r *Resolver) resolveLocal(domain dnsname.FQDN, typ dns.Type) (netip.Addr, } // parseViaDomain synthesizes an IP address for quad-A DNS requests of the form -// `.via-` and the deprecated form `via-.`, +// `-via-[.*]`. Two prior formats that +// didn't pan out (due to a Chrome issue and DNS search ndots issues) were +// `.via-` and the older `via-.`, // where X is a decimal, or hex-encoded number with a '0x' prefix. // // This exists as a convenient mapping into Tailscales 'Via Range'. @@ -650,14 +652,28 @@ func (r *Resolver) parseViaDomain(domain dnsname.FQDN, typ dns.Type) (netip.Addr var siteID string var ip4Str string - if strings.HasPrefix(fqdn, "via-") { + switch { + case strings.Contains(fqdn, "-via-"): + // Format number 3: "192-168-1-2-via-7" or "192-168-1-2-via-7.foo.ts.net." + // Third time's a charm. The earlier two formats follow after this block. + firstLabel, domain, _ := strings.Cut(fqdn, ".") // "192-168-1-2-via-7" + if !(domain == "" || dnsname.HasSuffix(domain, "ts.net") || dnsname.HasSuffix(domain, "tailscale.net")) { + return netip.Addr{}, false + } + v4hyphens, suffix, ok := strings.Cut(firstLabel, "-via-") + if !ok { + return netip.Addr{}, false + } + siteID = suffix + ip4Str = strings.ReplaceAll(v4hyphens, "-", ".") + case strings.HasPrefix(fqdn, "via-"): firstDot := strings.Index(fqdn, ".") if firstDot < 0 { return netip.Addr{}, false // missing dot delimiters } siteID = fqdn[len("via-"):firstDot] ip4Str = fqdn[firstDot+1:] - } else { + default: lastDot := strings.LastIndex(fqdn, ".") if lastDot < 0 { return netip.Addr{}, false // missing dot delimiters @@ -672,12 +688,12 @@ func (r *Resolver) parseViaDomain(domain dnsname.FQDN, typ dns.Type) (netip.Addr ip4, err := netip.ParseAddr(ip4Str) if err != nil { - return netip.Addr{}, false // badly formed, dont respond + return netip.Addr{}, false // badly formed, don't respond } prefix, err := strconv.ParseUint(siteID, 0, 32) if err != nil { - return netip.Addr{}, false // badly formed, dont respond + return netip.Addr{}, false // badly formed, don't respond } // MapVia will never error when given an ipv4 netip.Prefix. diff --git a/net/dns/resolver/tsdns_test.go b/net/dns/resolver/tsdns_test.go index 774de0a2a..5498a7d41 100644 --- a/net/dns/resolver/tsdns_test.go +++ b/net/dns/resolver/tsdns_test.go @@ -350,6 +350,18 @@ func TestResolveLocal(t *testing.T) { {"x_via_dec", dnsname.FQDN("1.0.0.10.via-1."), dns.TypeAAAA, netip.MustParseAddr("fd7a:115c:a1e0:b1a:0:1:1.0.0.10"), dns.RCodeSuccess}, {"via_invalid", dnsname.FQDN("via-."), dns.TypeAAAA, netip.Addr{}, dns.RCodeRefused}, {"via_invalid_2", dnsname.FQDN("2.3.4.5.via-."), dns.TypeAAAA, netip.Addr{}, dns.RCodeRefused}, + + // Hyphenated 4via6 format. + // Without any suffix domain: + {"via_form3_hex_bare", dnsname.FQDN("1-2-3-4-via-0xff."), dns.TypeAAAA, netip.MustParseAddr("fd7a:115c:a1e0:b1a:0:ff:1.2.3.4"), dns.RCodeSuccess}, + {"via_form3_dec_bare", dnsname.FQDN("1-2-3-4-via-1."), dns.TypeAAAA, netip.MustParseAddr("fd7a:115c:a1e0:b1a:0:1:1.2.3.4"), dns.RCodeSuccess}, + // With a Tailscale domain: + {"via_form3_dec_ts.net", dnsname.FQDN("1-2-3-4-via-1.foo.ts.net."), dns.TypeAAAA, netip.MustParseAddr("fd7a:115c:a1e0:b1a:0:1:1.2.3.4"), dns.RCodeSuccess}, + {"via_form3_dec_tailscale.net", dnsname.FQDN("1-2-3-4-via-1.foo.tailscale.net."), dns.TypeAAAA, netip.MustParseAddr("fd7a:115c:a1e0:b1a:0:1:1.2.3.4"), dns.RCodeSuccess}, + // Non-Tailscale domain suffixes aren't allowed for now: (the allowed + // suffixes are currently hard-coded and not plumbed via the netmap) + {"via_form3_dec_example.com", dnsname.FQDN("1-2-3-4-via-1.example.com."), dns.TypeAAAA, netip.Addr{}, dns.RCodeRefused}, + {"via_form3_dec_examplets.net", dnsname.FQDN("1-2-3-4-via-1.examplets.net."), dns.TypeAAAA, netip.Addr{}, dns.RCodeRefused}, } for _, tt := range tests {