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