tsdns: delegate requests asynchronously (#687)
Signed-Off-By: Dmytro Shynkevych <dmytro@tailscale.com>
This commit is contained in:
committed by
GitHub
parent
a583e498b0
commit
1af70e2468
+170
-24
@@ -7,11 +7,13 @@ package tsdns
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
dns "golang.org/x/net/dns/dnsmessage"
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/tstest"
|
||||
)
|
||||
|
||||
var testipv4 = netaddr.IPv4(1, 2, 3, 4)
|
||||
@@ -178,9 +180,13 @@ func TestRDNSNameToIPv6(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
r := NewResolver(t.Logf, "ipn.dev")
|
||||
r := NewResolver(ResolverConfig{Logf: t.Logf, RootDomain: "ipn.dev.", Forward: false})
|
||||
r.SetMap(dnsMap)
|
||||
r.Start()
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -212,9 +218,13 @@ func TestResolve(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveReverse(t *testing.T) {
|
||||
r := NewResolver(t.Logf, "ipn.dev")
|
||||
r := NewResolver(ResolverConfig{Logf: t.Logf, RootDomain: "ipn.dev.", Forward: false})
|
||||
r.SetMap(dnsMap)
|
||||
r.Start()
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -244,6 +254,9 @@ func TestResolveReverse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDelegate(t *testing.T) {
|
||||
rc := tstest.NewResourceCheck()
|
||||
defer rc.Assert(t)
|
||||
|
||||
dnsHandleFunc("test.site.", resolveToIP(testipv4, testipv6))
|
||||
dnsHandleFunc("nxdomain.site.", resolveToNXDOMAIN)
|
||||
|
||||
@@ -271,12 +284,16 @@ func TestDelegate(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
r := NewResolver(t.Logf, "ipn.dev")
|
||||
r.SetNameservers([]string{
|
||||
v4server.PacketConn.LocalAddr().String(),
|
||||
v6server.PacketConn.LocalAddr().String(),
|
||||
r := NewResolver(ResolverConfig{Logf: t.Logf, RootDomain: "ipn.dev.", Forward: true})
|
||||
r.SetUpstreams([]net.Addr{
|
||||
v4server.PacketConn.LocalAddr(),
|
||||
v6server.PacketConn.LocalAddr(),
|
||||
})
|
||||
r.Start()
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -311,9 +328,92 @@ func TestDelegate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelegateCollision(t *testing.T) {
|
||||
dnsHandleFunc("test.site.", resolveToIP(testipv4, testipv6))
|
||||
|
||||
server, errch := serveDNS("127.0.0.1:0")
|
||||
defer func() {
|
||||
if err := <-errch; err != nil {
|
||||
t.Errorf("server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if server == nil {
|
||||
return
|
||||
}
|
||||
defer server.Shutdown()
|
||||
|
||||
r := NewResolver(ResolverConfig{Logf: t.Logf, RootDomain: "ipn.dev.", Forward: true})
|
||||
r.SetUpstreams([]net.Addr{server.PacketConn.LocalAddr()})
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
packets := []struct {
|
||||
qname string
|
||||
qtype dns.Type
|
||||
addr netaddr.IPPort
|
||||
}{
|
||||
{"test.site.", dns.TypeA, netaddr.IPPort{IP: netaddr.IPv4(1, 1, 1, 1), Port: 1001}},
|
||||
{"test.site.", dns.TypeAAAA, netaddr.IPPort{IP: netaddr.IPv4(1, 1, 1, 1), Port: 1002}},
|
||||
}
|
||||
|
||||
// packets will have the same dns txid.
|
||||
for _, p := range packets {
|
||||
payload := dnspacket(p.qname, p.qtype)
|
||||
req := Packet{Payload: payload, Addr: p.addr}
|
||||
err := r.EnqueueRequest(req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Despite the txid collision, the answer(s) should still match the query.
|
||||
resp, err := r.NextResponse()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var p dns.Parser
|
||||
_, err = p.Start(resp.Payload)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = p.SkipAllQuestions()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
ans, err := p.AllAnswers()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var wantType dns.Type
|
||||
switch ans[0].Body.(type) {
|
||||
case *dns.AResource:
|
||||
wantType = dns.TypeA
|
||||
case *dns.AAAAResource:
|
||||
wantType = dns.TypeAAAA
|
||||
default:
|
||||
t.Errorf("unexpected answer type: %T", ans[0].Body)
|
||||
}
|
||||
|
||||
for _, p := range packets {
|
||||
if p.qtype == wantType && p.addr != resp.Addr {
|
||||
t.Errorf("addr = %v; want %v", resp.Addr, p.addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentSetMap(t *testing.T) {
|
||||
r := NewResolver(t.Logf, "ipn.dev.")
|
||||
r.Start()
|
||||
r := NewResolver(ResolverConfig{Logf: t.Logf, RootDomain: "ipn.dev.", Forward: false})
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// This is purely to ensure that Resolve does not race with SetMap.
|
||||
var wg sync.WaitGroup
|
||||
@@ -329,17 +429,36 @@ func TestConcurrentSetMap(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestConcurrentSetNameservers(t *testing.T) {
|
||||
r := NewResolver(t.Logf, "ipn.dev.")
|
||||
r.Start()
|
||||
packet := dnspacket("google.com.", dns.TypeA)
|
||||
func TestConcurrentSetUpstreams(t *testing.T) {
|
||||
dnsHandleFunc("test.site.", resolveToIP(testipv4, testipv6))
|
||||
|
||||
// This is purely to ensure that delegation does not race with SetNameservers.
|
||||
server, errch := serveDNS("127.0.0.1:0")
|
||||
defer func() {
|
||||
if err := <-errch; err != nil {
|
||||
t.Errorf("server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if server == nil {
|
||||
return
|
||||
}
|
||||
defer server.Shutdown()
|
||||
|
||||
r := NewResolver(ResolverConfig{Logf: t.Logf, RootDomain: "ipn.dev.", Forward: true})
|
||||
r.SetMap(dnsMap)
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
packet := dnspacket("test.site.", dns.TypeA)
|
||||
// This is purely to ensure that delegation does not race with SetUpstreams.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r.SetNameservers([]string{"9.9.9.9:53"})
|
||||
r.SetUpstreams([]net.Addr{server.PacketConn.LocalAddr()})
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
@@ -415,9 +534,13 @@ var nxdomainResponse = []byte{
|
||||
}
|
||||
|
||||
func TestFull(t *testing.T) {
|
||||
r := NewResolver(t.Logf, "ipn.dev.")
|
||||
r := NewResolver(ResolverConfig{Logf: t.Logf, RootDomain: "ipn.dev.", Forward: false})
|
||||
r.SetMap(dnsMap)
|
||||
r.Start()
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// One full packet and one error packet
|
||||
tests := []struct {
|
||||
@@ -445,9 +568,13 @@ func TestFull(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAllocs(t *testing.T) {
|
||||
r := NewResolver(t.Logf, "ipn.dev.")
|
||||
r := NewResolver(ResolverConfig{Logf: t.Logf, RootDomain: "ipn.dev.", Forward: false})
|
||||
r.SetMap(dnsMap)
|
||||
r.Start()
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// It is seemingly pointless to test allocs in the delegate path,
|
||||
// as dialer.Dial -> Read -> Write alone comprise 12 allocs.
|
||||
@@ -473,9 +600,28 @@ func TestAllocs(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkFull(b *testing.B) {
|
||||
r := NewResolver(b.Logf, "ipn.dev.")
|
||||
dnsHandleFunc("test.site.", resolveToIP(testipv4, testipv6))
|
||||
|
||||
server, errch := serveDNS("127.0.0.1:0")
|
||||
defer func() {
|
||||
if err := <-errch; err != nil {
|
||||
b.Errorf("server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if server == nil {
|
||||
return
|
||||
}
|
||||
defer server.Shutdown()
|
||||
|
||||
r := NewResolver(ResolverConfig{Logf: b.Logf, RootDomain: "ipn.dev.", Forward: true})
|
||||
r.SetMap(dnsMap)
|
||||
r.Start()
|
||||
r.SetUpstreams([]net.Addr{server.PacketConn.LocalAddr()})
|
||||
|
||||
if err := r.Start(); err != nil {
|
||||
b.Fatalf("start: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -483,7 +629,7 @@ func BenchmarkFull(b *testing.B) {
|
||||
}{
|
||||
{"forward", dnspacket("test1.ipn.dev.", dns.TypeA)},
|
||||
{"reverse", dnspacket("4.3.2.1.in-addr.arpa.", dns.TypePTR)},
|
||||
{"nxdomain", dnspacket("test3.ipn.dev.", dns.TypeA)},
|
||||
{"delegated", dnspacket("test.site.", dns.TypeA)},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user