diff options
Diffstat (limited to 'src/net/lookup_windows_test.go')
-rw-r--r-- | src/net/lookup_windows_test.go | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/src/net/lookup_windows_test.go b/src/net/lookup_windows_test.go new file mode 100644 index 0000000..c618a05 --- /dev/null +++ b/src/net/lookup_windows_test.go @@ -0,0 +1,340 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "internal/testenv" + "os/exec" + "reflect" + "regexp" + "sort" + "strings" + "syscall" + "testing" +) + +var nslookupTestServers = []string{"mail.golang.com", "gmail.com"} +var lookupTestIPs = []string{"8.8.8.8", "1.1.1.1"} + +func toJson(v any) string { + data, _ := json.Marshal(v) + return string(data) +} + +func testLookup(t *testing.T, fn func(*testing.T, *Resolver, string)) { + for _, def := range []bool{true, false} { + def := def + for _, server := range nslookupTestServers { + server := server + var name string + if def { + name = "default/" + } else { + name = "go/" + } + t.Run(name+server, func(t *testing.T) { + t.Parallel() + r := DefaultResolver + if !def { + r = &Resolver{PreferGo: true} + } + fn(t, r, server) + }) + } + } +} + +func TestNSLookupMX(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + testLookup(t, func(t *testing.T, r *Resolver, server string) { + mx, err := r.LookupMX(context.Background(), server) + if err != nil { + t.Fatal(err) + } + if len(mx) == 0 { + t.Fatal("no results") + } + expected, err := nslookupMX(server) + if err != nil { + t.Skipf("skipping failed nslookup %s test: %s", server, err) + } + sort.Sort(byPrefAndHost(expected)) + sort.Sort(byPrefAndHost(mx)) + if !reflect.DeepEqual(expected, mx) { + t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(mx)) + } + }) +} + +func TestNSLookupCNAME(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + testLookup(t, func(t *testing.T, r *Resolver, server string) { + cname, err := r.LookupCNAME(context.Background(), server) + if err != nil { + t.Fatalf("failed %s: %s", server, err) + } + if cname == "" { + t.Fatalf("no result %s", server) + } + expected, err := nslookupCNAME(server) + if err != nil { + t.Skipf("skipping failed nslookup %s test: %s", server, err) + } + if expected != cname { + t.Errorf("different results %s:\texp:%v\tgot:%v", server, expected, cname) + } + }) +} + +func TestNSLookupNS(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + testLookup(t, func(t *testing.T, r *Resolver, server string) { + ns, err := r.LookupNS(context.Background(), server) + if err != nil { + t.Fatalf("failed %s: %s", server, err) + } + if len(ns) == 0 { + t.Fatal("no results") + } + expected, err := nslookupNS(server) + if err != nil { + t.Skipf("skipping failed nslookup %s test: %s", server, err) + } + sort.Sort(byHost(expected)) + sort.Sort(byHost(ns)) + if !reflect.DeepEqual(expected, ns) { + t.Errorf("different results %s:\texp:%v\tgot:%v", toJson(server), toJson(expected), ns) + } + }) +} + +func TestNSLookupTXT(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + testLookup(t, func(t *testing.T, r *Resolver, server string) { + txt, err := r.LookupTXT(context.Background(), server) + if err != nil { + t.Fatalf("failed %s: %s", server, err) + } + if len(txt) == 0 { + t.Fatalf("no results") + } + expected, err := nslookupTXT(server) + if err != nil { + t.Skipf("skipping failed nslookup %s test: %s", server, err) + } + sort.Strings(expected) + sort.Strings(txt) + if !reflect.DeepEqual(expected, txt) { + t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(txt)) + } + }) +} + +func TestLookupLocalPTR(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + addr, err := localIP() + if err != nil { + t.Errorf("failed to get local ip: %s", err) + } + names, err := LookupAddr(addr.String()) + if err != nil { + t.Errorf("failed %s: %s", addr, err) + } + if len(names) == 0 { + t.Errorf("no results") + } + expected, err := lookupPTR(addr.String()) + if err != nil { + t.Skipf("skipping failed lookup %s test: %s", addr.String(), err) + } + sort.Strings(expected) + sort.Strings(names) + if !reflect.DeepEqual(expected, names) { + t.Errorf("different results %s:\texp:%v\tgot:%v", addr, toJson(expected), toJson(names)) + } +} + +func TestLookupPTR(t *testing.T) { + testenv.MustHaveExternalNetwork(t) + + for _, addr := range lookupTestIPs { + names, err := LookupAddr(addr) + if err != nil { + // The DNSError type stores the error as a string, so it cannot wrap the + // original error code and we cannot check for it here. However, we can at + // least use its error string to identify the correct localized text for + // the error to skip. + var DNS_ERROR_RCODE_SERVER_FAILURE syscall.Errno = 9002 + if strings.HasSuffix(err.Error(), DNS_ERROR_RCODE_SERVER_FAILURE.Error()) { + testenv.SkipFlaky(t, 38111) + } + t.Errorf("failed %s: %s", addr, err) + } + if len(names) == 0 { + t.Errorf("no results") + } + expected, err := lookupPTR(addr) + if err != nil { + t.Logf("skipping failed lookup %s test: %s", addr, err) + continue + } + sort.Strings(expected) + sort.Strings(names) + if !reflect.DeepEqual(expected, names) { + t.Errorf("different results %s:\texp:%v\tgot:%v", addr, toJson(expected), toJson(names)) + } + } +} + +type byPrefAndHost []*MX + +func (s byPrefAndHost) Len() int { return len(s) } +func (s byPrefAndHost) Less(i, j int) bool { + if s[i].Pref != s[j].Pref { + return s[i].Pref < s[j].Pref + } + return s[i].Host < s[j].Host +} +func (s byPrefAndHost) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type byHost []*NS + +func (s byHost) Len() int { return len(s) } +func (s byHost) Less(i, j int) bool { return s[i].Host < s[j].Host } +func (s byHost) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func nslookup(qtype, name string) (string, error) { + var out strings.Builder + var err strings.Builder + cmd := exec.Command("nslookup", "-querytype="+qtype, name) + cmd.Stdout = &out + cmd.Stderr = &err + if err := cmd.Run(); err != nil { + return "", err + } + r := strings.ReplaceAll(out.String(), "\r\n", "\n") + // nslookup stderr output contains also debug information such as + // "Non-authoritative answer" and it doesn't return the correct errcode + if strings.Contains(err.String(), "can't find") { + return r, errors.New(err.String()) + } + return r, nil +} + +func nslookupMX(name string) (mx []*MX, err error) { + var r string + if r, err = nslookup("mx", name); err != nil { + return + } + mx = make([]*MX, 0, 10) + // linux nslookup syntax + // golang.org mail exchanger = 2 alt1.aspmx.l.google.com. + rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+mail exchanger\s*=\s*([0-9]+)\s*([a-z0-9.\-]+)$`) + for _, ans := range rx.FindAllStringSubmatch(r, -1) { + pref, _, _ := dtoi(ans[2]) + mx = append(mx, &MX{absDomainName(ans[3]), uint16(pref)}) + } + // windows nslookup syntax + // gmail.com MX preference = 30, mail exchanger = alt3.gmail-smtp-in.l.google.com + rx = regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+MX preference\s*=\s*([0-9]+)\s*,\s*mail exchanger\s*=\s*([a-z0-9.\-]+)$`) + for _, ans := range rx.FindAllStringSubmatch(r, -1) { + pref, _, _ := dtoi(ans[2]) + mx = append(mx, &MX{absDomainName(ans[3]), uint16(pref)}) + } + return +} + +func nslookupNS(name string) (ns []*NS, err error) { + var r string + if r, err = nslookup("ns", name); err != nil { + return + } + ns = make([]*NS, 0, 10) + // golang.org nameserver = ns1.google.com. + rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+nameserver\s*=\s*([a-z0-9.\-]+)$`) + for _, ans := range rx.FindAllStringSubmatch(r, -1) { + ns = append(ns, &NS{absDomainName(ans[2])}) + } + return +} + +func nslookupCNAME(name string) (cname string, err error) { + var r string + if r, err = nslookup("cname", name); err != nil { + return + } + // mail.golang.com canonical name = golang.org. + rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+canonical name\s*=\s*([a-z0-9.\-]+)$`) + // assumes the last CNAME is the correct one + last := name + for _, ans := range rx.FindAllStringSubmatch(r, -1) { + last = ans[2] + } + return absDomainName(last), nil +} + +func nslookupTXT(name string) (txt []string, err error) { + var r string + if r, err = nslookup("txt", name); err != nil { + return + } + txt = make([]string, 0, 10) + // linux + // golang.org text = "v=spf1 redirect=_spf.google.com" + + // windows + // golang.org text = + // + // "v=spf1 redirect=_spf.google.com" + rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+text\s*=\s*"(.*)"$`) + for _, ans := range rx.FindAllStringSubmatch(r, -1) { + txt = append(txt, ans[2]) + } + return +} + +func ping(name string) (string, error) { + cmd := exec.Command("ping", "-n", "1", "-a", name) + stdoutStderr, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("%v: %v", err, string(stdoutStderr)) + } + r := strings.ReplaceAll(string(stdoutStderr), "\r\n", "\n") + return r, nil +} + +func lookupPTR(name string) (ptr []string, err error) { + var r string + if r, err = ping(name); err != nil { + return + } + ptr = make([]string, 0, 10) + rx := regexp.MustCompile(`(?m)^Pinging\s+([a-zA-Z0-9.\-]+)\s+\[.*$`) + for _, ans := range rx.FindAllStringSubmatch(r, -1) { + ptr = append(ptr, absDomainName(ans[1])) + } + return +} + +func localIP() (ip IP, err error) { + conn, err := Dial("udp", "golang.org:80") + if err != nil { + return nil, err + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*UDPAddr) + + return localAddr.IP, nil +} |