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