11package dns01
22
33import (
4+ "net"
45 "sort"
6+ "sync"
57 "testing"
68
9+ "github.com/miekg/dns"
710 "github.com/stretchr/testify/assert"
811 "github.com/stretchr/testify/require"
912)
1013
14+ func testDNSHandler (writer dns.ResponseWriter , reply * dns.Msg ) {
15+ msg := dns.Msg {}
16+ msg .SetReply (reply )
17+
18+ if reply .Question [0 ].Qtype == dns .TypeA {
19+ msg .Authoritative = true
20+ domain := msg .Question [0 ].Name
21+ msg .Answer = append (
22+ msg .Answer ,
23+ & dns.A {
24+ Hdr : dns.RR_Header {
25+ Name : domain ,
26+ Rrtype : dns .TypeA ,
27+ Class : dns .ClassINET ,
28+ Ttl : 60 ,
29+ },
30+ A : net .IPv4 (127 , 0 , 0 , 1 ),
31+ },
32+ )
33+ }
34+
35+ _ = writer .WriteMsg (& msg )
36+ }
37+
38+ // getTestNameserver constructs a new DNS server on a local address, or set
39+ // of addresses, that responds to an `A` query for `example.com`.
40+ func getTestNameserver (t * testing.T , network string ) * dns.Server {
41+ t .Helper ()
42+ server := & dns.Server {
43+ Handler : dns .HandlerFunc (testDNSHandler ),
44+ Net : network ,
45+ }
46+ switch network {
47+ case "tcp" , "udp" :
48+ server .Addr = "0.0.0.0:0"
49+ case "tcp4" , "udp4" :
50+ server .Addr = "127.0.0.1:0"
51+ case "tcp6" , "udp6" :
52+ server .Addr = "[::1]:0"
53+ }
54+
55+ waitLock := sync.Mutex {}
56+ waitLock .Lock ()
57+ server .NotifyStartedFunc = waitLock .Unlock
58+
59+ go func () { _ = server .ListenAndServe () }()
60+
61+ waitLock .Lock ()
62+ return server
63+ }
64+
65+ func startTestNameserver (t * testing.T , stack networkStack , proto string ) (shutdown func (), addr string ) {
66+ t .Helper ()
67+ currentNetworkStack = stack
68+ srv := getTestNameserver (t , currentNetworkStack .Network (proto ))
69+
70+ shutdown = func () { _ = srv .Shutdown () }
71+ if proto == "tcp" {
72+ addr = srv .Listener .Addr ().String ()
73+ } else {
74+ addr = srv .PacketConn .LocalAddr ().String ()
75+ }
76+ return
77+ }
78+
79+ func TestSendDNSQuery (t * testing.T ) {
80+ currentNameservers := recursiveNameservers
81+
82+ t .Cleanup (func () {
83+ recursiveNameservers = currentNameservers
84+ currentNetworkStack = dualStack
85+ })
86+
87+ t .Run ("does udp4 only" , func (t * testing.T ) {
88+ stop , addr := startTestNameserver (t , ipv4only , "udp" )
89+ defer stop ()
90+
91+ recursiveNameservers = ParseNameservers ([]string {addr })
92+ msg := createDNSMsg ("example.com." , dns .TypeA , true )
93+ result , queryError := sendDNSQuery (msg , addr )
94+ require .NoError (t , queryError )
95+ assert .Equal (t , result .Answer [0 ].(* dns.A ).A .String (), "127.0.0.1" )
96+ })
97+
98+ t .Run ("does udp6 only" , func (t * testing.T ) {
99+ stop , addr := startTestNameserver (t , ipv6only , "udp" )
100+ defer stop ()
101+
102+ recursiveNameservers = ParseNameservers ([]string {addr })
103+ msg := createDNSMsg ("example.com." , dns .TypeA , true )
104+ result , queryError := sendDNSQuery (msg , addr )
105+ require .NoError (t , queryError )
106+ assert .Equal (t , result .Answer [0 ].(* dns.A ).A .String (), "127.0.0.1" )
107+ })
108+
109+ t .Run ("does tcp4 and tcp6" , func (t * testing.T ) {
110+ stop , addr := startTestNameserver (t , dualStack , "tcp" )
111+ host , port , _ := net .SplitHostPort (addr )
112+ defer stop ()
113+ t .Logf ("### port: %s" , port )
114+
115+ addr6 := net .JoinHostPort (host , port )
116+ recursiveNameservers = ParseNameservers ([]string {addr6 })
117+ msg := createDNSMsg ("example.com." , dns .TypeA , true )
118+ result , queryError := sendDNSQuery (msg , addr6 )
119+ require .NoError (t , queryError )
120+ assert .Equal (t , result .Answer [0 ].(* dns.A ).A .String (), "127.0.0.1" )
121+
122+ addr4 := net .JoinHostPort ("127.0.0.1" , port )
123+ recursiveNameservers = ParseNameservers ([]string {addr4 })
124+ msg = createDNSMsg ("example.com." , dns .TypeA , true )
125+ result , queryError = sendDNSQuery (msg , addr4 )
126+ require .NoError (t , queryError )
127+ assert .Equal (t , result .Answer [0 ].(* dns.A ).A .String (), "127.0.0.1" )
128+ })
129+ }
130+
11131func TestLookupNameserversOK (t * testing.T ) {
12132 testCases := []struct {
13133 fqdn string
@@ -74,7 +194,7 @@ var findXByFqdnTestCases = []struct {
74194 zone string
75195 primaryNs string
76196 nameservers []string
77- expectedError string
197+ expectedError string // regular expression
78198}{
79199 {
80200 desc : "domain is a CNAME" ,
@@ -109,7 +229,7 @@ var findXByFqdnTestCases = []struct {
109229 fqdn : "test.lego.zz." ,
110230 zone : "lego.zz." ,
111231 nameservers : []string {"8.8.8.8:53" },
112- expectedError : " could not find the start of authority for test.lego.zz.: NXDOMAIN" ,
232+ expectedError : `^ could not find the start of authority for test\ .lego\ .zz.: NXDOMAIN` ,
113233 },
114234 {
115235 desc : "several non existent nameservers" ,
@@ -119,18 +239,21 @@ var findXByFqdnTestCases = []struct {
119239 nameservers : []string {":7053" , ":8053" , "8.8.8.8:53" },
120240 },
121241 {
122- desc : "only non-existent nameservers" ,
123- fqdn : "mail.google.com." ,
124- zone : "google.com." ,
125- nameservers : []string {":7053" , ":8053" , ":9053" },
126- expectedError : "could not find the start of authority for mail.google.com.: read udp" ,
242+ desc : "only non-existent nameservers" ,
243+ fqdn : "mail.google.com." ,
244+ zone : "google.com." ,
245+ nameservers : []string {":7053" , ":8053" , ":9053" },
246+ // NOTE: On Windows, net.DialContext finds a way down to the ContectEx syscall.
247+ // There a fault is marked as "connectex", not "connect", see
248+ // https://cs.opensource.google/go/go/+/refs/tags/go1.19.5:src/net/fd_windows.go;l=112
249+ expectedError : `^could not find the start of authority for mail\.google\.com.: dial tcp :9053: connect(ex)?:` ,
127250 },
128251 {
129252 desc : "no nameservers" ,
130253 fqdn : "test.ldez.com." ,
131254 zone : "ldez.com." ,
132255 nameservers : []string {},
133- expectedError : " could not find the start of authority for test.ldez.com." ,
256+ expectedError : `^ could not find the start of authority for test\ .ldez\ .com\.` ,
134257 },
135258}
136259
@@ -142,7 +265,7 @@ func TestFindZoneByFqdnCustom(t *testing.T) {
142265 zone , err := FindZoneByFqdnCustom (test .fqdn , test .nameservers )
143266 if test .expectedError != "" {
144267 require .Error (t , err )
145- assert .Contains (t , err .Error (), test . expectedError )
268+ assert .Regexp (t , test . expectedError , err .Error ())
146269 } else {
147270 require .NoError (t , err )
148271 assert .Equal (t , test .zone , zone )
@@ -159,7 +282,7 @@ func TestFindPrimaryNsByFqdnCustom(t *testing.T) {
159282 ns , err := FindPrimaryNsByFqdnCustom (test .fqdn , test .nameservers )
160283 if test .expectedError != "" {
161284 require .Error (t , err )
162- assert .Contains (t , err .Error (), test . expectedError )
285+ assert .Regexp (t , test . expectedError , err .Error ())
163286 } else {
164287 require .NoError (t , err )
165288 assert .Equal (t , test .primaryNs , ns )
0 commit comments