@@ -2,14 +2,133 @@ package dns01
22
33import (
44 "errors"
5+ "net"
56 "sort"
7+ "sync"
68 "testing"
79
810 "github.com/miekg/dns"
911 "github.com/stretchr/testify/assert"
1012 "github.com/stretchr/testify/require"
1113)
1214
15+ func testDNSHandler (writer dns.ResponseWriter , reply * dns.Msg ) {
16+ msg := dns.Msg {}
17+ msg .SetReply (reply )
18+
19+ if reply .Question [0 ].Qtype == dns .TypeA {
20+ msg .Authoritative = true
21+ domain := msg .Question [0 ].Name
22+ msg .Answer = append (
23+ msg .Answer ,
24+ & dns.A {
25+ Hdr : dns.RR_Header {
26+ Name : domain ,
27+ Rrtype : dns .TypeA ,
28+ Class : dns .ClassINET ,
29+ Ttl : 60 ,
30+ },
31+ A : net .IPv4 (127 , 0 , 0 , 1 ),
32+ },
33+ )
34+ }
35+
36+ _ = writer .WriteMsg (& msg )
37+ }
38+
39+ // getTestNameserver constructs a new DNS server on a local address, or set
40+ // of addresses, that responds to an `A` query for `example.com`.
41+ func getTestNameserver (t * testing.T , network string ) * dns.Server {
42+ t .Helper ()
43+ server := & dns.Server {
44+ Handler : dns .HandlerFunc (testDNSHandler ),
45+ Net : network ,
46+ }
47+ switch network {
48+ case "tcp" , "udp" :
49+ server .Addr = "0.0.0.0:0"
50+ case "tcp4" , "udp4" :
51+ server .Addr = "127.0.0.1:0"
52+ case "tcp6" , "udp6" :
53+ server .Addr = "[::1]:0"
54+ }
55+
56+ waitLock := sync.Mutex {}
57+ waitLock .Lock ()
58+ server .NotifyStartedFunc = waitLock .Unlock
59+
60+ go func () { _ = server .ListenAndServe () }()
61+
62+ waitLock .Lock ()
63+ return server
64+ }
65+
66+ func startTestNameserver (t * testing.T , stack networkStack , proto string ) (shutdown func (), addr string ) {
67+ t .Helper ()
68+ currentNetworkStack = stack
69+ srv := getTestNameserver (t , currentNetworkStack .Network (proto ))
70+
71+ shutdown = func () { _ = srv .Shutdown () }
72+ if proto == "tcp" {
73+ addr = srv .Listener .Addr ().String ()
74+ } else {
75+ addr = srv .PacketConn .LocalAddr ().String ()
76+ }
77+ return
78+ }
79+
80+ func TestSendDNSQuery (t * testing.T ) {
81+ currentNameservers := recursiveNameservers
82+
83+ t .Cleanup (func () {
84+ recursiveNameservers = currentNameservers
85+ currentNetworkStack = dualStack
86+ })
87+
88+ t .Run ("does udp4 only" , func (t * testing.T ) {
89+ stop , addr := startTestNameserver (t , ipv4only , "udp" )
90+ defer stop ()
91+
92+ recursiveNameservers = ParseNameservers ([]string {addr })
93+ msg := createDNSMsg ("example.com." , dns .TypeA , true )
94+ result , queryError := sendDNSQuery (msg , addr )
95+ require .NoError (t , queryError )
96+ assert .Equal (t , result .Answer [0 ].(* dns.A ).A .String (), "127.0.0.1" )
97+ })
98+
99+ t .Run ("does udp6 only" , func (t * testing.T ) {
100+ stop , addr := startTestNameserver (t , ipv6only , "udp" )
101+ defer stop ()
102+
103+ recursiveNameservers = ParseNameservers ([]string {addr })
104+ msg := createDNSMsg ("example.com." , dns .TypeA , true )
105+ result , queryError := sendDNSQuery (msg , addr )
106+ require .NoError (t , queryError )
107+ assert .Equal (t , result .Answer [0 ].(* dns.A ).A .String (), "127.0.0.1" )
108+ })
109+
110+ t .Run ("does tcp4 and tcp6" , func (t * testing.T ) {
111+ stop , addr := startTestNameserver (t , dualStack , "tcp" )
112+ host , port , _ := net .SplitHostPort (addr )
113+ defer stop ()
114+ t .Logf ("### port: %s" , port )
115+
116+ addr6 := net .JoinHostPort (host , port )
117+ recursiveNameservers = ParseNameservers ([]string {addr6 })
118+ msg := createDNSMsg ("example.com." , dns .TypeA , true )
119+ result , queryError := sendDNSQuery (msg , addr6 )
120+ require .NoError (t , queryError )
121+ assert .Equal (t , result .Answer [0 ].(* dns.A ).A .String (), "127.0.0.1" )
122+
123+ addr4 := net .JoinHostPort ("127.0.0.1" , port )
124+ recursiveNameservers = ParseNameservers ([]string {addr4 })
125+ msg = createDNSMsg ("example.com." , dns .TypeA , true )
126+ result , queryError = sendDNSQuery (msg , addr4 )
127+ require .NoError (t , queryError )
128+ assert .Equal (t , result .Answer [0 ].(* dns.A ).A .String (), "127.0.0.1" )
129+ })
130+ }
131+
13132func TestLookupNameserversOK (t * testing.T ) {
14133 testCases := []struct {
15134 fqdn string
@@ -123,8 +242,10 @@ var findXByFqdnTestCases = []struct {
123242 fqdn : "mail.google.com." ,
124243 zone : "google.com." ,
125244 nameservers : []string {":7053" , ":8053" , ":9053" },
126- // use only the start of the message because the port changes with each call: 127.0.0.1:XXXXX->127.0.0.1:7053.
127- expectedError : "[fqdn=mail.google.com.] could not find the start of authority for 'mail.google.com.': DNS call error: read udp " ,
245+ // NOTE: On Windows, net.DialContext finds a way down to the ContectEx syscall.
246+ // There a fault is marked as "connectex", not "connect", see
247+ // https://cs.opensource.google/go/go/+/refs/tags/go1.19.5:src/net/fd_windows.go;l=112
248+ expectedError : "could not find the start of authority for 'mail.google.com.':" ,
128249 },
129250 {
130251 desc : "no nameservers" ,
0 commit comments