lookup_windows.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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. "context"
  7. "internal/syscall/windows"
  8. "os"
  9. "runtime"
  10. "syscall"
  11. "unsafe"
  12. )
  13. const _WSAHOST_NOT_FOUND = syscall.Errno(11001)
  14. func winError(call string, err error) error {
  15. switch err {
  16. case _WSAHOST_NOT_FOUND:
  17. return errNoSuchHost
  18. }
  19. return os.NewSyscallError(call, err)
  20. }
  21. func getprotobyname(name string) (proto int, err error) {
  22. p, err := syscall.GetProtoByName(name)
  23. if err != nil {
  24. return 0, winError("getprotobyname", err)
  25. }
  26. return int(p.Proto), nil
  27. }
  28. // lookupProtocol looks up IP protocol name and returns correspondent protocol number.
  29. func lookupProtocol(ctx context.Context, name string) (int, error) {
  30. // GetProtoByName return value is stored in thread local storage.
  31. // Start new os thread before the call to prevent races.
  32. type result struct {
  33. proto int
  34. err error
  35. }
  36. ch := make(chan result) // unbuffered
  37. go func() {
  38. acquireThread()
  39. defer releaseThread()
  40. runtime.LockOSThread()
  41. defer runtime.UnlockOSThread()
  42. proto, err := getprotobyname(name)
  43. select {
  44. case ch <- result{proto: proto, err: err}:
  45. case <-ctx.Done():
  46. }
  47. }()
  48. select {
  49. case r := <-ch:
  50. if r.err != nil {
  51. if proto, err := lookupProtocolMap(name); err == nil {
  52. return proto, nil
  53. }
  54. dnsError := &DNSError{Err: r.err.Error(), Name: name}
  55. if r.err == errNoSuchHost {
  56. dnsError.IsNotFound = true
  57. }
  58. r.err = dnsError
  59. }
  60. return r.proto, r.err
  61. case <-ctx.Done():
  62. return 0, mapErr(ctx.Err())
  63. }
  64. }
  65. func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error) {
  66. ips, err := r.lookupIP(ctx, "ip", name)
  67. if err != nil {
  68. return nil, err
  69. }
  70. addrs := make([]string, 0, len(ips))
  71. for _, ip := range ips {
  72. addrs = append(addrs, ip.String())
  73. }
  74. return addrs, nil
  75. }
  76. func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr, error) {
  77. // TODO(bradfitz,brainman): use ctx more. See TODO below.
  78. var family int32 = syscall.AF_UNSPEC
  79. switch ipVersion(network) {
  80. case '4':
  81. family = syscall.AF_INET
  82. case '6':
  83. family = syscall.AF_INET6
  84. }
  85. getaddr := func() ([]IPAddr, error) {
  86. acquireThread()
  87. defer releaseThread()
  88. hints := syscall.AddrinfoW{
  89. Family: family,
  90. Socktype: syscall.SOCK_STREAM,
  91. Protocol: syscall.IPPROTO_IP,
  92. }
  93. var result *syscall.AddrinfoW
  94. name16p, err := syscall.UTF16PtrFromString(name)
  95. if err != nil {
  96. return nil, &DNSError{Name: name, Err: err.Error()}
  97. }
  98. e := syscall.GetAddrInfoW(name16p, nil, &hints, &result)
  99. if e != nil {
  100. err := winError("getaddrinfow", e)
  101. dnsError := &DNSError{Err: err.Error(), Name: name}
  102. if err == errNoSuchHost {
  103. dnsError.IsNotFound = true
  104. }
  105. return nil, dnsError
  106. }
  107. defer syscall.FreeAddrInfoW(result)
  108. addrs := make([]IPAddr, 0, 5)
  109. for ; result != nil; result = result.Next {
  110. addr := unsafe.Pointer(result.Addr)
  111. switch result.Family {
  112. case syscall.AF_INET:
  113. a := (*syscall.RawSockaddrInet4)(addr).Addr
  114. addrs = append(addrs, IPAddr{IP: IPv4(a[0], a[1], a[2], a[3])})
  115. case syscall.AF_INET6:
  116. a := (*syscall.RawSockaddrInet6)(addr).Addr
  117. zone := zoneCache.name(int((*syscall.RawSockaddrInet6)(addr).Scope_id))
  118. addrs = append(addrs, IPAddr{IP: IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]}, Zone: zone})
  119. default:
  120. return nil, &DNSError{Err: syscall.EWINDOWS.Error(), Name: name}
  121. }
  122. }
  123. return addrs, nil
  124. }
  125. type ret struct {
  126. addrs []IPAddr
  127. err error
  128. }
  129. var ch chan ret
  130. if ctx.Err() == nil {
  131. ch = make(chan ret, 1)
  132. go func() {
  133. addr, err := getaddr()
  134. ch <- ret{addrs: addr, err: err}
  135. }()
  136. }
  137. select {
  138. case r := <-ch:
  139. return r.addrs, r.err
  140. case <-ctx.Done():
  141. // TODO(bradfitz,brainman): cancel the ongoing
  142. // GetAddrInfoW? It would require conditionally using
  143. // GetAddrInfoEx with lpOverlapped, which requires
  144. // Windows 8 or newer. I guess we'll need oldLookupIP,
  145. // newLookupIP, and newerLookUP.
  146. //
  147. // For now we just let it finish and write to the
  148. // buffered channel.
  149. return nil, &DNSError{
  150. Name: name,
  151. Err: ctx.Err().Error(),
  152. IsTimeout: ctx.Err() == context.DeadlineExceeded,
  153. }
  154. }
  155. }
  156. func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
  157. if r.preferGo() {
  158. return lookupPortMap(network, service)
  159. }
  160. // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
  161. acquireThread()
  162. defer releaseThread()
  163. var stype int32
  164. switch network {
  165. case "tcp4", "tcp6":
  166. stype = syscall.SOCK_STREAM
  167. case "udp4", "udp6":
  168. stype = syscall.SOCK_DGRAM
  169. }
  170. hints := syscall.AddrinfoW{
  171. Family: syscall.AF_UNSPEC,
  172. Socktype: stype,
  173. Protocol: syscall.IPPROTO_IP,
  174. }
  175. var result *syscall.AddrinfoW
  176. e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result)
  177. if e != nil {
  178. if port, err := lookupPortMap(network, service); err == nil {
  179. return port, nil
  180. }
  181. err := winError("getaddrinfow", e)
  182. dnsError := &DNSError{Err: err.Error(), Name: network + "/" + service}
  183. if err == errNoSuchHost {
  184. dnsError.IsNotFound = true
  185. }
  186. return 0, dnsError
  187. }
  188. defer syscall.FreeAddrInfoW(result)
  189. if result == nil {
  190. return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
  191. }
  192. addr := unsafe.Pointer(result.Addr)
  193. switch result.Family {
  194. case syscall.AF_INET:
  195. a := (*syscall.RawSockaddrInet4)(addr)
  196. return int(syscall.Ntohs(a.Port)), nil
  197. case syscall.AF_INET6:
  198. a := (*syscall.RawSockaddrInet6)(addr)
  199. return int(syscall.Ntohs(a.Port)), nil
  200. }
  201. return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
  202. }
  203. func (*Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
  204. // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
  205. acquireThread()
  206. defer releaseThread()
  207. var r *syscall.DNSRecord
  208. e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
  209. // windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s
  210. if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS {
  211. // if there are no aliases, the canonical name is the input name
  212. return absDomainName(name), nil
  213. }
  214. if e != nil {
  215. return "", &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
  216. }
  217. defer syscall.DnsRecordListFree(r, 1)
  218. resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), r)
  219. cname := windows.UTF16PtrToString(resolved)
  220. return absDomainName(cname), nil
  221. }
  222. func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
  223. // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
  224. acquireThread()
  225. defer releaseThread()
  226. var target string
  227. if service == "" && proto == "" {
  228. target = name
  229. } else {
  230. target = "_" + service + "._" + proto + "." + name
  231. }
  232. var r *syscall.DNSRecord
  233. e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil)
  234. if e != nil {
  235. return "", nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: target}
  236. }
  237. defer syscall.DnsRecordListFree(r, 1)
  238. srvs := make([]*SRV, 0, 10)
  239. for _, p := range validRecs(r, syscall.DNS_TYPE_SRV, target) {
  240. v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))
  241. srvs = append(srvs, &SRV{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:])), v.Port, v.Priority, v.Weight})
  242. }
  243. byPriorityWeight(srvs).sort()
  244. return absDomainName(target), srvs, nil
  245. }
  246. func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
  247. // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
  248. acquireThread()
  249. defer releaseThread()
  250. var r *syscall.DNSRecord
  251. e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
  252. if e != nil {
  253. return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
  254. }
  255. defer syscall.DnsRecordListFree(r, 1)
  256. mxs := make([]*MX, 0, 10)
  257. for _, p := range validRecs(r, syscall.DNS_TYPE_MX, name) {
  258. v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
  259. mxs = append(mxs, &MX{absDomainName(windows.UTF16PtrToString(v.NameExchange)), v.Preference})
  260. }
  261. byPref(mxs).sort()
  262. return mxs, nil
  263. }
  264. func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
  265. // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
  266. acquireThread()
  267. defer releaseThread()
  268. var r *syscall.DNSRecord
  269. e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
  270. if e != nil {
  271. return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
  272. }
  273. defer syscall.DnsRecordListFree(r, 1)
  274. nss := make([]*NS, 0, 10)
  275. for _, p := range validRecs(r, syscall.DNS_TYPE_NS, name) {
  276. v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
  277. nss = append(nss, &NS{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))})
  278. }
  279. return nss, nil
  280. }
  281. func (*Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
  282. // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
  283. acquireThread()
  284. defer releaseThread()
  285. var r *syscall.DNSRecord
  286. e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
  287. if e != nil {
  288. return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
  289. }
  290. defer syscall.DnsRecordListFree(r, 1)
  291. txts := make([]string, 0, 10)
  292. for _, p := range validRecs(r, syscall.DNS_TYPE_TEXT, name) {
  293. d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))
  294. s := ""
  295. for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount:d.StringCount] {
  296. s += windows.UTF16PtrToString(v)
  297. }
  298. txts = append(txts, s)
  299. }
  300. return txts, nil
  301. }
  302. func (*Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
  303. // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
  304. acquireThread()
  305. defer releaseThread()
  306. arpa, err := reverseaddr(addr)
  307. if err != nil {
  308. return nil, err
  309. }
  310. var r *syscall.DNSRecord
  311. e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil)
  312. if e != nil {
  313. return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: addr}
  314. }
  315. defer syscall.DnsRecordListFree(r, 1)
  316. ptrs := make([]string, 0, 10)
  317. for _, p := range validRecs(r, syscall.DNS_TYPE_PTR, arpa) {
  318. v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
  319. ptrs = append(ptrs, absDomainName(windows.UTF16PtrToString(v.Host)))
  320. }
  321. return ptrs, nil
  322. }
  323. const dnsSectionMask = 0x0003
  324. // returns only results applicable to name and resolves CNAME entries
  325. func validRecs(r *syscall.DNSRecord, dnstype uint16, name string) []*syscall.DNSRecord {
  326. cname := syscall.StringToUTF16Ptr(name)
  327. if dnstype != syscall.DNS_TYPE_CNAME {
  328. cname = resolveCNAME(cname, r)
  329. }
  330. rec := make([]*syscall.DNSRecord, 0, 10)
  331. for p := r; p != nil; p = p.Next {
  332. // in case of a local machine, DNS records are returned with DNSREC_QUESTION flag instead of DNS_ANSWER
  333. if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer && p.Dw&dnsSectionMask != syscall.DnsSectionQuestion {
  334. continue
  335. }
  336. if p.Type != dnstype {
  337. continue
  338. }
  339. if !syscall.DnsNameCompare(cname, p.Name) {
  340. continue
  341. }
  342. rec = append(rec, p)
  343. }
  344. return rec
  345. }
  346. // returns the last CNAME in chain
  347. func resolveCNAME(name *uint16, r *syscall.DNSRecord) *uint16 {
  348. // limit cname resolving to 10 in case of an infinite CNAME loop
  349. Cname:
  350. for cnameloop := 0; cnameloop < 10; cnameloop++ {
  351. for p := r; p != nil; p = p.Next {
  352. if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {
  353. continue
  354. }
  355. if p.Type != syscall.DNS_TYPE_CNAME {
  356. continue
  357. }
  358. if !syscall.DnsNameCompare(name, p.Name) {
  359. continue
  360. }
  361. name = (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0])).Host
  362. continue Cname
  363. }
  364. break
  365. }
  366. return name
  367. }
  368. // concurrentThreadsLimit returns the number of threads we permit to
  369. // run concurrently doing DNS lookups.
  370. func concurrentThreadsLimit() int {
  371. return 500
  372. }