Source file src/net/lookup_windows_test.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package net
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"internal/testenv"
    13  	"os/exec"
    14  	"reflect"
    15  	"regexp"
    16  	"sort"
    17  	"strings"
    18  	"testing"
    19  )
    20  
    21  var nslookupTestServers = []string{"mail.golang.com", "gmail.com"}
    22  var lookupTestIPs = []string{"8.8.8.8", "1.1.1.1"}
    23  
    24  func toJson(v any) string {
    25  	data, _ := json.Marshal(v)
    26  	return string(data)
    27  }
    28  
    29  func TestNSLookupMX(t *testing.T) {
    30  	testenv.MustHaveExternalNetwork(t)
    31  
    32  	for _, server := range nslookupTestServers {
    33  		mx, err := LookupMX(server)
    34  		if err != nil {
    35  			t.Error(err)
    36  			continue
    37  		}
    38  		if len(mx) == 0 {
    39  			t.Errorf("no results")
    40  			continue
    41  		}
    42  		expected, err := nslookupMX(server)
    43  		if err != nil {
    44  			t.Logf("skipping failed nslookup %s test: %s", server, err)
    45  		}
    46  		sort.Sort(byPrefAndHost(expected))
    47  		sort.Sort(byPrefAndHost(mx))
    48  		if !reflect.DeepEqual(expected, mx) {
    49  			t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(mx))
    50  		}
    51  	}
    52  }
    53  
    54  func TestNSLookupCNAME(t *testing.T) {
    55  	testenv.MustHaveExternalNetwork(t)
    56  
    57  	for _, server := range nslookupTestServers {
    58  		cname, err := LookupCNAME(server)
    59  		if err != nil {
    60  			t.Errorf("failed %s: %s", server, err)
    61  			continue
    62  		}
    63  		if cname == "" {
    64  			t.Errorf("no result %s", server)
    65  		}
    66  		expected, err := nslookupCNAME(server)
    67  		if err != nil {
    68  			t.Logf("skipping failed nslookup %s test: %s", server, err)
    69  			continue
    70  		}
    71  		if expected != cname {
    72  			t.Errorf("different results %s:\texp:%v\tgot:%v", server, expected, cname)
    73  		}
    74  	}
    75  }
    76  
    77  func TestNSLookupNS(t *testing.T) {
    78  	testenv.MustHaveExternalNetwork(t)
    79  
    80  	for _, server := range nslookupTestServers {
    81  		ns, err := LookupNS(server)
    82  		if err != nil {
    83  			t.Errorf("failed %s: %s", server, err)
    84  			continue
    85  		}
    86  		if len(ns) == 0 {
    87  			t.Errorf("no results")
    88  			continue
    89  		}
    90  		expected, err := nslookupNS(server)
    91  		if err != nil {
    92  			t.Logf("skipping failed nslookup %s test: %s", server, err)
    93  			continue
    94  		}
    95  		sort.Sort(byHost(expected))
    96  		sort.Sort(byHost(ns))
    97  		if !reflect.DeepEqual(expected, ns) {
    98  			t.Errorf("different results %s:\texp:%v\tgot:%v", toJson(server), toJson(expected), ns)
    99  		}
   100  	}
   101  }
   102  
   103  func TestNSLookupTXT(t *testing.T) {
   104  	testenv.MustHaveExternalNetwork(t)
   105  
   106  	for _, server := range nslookupTestServers {
   107  		txt, err := LookupTXT(server)
   108  		if err != nil {
   109  			t.Errorf("failed %s: %s", server, err)
   110  			continue
   111  		}
   112  		if len(txt) == 0 {
   113  			t.Errorf("no results")
   114  			continue
   115  		}
   116  		expected, err := nslookupTXT(server)
   117  		if err != nil {
   118  			t.Logf("skipping failed nslookup %s test: %s", server, err)
   119  			continue
   120  		}
   121  		sort.Strings(expected)
   122  		sort.Strings(txt)
   123  		if !reflect.DeepEqual(expected, txt) {
   124  			t.Errorf("different results %s:\texp:%v\tgot:%v", server, toJson(expected), toJson(txt))
   125  		}
   126  	}
   127  }
   128  
   129  func TestLookupLocalPTR(t *testing.T) {
   130  	testenv.MustHaveExternalNetwork(t)
   131  
   132  	addr, err := localIP()
   133  	if err != nil {
   134  		t.Errorf("failed to get local ip: %s", err)
   135  	}
   136  	names, err := LookupAddr(addr.String())
   137  	if err != nil {
   138  		t.Errorf("failed %s: %s", addr, err)
   139  	}
   140  	if len(names) == 0 {
   141  		t.Errorf("no results")
   142  	}
   143  	expected, err := lookupPTR(addr.String())
   144  	if err != nil {
   145  		t.Logf("skipping failed lookup %s test: %s", addr.String(), err)
   146  	}
   147  	sort.Strings(expected)
   148  	sort.Strings(names)
   149  	if !reflect.DeepEqual(expected, names) {
   150  		t.Errorf("different results %s:\texp:%v\tgot:%v", addr, toJson(expected), toJson(names))
   151  	}
   152  }
   153  
   154  func TestLookupPTR(t *testing.T) {
   155  	testenv.MustHaveExternalNetwork(t)
   156  
   157  	for _, addr := range lookupTestIPs {
   158  		names, err := LookupAddr(addr)
   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)
   166  		if err != nil {
   167  			t.Logf("skipping failed lookup %s test: %s", addr, err)
   168  		}
   169  		sort.Strings(expected)
   170  		sort.Strings(names)
   171  		if !reflect.DeepEqual(expected, names) {
   172  			t.Errorf("different results %s:\texp:%v\tgot:%v", addr, toJson(expected), toJson(names))
   173  		}
   174  	}
   175  }
   176  
   177  type byPrefAndHost []*MX
   178  
   179  func (s byPrefAndHost) Len() int { return len(s) }
   180  func (s byPrefAndHost) Less(i, j int) bool {
   181  	if s[i].Pref != s[j].Pref {
   182  		return s[i].Pref < s[j].Pref
   183  	}
   184  	return s[i].Host < s[j].Host
   185  }
   186  func (s byPrefAndHost) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   187  
   188  type byHost []*NS
   189  
   190  func (s byHost) Len() int           { return len(s) }
   191  func (s byHost) Less(i, j int) bool { return s[i].Host < s[j].Host }
   192  func (s byHost) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   193  
   194  func nslookup(qtype, name string) (string, error) {
   195  	var out bytes.Buffer
   196  	var err bytes.Buffer
   197  	cmd := exec.Command("nslookup", "-querytype="+qtype, name)
   198  	cmd.Stdout = &out
   199  	cmd.Stderr = &err
   200  	if err := cmd.Run(); err != nil {
   201  		return "", err
   202  	}
   203  	r := strings.ReplaceAll(out.String(), "\r\n", "\n")
   204  	// nslookup stderr output contains also debug information such as
   205  	// "Non-authoritative answer" and it doesn't return the correct errcode
   206  	if strings.Contains(err.String(), "can't find") {
   207  		return r, errors.New(err.String())
   208  	}
   209  	return r, nil
   210  }
   211  
   212  func nslookupMX(name string) (mx []*MX, err error) {
   213  	var r string
   214  	if r, err = nslookup("mx", name); err != nil {
   215  		return
   216  	}
   217  	mx = make([]*MX, 0, 10)
   218  	// linux nslookup syntax
   219  	// golang.org      mail exchanger = 2 alt1.aspmx.l.google.com.
   220  	rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+mail exchanger\s*=\s*([0-9]+)\s*([a-z0-9.\-]+)$`)
   221  	for _, ans := range rx.FindAllStringSubmatch(r, -1) {
   222  		pref, _, _ := dtoi(ans[2])
   223  		mx = append(mx, &MX{absDomainName(ans[3]), uint16(pref)})
   224  	}
   225  	// windows nslookup syntax
   226  	// gmail.com       MX preference = 30, mail exchanger = alt3.gmail-smtp-in.l.google.com
   227  	rx = regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+MX preference\s*=\s*([0-9]+)\s*,\s*mail exchanger\s*=\s*([a-z0-9.\-]+)$`)
   228  	for _, ans := range rx.FindAllStringSubmatch(r, -1) {
   229  		pref, _, _ := dtoi(ans[2])
   230  		mx = append(mx, &MX{absDomainName(ans[3]), uint16(pref)})
   231  	}
   232  	return
   233  }
   234  
   235  func nslookupNS(name string) (ns []*NS, err error) {
   236  	var r string
   237  	if r, err = nslookup("ns", name); err != nil {
   238  		return
   239  	}
   240  	ns = make([]*NS, 0, 10)
   241  	// golang.org      nameserver = ns1.google.com.
   242  	rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+nameserver\s*=\s*([a-z0-9.\-]+)$`)
   243  	for _, ans := range rx.FindAllStringSubmatch(r, -1) {
   244  		ns = append(ns, &NS{absDomainName(ans[2])})
   245  	}
   246  	return
   247  }
   248  
   249  func nslookupCNAME(name string) (cname string, err error) {
   250  	var r string
   251  	if r, err = nslookup("cname", name); err != nil {
   252  		return
   253  	}
   254  	// mail.golang.com canonical name = golang.org.
   255  	rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+canonical name\s*=\s*([a-z0-9.\-]+)$`)
   256  	// assumes the last CNAME is the correct one
   257  	last := name
   258  	for _, ans := range rx.FindAllStringSubmatch(r, -1) {
   259  		last = ans[2]
   260  	}
   261  	return absDomainName(last), nil
   262  }
   263  
   264  func nslookupTXT(name string) (txt []string, err error) {
   265  	var r string
   266  	if r, err = nslookup("txt", name); err != nil {
   267  		return
   268  	}
   269  	txt = make([]string, 0, 10)
   270  	// linux
   271  	// golang.org      text = "v=spf1 redirect=_spf.google.com"
   272  
   273  	// windows
   274  	// golang.org      text =
   275  	//
   276  	//    "v=spf1 redirect=_spf.google.com"
   277  	rx := regexp.MustCompile(`(?m)^([a-z0-9.\-]+)\s+text\s*=\s*"(.*)"$`)
   278  	for _, ans := range rx.FindAllStringSubmatch(r, -1) {
   279  		txt = append(txt, ans[2])
   280  	}
   281  	return
   282  }
   283  
   284  func ping(name string) (string, error) {
   285  	cmd := exec.Command("ping", "-n", "1", "-a", name)
   286  	stdoutStderr, err := cmd.CombinedOutput()
   287  	if err != nil {
   288  		return "", fmt.Errorf("%v: %v", err, string(stdoutStderr))
   289  	}
   290  	r := strings.ReplaceAll(string(stdoutStderr), "\r\n", "\n")
   291  	return r, nil
   292  }
   293  
   294  func lookupPTR(name string) (ptr []string, err error) {
   295  	var r string
   296  	if r, err = ping(name); err != nil {
   297  		return
   298  	}
   299  	ptr = make([]string, 0, 10)
   300  	rx := regexp.MustCompile(`(?m)^Pinging\s+([a-zA-Z0-9.\-]+)\s+\[.*$`)
   301  	for _, ans := range rx.FindAllStringSubmatch(r, -1) {
   302  		ptr = append(ptr, absDomainName(ans[1]))
   303  	}
   304  	return
   305  }
   306  
   307  func localIP() (ip IP, err error) {
   308  	conn, err := Dial("udp", "golang.org:80")
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  	defer conn.Close()
   313  
   314  	localAddr := conn.LocalAddr().(*UDPAddr)
   315  
   316  	return localAddr.IP, nil
   317  }
   318  

View as plain text