mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-19 19:38:40 +00:00
wgengine/tsdns: delegate bonjour service rdns requests
While we're here, parseQuery into a plain function. This is helpful for fuzzing. (Which I did a bit of. Didn't find anything.) And clean up a few minor things. Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
This commit is contained in:
parent
2d0ed99672
commit
1fd10061fd
@ -299,7 +299,7 @@ type response struct {
|
||||
}
|
||||
|
||||
// parseQuery parses the query in given packet into a response struct.
|
||||
func (r *Resolver) parseQuery(query []byte, resp *response) error {
|
||||
func parseQuery(query []byte, resp *response) error {
|
||||
var parser dns.Parser
|
||||
var err error
|
||||
|
||||
@ -423,6 +423,35 @@ const (
|
||||
rdnsv6Suffix = ".ip6.arpa."
|
||||
)
|
||||
|
||||
// hasRDNSBonjourPrefix reports whether name has a Bonjour Service Prefix..
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc6763 lists
|
||||
// "five special RR names" for Bonjour service discovery:
|
||||
//
|
||||
// b._dns-sd._udp.<domain>.
|
||||
// db._dns-sd._udp.<domain>.
|
||||
// r._dns-sd._udp.<domain>.
|
||||
// dr._dns-sd._udp.<domain>.
|
||||
// lb._dns-sd._udp.<domain>.
|
||||
func hasRDNSBonjourPrefix(s string) bool {
|
||||
// Even the shortest name containing a Bonjour prefix is long,
|
||||
// so check length (cheap) and bail early if possible.
|
||||
if len(s) < len("*._dns-sd._udp.0.0.0.0.in-addr.arpa.") {
|
||||
return false
|
||||
}
|
||||
dot := strings.IndexByte(s, '.')
|
||||
if dot == -1 {
|
||||
return false // shouldn't happen
|
||||
}
|
||||
switch s[:dot] {
|
||||
case "b", "db", "r", "dr", "lb":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.HasPrefix(s[dot:], "._dns-sd._udp.")
|
||||
}
|
||||
|
||||
// rawNameToLower converts a raw DNS name to a string, lowercasing it.
|
||||
func rawNameToLower(name []byte) string {
|
||||
var sb strings.Builder
|
||||
@ -502,9 +531,12 @@ func rdnsNameToIPv6(name string) (ip netaddr.IP, ok bool) {
|
||||
// respondReverse returns a DNS response to a PTR query.
|
||||
// It is assumed that resp.Question is populated by respond before this is called.
|
||||
func (r *Resolver) respondReverse(query []byte, name string, resp *response) ([]byte, error) {
|
||||
if hasRDNSBonjourPrefix(name) {
|
||||
return nil, errNotOurName
|
||||
}
|
||||
|
||||
var ip netaddr.IP
|
||||
var ok bool
|
||||
var err error
|
||||
switch {
|
||||
case strings.HasSuffix(name, rdnsv4Suffix):
|
||||
ip, ok = rdnsNameToIPv4(name)
|
||||
@ -521,6 +553,7 @@ func (r *Resolver) respondReverse(query []byte, name string, resp *response) ([]
|
||||
return nil, errNotOurName
|
||||
}
|
||||
|
||||
var err error
|
||||
resp.Name, resp.Header.RCode, err = r.ResolveReverse(ip)
|
||||
if err != nil {
|
||||
r.logf("resolving rdns: %v", ip, err)
|
||||
@ -540,7 +573,7 @@ func (r *Resolver) respond(query []byte) ([]byte, error) {
|
||||
// ParseQuery is sufficiently fast to run on every DNS packet.
|
||||
// This is considerably simpler than extracting the name by hand
|
||||
// to shave off microseconds in case of delegation.
|
||||
err := r.parseQuery(query, resp)
|
||||
err := parseQuery(query, resp)
|
||||
// We will not return this error: it is the sender's fault.
|
||||
if err != nil {
|
||||
r.logf("parsing query: %v", err)
|
||||
@ -551,7 +584,7 @@ func (r *Resolver) respond(query []byte) ([]byte, error) {
|
||||
name := rawNameToLower(rawName)
|
||||
|
||||
// Always try to handle reverse lookups; delegate inside when not found.
|
||||
// This way, queries for exitent nodes do not leak,
|
||||
// This way, queries for existent nodes do not leak,
|
||||
// but we behave gracefully if non-Tailscale nodes exist in CGNATRange.
|
||||
if resp.Question.Type == dns.TypePTR {
|
||||
return r.respondReverse(query, name, resp)
|
||||
|
@ -684,6 +684,29 @@ func TestAllocs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrimRDNSBonjourPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
want bool
|
||||
}{
|
||||
{"b._dns-sd._udp.0.10.20.172.in-addr.arpa.", true},
|
||||
{"db._dns-sd._udp.0.10.20.172.in-addr.arpa.", true},
|
||||
{"r._dns-sd._udp.0.10.20.172.in-addr.arpa.", true},
|
||||
{"dr._dns-sd._udp.0.10.20.172.in-addr.arpa.", true},
|
||||
{"lb._dns-sd._udp.0.10.20.172.in-addr.arpa.", true},
|
||||
{"qq._dns-sd._udp.0.10.20.172.in-addr.arpa.", false},
|
||||
{"0.10.20.172.in-addr.arpa.", false},
|
||||
{"i-have-no-dot", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := hasRDNSBonjourPrefix(test.in)
|
||||
if got != test.want {
|
||||
t.Errorf("trimRDNSBonjourPrefix(%q) = %v, want %v", test.in, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFull(b *testing.B) {
|
||||
dnsHandleFunc("test.site.", resolveToIP(testipv4, testipv6, "dns.test.site."))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user