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