Source file
src/net/lookup_windows_test.go
1
2
3
4
5 package net
6
7 import (
8 "cmp"
9 "context"
10 "encoding/json"
11 "errors"
12 "fmt"
13 "internal/testenv"
14 "os/exec"
15 "reflect"
16 "regexp"
17 "slices"
18 "strings"
19 "syscall"
20 "testing"
21 )
22
23 var nslookupTestServers = []string{"mail.golang.com", "gmail.com"}
24 var lookupTestIPs = []string{"8.8.8.8", "1.1.1.1"}
25
26 func toJson(v any) string {
27 data, _ := json.Marshal(v)
28 return string(data)
29 }
30
31 func testLookup(t *testing.T, fn func(*testing.T, *Resolver, string)) {
32 for _, def := range []bool{true, false} {
33 for _, server := range nslookupTestServers {
34 var name string
35 if def {
36 name = "default/"
37 } else {
38 name = "go/"
39 }
40 t.Run(name+server, func(t *testing.T) {
41 t.Parallel()
42 r := DefaultResolver
43 if !def {
44 r = &Resolver{PreferGo: true}
45 }
46 fn(t, r, server)
47 })
48 }
49 }
50 }
51
52 func TestNSLookupMX(t *testing.T) {
53 testenv.MustHaveExternalNetwork(t)
54
55 testLookup(t, func(t *testing.T, r *Resolver, server string) {
56 mx, err := r.LookupMX(context.Background(), server)
57 if err != nil {
58 t.Fatal(err)
59 }
60 if len(mx) == 0 {
61 t.Fatal("no results")
62 }
63 expected, err := nslookupMX(server)
64 if err != nil {
65 t.Skipf("skipping failed nslookup %s test: %s", server, err)
66 }
67 byPrefAndHost := func(a, b *MX) int {
68 if r := cmp.Compare(a.Pref, b.Pref); r != 0 {
69 return r
70 }
71 return strings.Compare(a.Host, b.Host)
72 }
73 slices.SortFunc(expected, byPrefAndHost)
74 slices.SortFunc(mx, byPrefAndHost)
75 if !reflect.DeepEqual(expected, mx) {
76 t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(mx))
77 }
78 })
79 }
80
81 func TestNSLookupCNAME(t *testing.T) {
82 testenv.MustHaveExternalNetwork(t)
83
84 testLookup(t, func(t *testing.T, r *Resolver, server string) {
85 cname, err := r.LookupCNAME(context.Background(), server)
86 if err != nil {
87 t.Fatalf("failed %s: %s", server, err)
88 }
89 if cname == "" {
90 t.Fatalf("no result %s", server)
91 }
92 expected, err := nslookupCNAME(server)
93 if err != nil {
94 t.Skipf("skipping failed nslookup %s test: %s", server, err)
95 }
96 if expected != cname {
97 t.Errorf("different results %s:\texp:%v\tgot:%v", server, expected, cname)
98 }
99 })
100 }
101
102 func TestNSLookupNS(t *testing.T) {
103 testenv.MustHaveExternalNetwork(t)
104
105 testLookup(t, func(t *testing.T, r *Resolver, server string) {
106 ns, err := r.LookupNS(context.Background(), server)
107 if err != nil {
108 t.Fatalf("failed %s: %s", server, err)
109 }
110 if len(ns) == 0 {
111 t.Fatal("no results")
112 }
113 expected, err := nslookupNS(server)
114 if err != nil {
115 t.Skipf("skipping failed nslookup %s test: %s", server, err)
116 }
117 byHost := func(a, b *NS) int {
118 return strings.Compare(a.Host, b.Host)
119 }
120 slices.SortFunc(expected, byHost)
121 slices.SortFunc(ns, byHost)
122 if !reflect.DeepEqual(expected, ns) {
123 t.Errorf("different results %s:\texp:%v\tgot:%v", toJson(server), toJson(expected), ns)
124 }
125 })
126 }
127
128 func TestNSLookupTXT(t *testing.T) {
129 testenv.MustHaveExternalNetwork(t)
130
131 testLookup(t, func(t *testing.T, r *Resolver, server string) {
132 txt, err := r.LookupTXT(context.Background(), server)
133 if err != nil {
134 t.Fatalf("failed %s: %s", server, err)
135 }
136 if len(txt) == 0 {
137 t.Fatalf("no results")
138 }
139 expected, err := nslookupTXT(server)
140 if err != nil {
141 t.Skipf("skipping failed nslookup %s test: %s", server, err)
142 }
143 slices.Sort(expected)
144 slices.Sort(txt)
145 if !slices.Equal(expected, txt) {
146 t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(txt))
147 }
148 })
149 }
150
151 func TestLookupLocalPTR(t *testing.T) {
152 testenv.MustHaveExternalNetwork(t)
153
154 addr, err := localIP()
155 if err != nil {
156 t.Errorf("failed to get local ip: %s", err)
157 }
158 names, err := LookupAddr(addr.String())
159 if err != nil {
160 t.Errorf("failed %s: %s", addr, err)
161 }
162 if len(names) == 0 {
163 t.Errorf("no results")
164 }
165 expected, err := lookupPTR(addr.String())
166 if err != nil {
167 t.Skipf("skipping failed lookup %s test: %s", addr.String(), err)
168 }
169 slices.Sort(expected)
170 slices.Sort(names)
171 if !slices.Equal(expected, names) {
172 t.Errorf("different results %s:\texp:%v\tgot:%v", addr, toJson(expected), toJson(names))
173 }
174 }
175
176 func TestLookupPTR(t *testing.T) {
177 testenv.MustHaveExternalNetwork(t)
178
179 for _, addr := range lookupTestIPs {
180 names, err := LookupAddr(addr)
181 if err != nil {
182
183
184
185
186 var DNS_ERROR_RCODE_SERVER_FAILURE syscall.Errno = 9002
187 if strings.HasSuffix(err.Error(), DNS_ERROR_RCODE_SERVER_FAILURE.Error()) {
188 testenv.SkipFlaky(t, 38111)
189 }
190 t.Errorf("failed %s: %s", addr, err)
191 }
192 if len(names) == 0 {
193 t.Errorf("no results")
194 }
195 expected, err := lookupPTR(addr)
196 if err != nil {
197 t.Logf("skipping failed lookup %s test: %s", addr, err)
198 continue
199 }
200 slices.Sort(expected)
201 slices.Sort(names)
202 if !slices.Equal(expected, names) {
203 t.Errorf("different results %s:\texp:%v\tgot:%v", addr, toJson(expected), toJson(names))
204 }
205 }
206 }
207
208 func nslookup(qtype, name string) (string, error) {
209 var out strings.Builder
210 var err strings.Builder
211 cmd := exec.Command("nslookup", "-querytype="+qtype, name)
212 cmd.Stdout = &out
213 cmd.Stderr = &err
214 if err := cmd.Run(); err != nil {
215 return "", err
216 }
217 r := strings.ReplaceAll(out.String(), "\r\n", "\n")
218
219
220 if strings.Contains(err.String(), "can't find") {
221 return r, errors.New(err.String())
222 }
223 return r, nil
224 }
225
226 func nslookupMX(name string) (mx []*MX, err error) {
227 var r string
228 if r, err = nslookup("mx", name); err != nil {
229 return
230 }
231 mx = make([]*MX, 0, 10)
232
233
234 rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+mail exchanger\s*=\s*([0-9]+)\s*([a-z0-9.\-]+)$`)
235 for _, ans := range rx.FindAllStringSubmatch(r, -1) {
236 pref, _, _ := dtoi(ans[2])
237 mx = append(mx, &MX{absDomainName(ans[3]), uint16(pref)})
238 }
239
240
241 rx = regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+MX preference\s*=\s*([0-9]+)\s*,\s*mail exchanger\s*=\s*([a-z0-9.\-]+)$`)
242 for _, ans := range rx.FindAllStringSubmatch(r, -1) {
243 pref, _, _ := dtoi(ans[2])
244 mx = append(mx, &MX{absDomainName(ans[3]), uint16(pref)})
245 }
246 return
247 }
248
249 func nslookupNS(name string) (ns []*NS, err error) {
250 var r string
251 if r, err = nslookup("ns", name); err != nil {
252 return
253 }
254 ns = make([]*NS, 0, 10)
255
256 rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+nameserver\s*=\s*([a-z0-9.\-]+)$`)
257 for _, ans := range rx.FindAllStringSubmatch(r, -1) {
258 ns = append(ns, &NS{absDomainName(ans[2])})
259 }
260 return
261 }
262
263 func nslookupCNAME(name string) (cname string, err error) {
264 var r string
265 if r, err = nslookup("cname", name); err != nil {
266 return
267 }
268
269 rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+canonical name\s*=\s*([a-z0-9.\-]+)$`)
270
271 last := name
272 for _, ans := range rx.FindAllStringSubmatch(r, -1) {
273 last = ans[2]
274 }
275 return absDomainName(last), nil
276 }
277
278 func nslookupTXT(name string) (txt []string, err error) {
279 var r string
280 if r, err = nslookup("txt", name); err != nil {
281 return
282 }
283 txt = make([]string, 0, 10)
284
285
286
287
288
289
290
291 rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+text\s*=\s*"(.*)"$`)
292 for _, ans := range rx.FindAllStringSubmatch(r, -1) {
293 txt = append(txt, ans[2])
294 }
295 return
296 }
297
298 func ping(name string) (string, error) {
299 cmd := exec.Command("ping", "-n", "1", "-a", name)
300 stdoutStderr, err := cmd.CombinedOutput()
301 if err != nil {
302 return "", fmt.Errorf("%v: %v", err, string(stdoutStderr))
303 }
304 r := strings.ReplaceAll(string(stdoutStderr), "\r\n", "\n")
305 return r, nil
306 }
307
308 func lookupPTR(name string) (ptr []string, err error) {
309 var r string
310 if r, err = ping(name); err != nil {
311 return
312 }
313 ptr = make([]string, 0, 10)
314 rx := regexp.MustCompile(`(?m)^Pinging\s+([a-zA-Z0-9.\-]+)\s+\[.*$`)
315 for _, ans := range rx.FindAllStringSubmatch(r, -1) {
316 ptr = append(ptr, absDomainName(ans[1]))
317 }
318 return
319 }
320
321 func localIP() (ip IP, err error) {
322 conn, err := Dial("udp", "golang.org:80")
323 if err != nil {
324 return nil, err
325 }
326 defer conn.Close()
327
328 localAddr := conn.LocalAddr().(*UDPAddr)
329
330 return localAddr.IP, nil
331 }
332
View as plain text