1
1
package dns01
2
2
3
3
import (
4
+ "net"
4
5
"sort"
6
+ "sync"
5
7
"testing"
6
8
9
+ "github.com/miekg/dns"
7
10
"github.com/stretchr/testify/assert"
8
11
"github.com/stretchr/testify/require"
9
12
)
10
13
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
+
11
131
func TestLookupNameserversOK (t * testing.T ) {
12
132
testCases := []struct {
13
133
fqdn string
@@ -74,7 +194,7 @@ var findXByFqdnTestCases = []struct {
74
194
zone string
75
195
primaryNs string
76
196
nameservers []string
77
- expectedError string
197
+ expectedError string // regular expression
78
198
}{
79
199
{
80
200
desc : "domain is a CNAME" ,
@@ -109,7 +229,7 @@ var findXByFqdnTestCases = []struct {
109
229
fqdn : "test.lego.zz." ,
110
230
zone : "lego.zz." ,
111
231
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` ,
113
233
},
114
234
{
115
235
desc : "several non existent nameservers" ,
@@ -119,18 +239,21 @@ var findXByFqdnTestCases = []struct {
119
239
nameservers : []string {":7053" , ":8053" , "8.8.8.8:53" },
120
240
},
121
241
{
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)?:` ,
127
250
},
128
251
{
129
252
desc : "no nameservers" ,
130
253
fqdn : "test.ldez.com." ,
131
254
zone : "ldez.com." ,
132
255
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\.` ,
134
257
},
135
258
}
136
259
@@ -142,7 +265,7 @@ func TestFindZoneByFqdnCustom(t *testing.T) {
142
265
zone , err := FindZoneByFqdnCustom (test .fqdn , test .nameservers )
143
266
if test .expectedError != "" {
144
267
require .Error (t , err )
145
- assert .Contains (t , err .Error (), test . expectedError )
268
+ assert .Regexp (t , test . expectedError , err .Error ())
146
269
} else {
147
270
require .NoError (t , err )
148
271
assert .Equal (t , test .zone , zone )
@@ -159,7 +282,7 @@ func TestFindPrimaryNsByFqdnCustom(t *testing.T) {
159
282
ns , err := FindPrimaryNsByFqdnCustom (test .fqdn , test .nameservers )
160
283
if test .expectedError != "" {
161
284
require .Error (t , err )
162
- assert .Contains (t , err .Error (), test . expectedError )
285
+ assert .Regexp (t , test . expectedError , err .Error ())
163
286
} else {
164
287
require .NoError (t , err )
165
288
assert .Equal (t , test .primaryNs , ns )
0 commit comments