summaryrefslogtreecommitdiffstats
path: root/src/net/lookup_windows_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/lookup_windows_test.go')
-rw-r--r--src/net/lookup_windows_test.go340
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
+}