github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/net/cgo_unix.go (about) 1 // Copyright 2011 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 // +build cgo,!netgo 6 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 7 8 package net 9 10 /* 11 #include <sys/types.h> 12 #include <sys/socket.h> 13 #include <netinet/in.h> 14 #include <netdb.h> 15 #include <unistd.h> 16 #include <string.h> 17 18 // If nothing else defined EAI_OVERFLOW, make sure it has a value. 19 #ifndef EAI_OVERFLOW 20 #define EAI_OVERFLOW -12 21 #endif 22 */ 23 import "github.com/x04/go/src/C" 24 25 import ( 26 "github.com/x04/go/src/context" 27 "github.com/x04/go/src/syscall" 28 "github.com/x04/go/src/unsafe" 29 ) 30 31 // An addrinfoErrno represents a getaddrinfo, getnameinfo-specific 32 // error number. It's a signed number and a zero value is a non-error 33 // by convention. 34 type addrinfoErrno int 35 36 func (eai addrinfoErrno) Error() string { return C.GoString(C.gai_strerror(C.int(eai))) } 37 func (eai addrinfoErrno) Temporary() bool { return eai == C.EAI_AGAIN } 38 func (eai addrinfoErrno) Timeout() bool { return false } 39 40 type portLookupResult struct { 41 port int 42 err error 43 } 44 45 type ipLookupResult struct { 46 addrs []IPAddr 47 cname string 48 err error 49 } 50 51 type reverseLookupResult struct { 52 names []string 53 err error 54 } 55 56 func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) { 57 addrs, err, completed := cgoLookupIP(ctx, "ip", name) 58 for _, addr := range addrs { 59 hosts = append(hosts, addr.String()) 60 } 61 return 62 } 63 64 func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) { 65 var hints C.struct_addrinfo 66 switch network { 67 case "": // no hints 68 case "tcp", "tcp4", "tcp6": 69 hints.ai_socktype = C.SOCK_STREAM 70 hints.ai_protocol = C.IPPROTO_TCP 71 case "udp", "udp4", "udp6": 72 hints.ai_socktype = C.SOCK_DGRAM 73 hints.ai_protocol = C.IPPROTO_UDP 74 default: 75 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true 76 } 77 switch ipVersion(network) { 78 case '4': 79 hints.ai_family = C.AF_INET 80 case '6': 81 hints.ai_family = C.AF_INET6 82 } 83 if ctx.Done() == nil { 84 port, err := cgoLookupServicePort(&hints, network, service) 85 return port, err, true 86 } 87 result := make(chan portLookupResult, 1) 88 go cgoPortLookup(result, &hints, network, service) 89 select { 90 case r := <-result: 91 return r.port, r.err, true 92 case <-ctx.Done(): 93 // Since there isn't a portable way to cancel the lookup, 94 // we just let it finish and write to the buffered channel. 95 return 0, mapErr(ctx.Err()), false 96 } 97 } 98 99 func cgoLookupServicePort(hints *C.struct_addrinfo, network, service string) (port int, err error) { 100 cservice := make([]byte, len(service)+1) 101 copy(cservice, service) 102 // Lowercase the C service name. 103 for i, b := range cservice[:len(service)] { 104 cservice[i] = lowerASCII(b) 105 } 106 var res *C.struct_addrinfo 107 gerrno, err := C.getaddrinfo(nil, (*C.char)(unsafe.Pointer(&cservice[0])), hints, &res) 108 if gerrno != 0 { 109 isTemporary := false 110 switch gerrno { 111 case C.EAI_SYSTEM: 112 if err == nil { // see golang.org/issue/6232 113 err = syscall.EMFILE 114 } 115 default: 116 err = addrinfoErrno(gerrno) 117 isTemporary = addrinfoErrno(gerrno).Temporary() 118 } 119 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service, IsTemporary: isTemporary} 120 } 121 defer C.freeaddrinfo(res) 122 123 for r := res; r != nil; r = r.ai_next { 124 switch r.ai_family { 125 case C.AF_INET: 126 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr)) 127 p := (*[2]byte)(unsafe.Pointer(&sa.Port)) 128 return int(p[0])<<8 | int(p[1]), nil 129 case C.AF_INET6: 130 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr)) 131 p := (*[2]byte)(unsafe.Pointer(&sa.Port)) 132 return int(p[0])<<8 | int(p[1]), nil 133 } 134 } 135 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service} 136 } 137 138 func cgoPortLookup(result chan<- portLookupResult, hints *C.struct_addrinfo, network, service string) { 139 port, err := cgoLookupServicePort(hints, network, service) 140 result <- portLookupResult{port, err} 141 } 142 143 func cgoLookupIPCNAME(network, name string) (addrs []IPAddr, cname string, err error) { 144 acquireThread() 145 defer releaseThread() 146 147 var hints C.struct_addrinfo 148 hints.ai_flags = cgoAddrInfoFlags 149 hints.ai_socktype = C.SOCK_STREAM 150 hints.ai_family = C.AF_UNSPEC 151 switch ipVersion(network) { 152 case '4': 153 hints.ai_family = C.AF_INET 154 case '6': 155 hints.ai_family = C.AF_INET6 156 } 157 158 h := make([]byte, len(name)+1) 159 copy(h, name) 160 var res *C.struct_addrinfo 161 gerrno, err := C.getaddrinfo((*C.char)(unsafe.Pointer(&h[0])), nil, &hints, &res) 162 if gerrno != 0 { 163 isErrorNoSuchHost := false 164 isTemporary := false 165 switch gerrno { 166 case C.EAI_SYSTEM: 167 if err == nil { 168 // err should not be nil, but sometimes getaddrinfo returns 169 // gerrno == C.EAI_SYSTEM with err == nil on Linux. 170 // The report claims that it happens when we have too many 171 // open files, so use syscall.EMFILE (too many open files in system). 172 // Most system calls would return ENFILE (too many open files), 173 // so at the least EMFILE should be easy to recognize if this 174 // comes up again. golang.org/issue/6232. 175 err = syscall.EMFILE 176 } 177 case C.EAI_NONAME: 178 err = errNoSuchHost 179 isErrorNoSuchHost = true 180 default: 181 err = addrinfoErrno(gerrno) 182 isTemporary = addrinfoErrno(gerrno).Temporary() 183 } 184 185 return nil, "", &DNSError{Err: err.Error(), Name: name, IsNotFound: isErrorNoSuchHost, IsTemporary: isTemporary} 186 } 187 defer C.freeaddrinfo(res) 188 189 if res != nil { 190 cname = C.GoString(res.ai_canonname) 191 if cname == "" { 192 cname = name 193 } 194 if len(cname) > 0 && cname[len(cname)-1] != '.' { 195 cname += "." 196 } 197 } 198 for r := res; r != nil; r = r.ai_next { 199 // We only asked for SOCK_STREAM, but check anyhow. 200 if r.ai_socktype != C.SOCK_STREAM { 201 continue 202 } 203 switch r.ai_family { 204 case C.AF_INET: 205 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr)) 206 addr := IPAddr{IP: copyIP(sa.Addr[:])} 207 addrs = append(addrs, addr) 208 case C.AF_INET6: 209 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr)) 210 addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneCache.name(int(sa.Scope_id))} 211 addrs = append(addrs, addr) 212 } 213 } 214 return addrs, cname, nil 215 } 216 217 func cgoIPLookup(result chan<- ipLookupResult, network, name string) { 218 addrs, cname, err := cgoLookupIPCNAME(network, name) 219 result <- ipLookupResult{addrs, cname, err} 220 } 221 222 func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) { 223 if ctx.Done() == nil { 224 addrs, _, err = cgoLookupIPCNAME(network, name) 225 return addrs, err, true 226 } 227 result := make(chan ipLookupResult, 1) 228 go cgoIPLookup(result, network, name) 229 select { 230 case r := <-result: 231 return r.addrs, r.err, true 232 case <-ctx.Done(): 233 return nil, mapErr(ctx.Err()), false 234 } 235 } 236 237 func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) { 238 if ctx.Done() == nil { 239 _, cname, err = cgoLookupIPCNAME("ip", name) 240 return cname, err, true 241 } 242 result := make(chan ipLookupResult, 1) 243 go cgoIPLookup(result, "ip", name) 244 select { 245 case r := <-result: 246 return r.cname, r.err, true 247 case <-ctx.Done(): 248 return "", mapErr(ctx.Err()), false 249 } 250 } 251 252 // These are roughly enough for the following: 253 // 254 // Source Encoding Maximum length of single name entry 255 // Unicast DNS ASCII or <=253 + a NUL terminator 256 // Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator 257 // Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator 258 // the same as unicast DNS ASCII <=253 + a NUL terminator 259 // Local database various depends on implementation 260 const ( 261 nameinfoLen = 64 262 maxNameinfoLen = 4096 263 ) 264 265 func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, completed bool) { 266 var zone string 267 ip := parseIPv4(addr) 268 if ip == nil { 269 ip, zone = parseIPv6Zone(addr) 270 } 271 if ip == nil { 272 return nil, &DNSError{Err: "invalid address", Name: addr}, true 273 } 274 sa, salen := cgoSockaddr(ip, zone) 275 if sa == nil { 276 return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true 277 } 278 if ctx.Done() == nil { 279 names, err := cgoLookupAddrPTR(addr, sa, salen) 280 return names, err, true 281 } 282 result := make(chan reverseLookupResult, 1) 283 go cgoReverseLookup(result, addr, sa, salen) 284 select { 285 case r := <-result: 286 return r.names, r.err, true 287 case <-ctx.Done(): 288 return nil, mapErr(ctx.Err()), false 289 } 290 } 291 292 func cgoLookupAddrPTR(addr string, sa *C.struct_sockaddr, salen C.socklen_t) (names []string, err error) { 293 acquireThread() 294 defer releaseThread() 295 296 var gerrno int 297 var b []byte 298 for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 { 299 b = make([]byte, l) 300 gerrno, err = cgoNameinfoPTR(b, sa, salen) 301 if gerrno == 0 || gerrno != C.EAI_OVERFLOW { 302 break 303 } 304 } 305 if gerrno != 0 { 306 isTemporary := false 307 switch gerrno { 308 case C.EAI_SYSTEM: 309 if err == nil { // see golang.org/issue/6232 310 err = syscall.EMFILE 311 } 312 default: 313 err = addrinfoErrno(gerrno) 314 isTemporary = addrinfoErrno(gerrno).Temporary() 315 } 316 return nil, &DNSError{Err: err.Error(), Name: addr, IsTemporary: isTemporary} 317 } 318 for i := 0; i < len(b); i++ { 319 if b[i] == 0 { 320 b = b[:i] 321 break 322 } 323 } 324 return []string{absDomainName(b)}, nil 325 } 326 327 func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *C.struct_sockaddr, salen C.socklen_t) { 328 names, err := cgoLookupAddrPTR(addr, sa, salen) 329 result <- reverseLookupResult{names, err} 330 } 331 332 func cgoSockaddr(ip IP, zone string) (*C.struct_sockaddr, C.socklen_t) { 333 if ip4 := ip.To4(); ip4 != nil { 334 return cgoSockaddrInet4(ip4), C.socklen_t(syscall.SizeofSockaddrInet4) 335 } 336 if ip6 := ip.To16(); ip6 != nil { 337 return cgoSockaddrInet6(ip6, zoneCache.index(zone)), C.socklen_t(syscall.SizeofSockaddrInet6) 338 } 339 return nil, 0 340 } 341 342 func copyIP(x IP) IP { 343 if len(x) < 16 { 344 return x.To16() 345 } 346 y := make(IP, len(x)) 347 copy(y, x) 348 return y 349 }