2021-08-03 15:31:20 +00:00
|
|
|
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package resolver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-08-03 13:56:31 +00:00
|
|
|
"net"
|
2021-08-03 15:31:20 +00:00
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2021-09-19 00:34:33 +00:00
|
|
|
dns "golang.org/x/net/dns/dnsmessage"
|
2021-10-15 02:39:11 +00:00
|
|
|
"tailscale.com/hostinfo"
|
2021-08-03 13:56:31 +00:00
|
|
|
"tailscale.com/types/dnstype"
|
2021-08-03 15:31:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func (rr resolverAndDelay) String() string {
|
2021-08-03 13:56:31 +00:00
|
|
|
return fmt.Sprintf("%v+%v", rr.name, rr.startDelay)
|
2021-08-03 15:31:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestResolversWithDelays(t *testing.T) {
|
|
|
|
// query
|
2021-08-03 13:56:31 +00:00
|
|
|
q := func(ss ...string) (ipps []dnstype.Resolver) {
|
2021-08-03 15:31:20 +00:00
|
|
|
for _, s := range ss {
|
2021-08-03 13:56:31 +00:00
|
|
|
host, _, err := net.SplitHostPort(s)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
ipps = append(ipps, dnstype.Resolver{Addr: host})
|
2021-08-03 15:31:20 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// output
|
|
|
|
o := func(ss ...string) (rr []resolverAndDelay) {
|
|
|
|
for _, s := range ss {
|
|
|
|
var d time.Duration
|
2022-03-19 19:42:46 +00:00
|
|
|
s, durStr, hasPlus := strings.Cut(s, "+")
|
|
|
|
if hasPlus {
|
2021-08-03 15:31:20 +00:00
|
|
|
var err error
|
2022-03-19 19:42:46 +00:00
|
|
|
d, err = time.ParseDuration(durStr)
|
2021-08-03 15:31:20 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("parsing duration in %q: %v", s, err))
|
|
|
|
}
|
|
|
|
}
|
2021-08-03 13:56:31 +00:00
|
|
|
host, _, err := net.SplitHostPort(s)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2021-08-03 15:31:20 +00:00
|
|
|
rr = append(rr, resolverAndDelay{
|
2021-08-03 13:56:31 +00:00
|
|
|
name: dnstype.Resolver{Addr: host},
|
2021-08-03 15:31:20 +00:00
|
|
|
startDelay: d,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
2021-08-03 13:56:31 +00:00
|
|
|
in []dnstype.Resolver
|
2021-08-03 15:31:20 +00:00
|
|
|
want []resolverAndDelay
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "unknown-no-delays",
|
|
|
|
in: q("1.2.3.4:53", "2.3.4.5:53"),
|
|
|
|
want: o("1.2.3.4:53", "2.3.4.5:53"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "google-all-ipv4",
|
|
|
|
in: q("8.8.8.8:53", "8.8.4.4:53"),
|
|
|
|
want: o("8.8.8.8:53", "8.8.4.4:53+200ms"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "google-only-ipv6",
|
|
|
|
in: q("[2001:4860:4860::8888]:53", "[2001:4860:4860::8844]:53"),
|
|
|
|
want: o("[2001:4860:4860::8888]:53", "[2001:4860:4860::8844]:53+200ms"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "google-all-four",
|
|
|
|
in: q("8.8.8.8:53", "8.8.4.4:53", "[2001:4860:4860::8888]:53", "[2001:4860:4860::8844]:53"),
|
|
|
|
want: o("8.8.8.8:53", "8.8.4.4:53+200ms", "[2001:4860:4860::8888]:53+2.5s", "[2001:4860:4860::8844]:53+2.7s"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "quad9-one-v4-one-v6",
|
|
|
|
in: q("9.9.9.9:53", "[2620:fe::fe]:53"),
|
|
|
|
want: o("9.9.9.9:53", "[2620:fe::fe]:53+200ms"),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
got := resolversWithDelays(tt.in)
|
|
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
|
|
t.Errorf("got %v; want %v", got, tt.want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2021-09-19 00:34:33 +00:00
|
|
|
|
|
|
|
func TestGetRCode(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
packet []byte
|
|
|
|
want dns.RCode
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "empty",
|
|
|
|
packet: []byte{},
|
|
|
|
want: dns.RCode(5),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "too-short",
|
|
|
|
packet: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
|
|
want: dns.RCode(5),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "noerror",
|
|
|
|
packet: []byte{0xC4, 0xFE, 0x81, 0xA0, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01},
|
|
|
|
want: dns.RCode(0),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "refused",
|
|
|
|
packet: []byte{0xee, 0xa1, 0x81, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
|
|
|
|
want: dns.RCode(5),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "nxdomain",
|
|
|
|
packet: []byte{0x34, 0xf4, 0x81, 0x83, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01},
|
|
|
|
want: dns.RCode(3),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
got := getRCode(tt.packet)
|
|
|
|
if got != tt.want {
|
|
|
|
t.Errorf("got %d; want %d", got, tt.want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-10-15 02:39:11 +00:00
|
|
|
|
|
|
|
func TestMaxDoHInFlight(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
goos string
|
|
|
|
ver string
|
|
|
|
want int
|
|
|
|
}{
|
|
|
|
{"ios", "", 10},
|
|
|
|
{"ios", "1532", 10},
|
|
|
|
{"ios", "9.3.2", 10},
|
|
|
|
{"ios", "14.3.2", 10},
|
|
|
|
{"ios", "15.3.2", 1000},
|
|
|
|
{"ios", "20.3.2", 1000},
|
|
|
|
{"android", "", 1000},
|
|
|
|
{"darwin", "", 1000},
|
|
|
|
{"linux", "", 1000},
|
|
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
|
|
t.Run(fmt.Sprintf("%s-%s", tc.goos, tc.ver), func(t *testing.T) {
|
|
|
|
hostinfo.SetOSVersion(tc.ver)
|
|
|
|
got := maxDoHInFlight(tc.goos)
|
|
|
|
if got != tc.want {
|
|
|
|
t.Errorf("got %d; want %d", got, tc.want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-12-19 03:03:38 +00:00
|
|
|
|
|
|
|
func BenchmarkNameFromQuery(b *testing.B) {
|
|
|
|
builder := dns.NewBuilder(nil, dns.Header{})
|
|
|
|
builder.StartQuestions()
|
|
|
|
builder.Question(dns.Question{
|
|
|
|
Name: dns.MustNewName("foo.example."),
|
|
|
|
Type: dns.TypeA,
|
|
|
|
Class: dns.ClassINET,
|
|
|
|
})
|
|
|
|
msg, err := builder.Finish()
|
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
_, err := nameFromQuery(msg)
|
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|