lookup_windows_test.go 8.0 KB

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