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