github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/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 // This file is called cgo_unix.go, but to allow syscalls-to-libc-based 6 // implementations to share the code, it does not use cgo directly. 7 // Instead of C.foo it uses _C_foo, which is defined in either 8 // cgo_unix_cgo.go or cgo_unix_syscall.go 9 10 //go:build !netgo && ((cgo && unix) || darwin) 11 12 package net 13 14 import ( 15 "context" 16 "errors" 17 "internal/bytealg" 18 "net/netip" 19 "syscall" 20 "unsafe" 21 22 "golang.org/x/net/dns/dnsmessage" 23 ) 24 25 // cgoAvailable set to true to indicate that the cgo resolver 26 // is available on this system. 27 const cgoAvailable = true 28 29 // An addrinfoErrno represents a getaddrinfo, getnameinfo-specific 30 // error number. It's a signed number and a zero value is a non-error 31 // by convention. 32 type addrinfoErrno int 33 34 func (eai addrinfoErrno) Error() string { return _C_gai_strerror(_C_int(eai)) } 35 func (eai addrinfoErrno) Temporary() bool { return eai == _C_EAI_AGAIN } 36 func (eai addrinfoErrno) Timeout() bool { return false } 37 38 // isAddrinfoErrno is just for testing purposes. 39 func (eai addrinfoErrno) isAddrinfoErrno() {} 40 41 // doBlockingWithCtx executes a blocking function in a separate goroutine when the provided 42 // context is cancellable. It is intended for use with calls that don't support context 43 // cancellation (cgo, syscalls). blocking func may still be running after this function finishes. 44 // For the duration of the execution of the blocking function, the thread is 'acquired' using [acquireThread], 45 // blocking might not be executed when the context gets cancelled early. 46 func doBlockingWithCtx[T any](ctx context.Context, lookupName string, blocking func() (T, error)) (T, error) { 47 if err := acquireThread(ctx); err != nil { 48 var zero T 49 return zero, &DNSError{ 50 Name: lookupName, 51 Err: mapErr(err).Error(), 52 IsTimeout: err == context.DeadlineExceeded, 53 } 54 } 55 56 if ctx.Done() == nil { 57 defer releaseThread() 58 return blocking() 59 } 60 61 type result struct { 62 res T 63 err error 64 } 65 66 res := make(chan result, 1) 67 go func() { 68 defer releaseThread() 69 var r result 70 r.res, r.err = blocking() 71 res <- r 72 }() 73 74 select { 75 case r := <-res: 76 return r.res, r.err 77 case <-ctx.Done(): 78 var zero T 79 return zero, &DNSError{ 80 Name: lookupName, 81 Err: mapErr(ctx.Err()).Error(), 82 IsTimeout: ctx.Err() == context.DeadlineExceeded, 83 } 84 } 85 } 86 87 func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error) { 88 addrs, err := cgoLookupIP(ctx, "ip", name) 89 if err != nil { 90 return nil, err 91 } 92 for _, addr := range addrs { 93 hosts = append(hosts, addr.String()) 94 } 95 return hosts, nil 96 } 97 98 func cgoLookupPort(ctx context.Context, network, service string) (port int, err error) { 99 var hints _C_struct_addrinfo 100 switch network { 101 case "ip": // no hints 102 case "tcp", "tcp4", "tcp6": 103 *_C_ai_socktype(&hints) = _C_SOCK_STREAM 104 *_C_ai_protocol(&hints) = _C_IPPROTO_TCP 105 case "udp", "udp4", "udp6": 106 *_C_ai_socktype(&hints) = _C_SOCK_DGRAM 107 *_C_ai_protocol(&hints) = _C_IPPROTO_UDP 108 default: 109 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service} 110 } 111 switch ipVersion(network) { 112 case '4': 113 *_C_ai_family(&hints) = _C_AF_INET 114 case '6': 115 *_C_ai_family(&hints) = _C_AF_INET6 116 } 117 118 return doBlockingWithCtx(ctx, network+"/"+service, func() (int, error) { 119 return cgoLookupServicePort(&hints, network, service) 120 }) 121 } 122 123 func cgoLookupServicePort(hints *_C_struct_addrinfo, network, service string) (port int, err error) { 124 cservice, err := syscall.ByteSliceFromString(service) 125 if err != nil { 126 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service} 127 } 128 // Lowercase the C service name. 129 for i, b := range cservice[:len(service)] { 130 cservice[i] = lowerASCII(b) 131 } 132 var res *_C_struct_addrinfo 133 gerrno, err := _C_getaddrinfo(nil, (*_C_char)(unsafe.Pointer(&cservice[0])), hints, &res) 134 if gerrno != 0 { 135 isTemporary := false 136 switch gerrno { 137 case _C_EAI_SYSTEM: 138 if err == nil { // see golang.org/issue/6232 139 err = syscall.EMFILE 140 } 141 case _C_EAI_SERVICE, _C_EAI_NONAME: // Darwin returns EAI_NONAME. 142 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service, IsNotFound: true} 143 default: 144 err = addrinfoErrno(gerrno) 145 isTemporary = addrinfoErrno(gerrno).Temporary() 146 } 147 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service, IsTemporary: isTemporary} 148 } 149 defer _C_freeaddrinfo(res) 150 151 for r := res; r != nil; r = *_C_ai_next(r) { 152 switch *_C_ai_family(r) { 153 case _C_AF_INET: 154 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(*_C_ai_addr(r))) 155 p := (*[2]byte)(unsafe.Pointer(&sa.Port)) 156 return int(p[0])<<8 | int(p[1]), nil 157 case _C_AF_INET6: 158 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(*_C_ai_addr(r))) 159 p := (*[2]byte)(unsafe.Pointer(&sa.Port)) 160 return int(p[0])<<8 | int(p[1]), nil 161 } 162 } 163 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service, IsNotFound: true} 164 } 165 166 func cgoLookupHostIP(network, name string) (addrs []IPAddr, err error) { 167 var hints _C_struct_addrinfo 168 *_C_ai_flags(&hints) = cgoAddrInfoFlags 169 *_C_ai_socktype(&hints) = _C_SOCK_STREAM 170 *_C_ai_family(&hints) = _C_AF_UNSPEC 171 switch ipVersion(network) { 172 case '4': 173 *_C_ai_family(&hints) = _C_AF_INET 174 case '6': 175 *_C_ai_family(&hints) = _C_AF_INET6 176 } 177 178 h, err := syscall.BytePtrFromString(name) 179 if err != nil { 180 return nil, &DNSError{Err: err.Error(), Name: name} 181 } 182 var res *_C_struct_addrinfo 183 gerrno, err := _C_getaddrinfo((*_C_char)(unsafe.Pointer(h)), nil, &hints, &res) 184 if gerrno != 0 { 185 isErrorNoSuchHost := false 186 isTemporary := false 187 switch gerrno { 188 case _C_EAI_SYSTEM: 189 if err == nil { 190 // err should not be nil, but sometimes getaddrinfo returns 191 // gerrno == _C_EAI_SYSTEM with err == nil on Linux. 192 // The report claims that it happens when we have too many 193 // open files, so use syscall.EMFILE (too many open files in system). 194 // Most system calls would return ENFILE (too many open files), 195 // so at the least EMFILE should be easy to recognize if this 196 // comes up again. golang.org/issue/6232. 197 err = syscall.EMFILE 198 } 199 case _C_EAI_NONAME, _C_EAI_NODATA: 200 err = errNoSuchHost 201 isErrorNoSuchHost = true 202 default: 203 err = addrinfoErrno(gerrno) 204 isTemporary = addrinfoErrno(gerrno).Temporary() 205 } 206 207 return nil, &DNSError{Err: err.Error(), Name: name, IsNotFound: isErrorNoSuchHost, IsTemporary: isTemporary} 208 } 209 defer _C_freeaddrinfo(res) 210 211 for r := res; r != nil; r = *_C_ai_next(r) { 212 // We only asked for SOCK_STREAM, but check anyhow. 213 if *_C_ai_socktype(r) != _C_SOCK_STREAM { 214 continue 215 } 216 switch *_C_ai_family(r) { 217 case _C_AF_INET: 218 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(*_C_ai_addr(r))) 219 addr := IPAddr{IP: copyIP(sa.Addr[:])} 220 addrs = append(addrs, addr) 221 case _C_AF_INET6: 222 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(*_C_ai_addr(r))) 223 addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneCache.name(int(sa.Scope_id))} 224 addrs = append(addrs, addr) 225 } 226 } 227 return addrs, nil 228 } 229 230 func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error) { 231 return doBlockingWithCtx(ctx, name, func() ([]IPAddr, error) { 232 return cgoLookupHostIP(network, name) 233 }) 234 } 235 236 // These are roughly enough for the following: 237 // 238 // Source Encoding Maximum length of single name entry 239 // Unicast DNS ASCII or <=253 + a NUL terminator 240 // Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator 241 // Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator 242 // the same as unicast DNS ASCII <=253 + a NUL terminator 243 // Local database various depends on implementation 244 const ( 245 nameinfoLen = 64 246 maxNameinfoLen = 4096 247 ) 248 249 func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error) { 250 ip, err := netip.ParseAddr(addr) 251 if err != nil { 252 return nil, &DNSError{Err: "invalid address", Name: addr} 253 } 254 sa, salen := cgoSockaddr(IP(ip.AsSlice()), ip.Zone()) 255 if sa == nil { 256 return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr} 257 } 258 259 return doBlockingWithCtx(ctx, addr, func() ([]string, error) { 260 return cgoLookupAddrPTR(addr, sa, salen) 261 }) 262 } 263 264 func cgoLookupAddrPTR(addr string, sa *_C_struct_sockaddr, salen _C_socklen_t) (names []string, err error) { 265 var gerrno int 266 var b []byte 267 for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 { 268 b = make([]byte, l) 269 gerrno, err = cgoNameinfoPTR(b, sa, salen) 270 if gerrno == 0 || gerrno != _C_EAI_OVERFLOW { 271 break 272 } 273 } 274 if gerrno != 0 { 275 isErrorNoSuchHost := false 276 isTemporary := false 277 switch gerrno { 278 case _C_EAI_SYSTEM: 279 if err == nil { // see golang.org/issue/6232 280 err = syscall.EMFILE 281 } 282 case _C_EAI_NONAME: 283 err = errNoSuchHost 284 isErrorNoSuchHost = true 285 default: 286 err = addrinfoErrno(gerrno) 287 isTemporary = addrinfoErrno(gerrno).Temporary() 288 } 289 return nil, &DNSError{Err: err.Error(), Name: addr, IsTemporary: isTemporary, IsNotFound: isErrorNoSuchHost} 290 } 291 if i := bytealg.IndexByte(b, 0); i != -1 { 292 b = b[:i] 293 } 294 return []string{absDomainName(string(b))}, nil 295 } 296 297 func cgoSockaddr(ip IP, zone string) (*_C_struct_sockaddr, _C_socklen_t) { 298 if ip4 := ip.To4(); ip4 != nil { 299 return cgoSockaddrInet4(ip4), _C_socklen_t(syscall.SizeofSockaddrInet4) 300 } 301 if ip6 := ip.To16(); ip6 != nil { 302 return cgoSockaddrInet6(ip6, zoneCache.index(zone)), _C_socklen_t(syscall.SizeofSockaddrInet6) 303 } 304 return nil, 0 305 } 306 307 func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) { 308 resources, err := resSearch(ctx, name, int(dnsmessage.TypeCNAME), int(dnsmessage.ClassINET)) 309 if err != nil { 310 return 311 } 312 cname, err = parseCNAMEFromResources(resources) 313 if err != nil { 314 return "", err, false 315 } 316 return cname, nil, true 317 } 318 319 // resSearch will make a call to the 'res_nsearch' routine in the C library 320 // and parse the output as a slice of DNS resources. 321 func resSearch(ctx context.Context, hostname string, rtype, class int) ([]dnsmessage.Resource, error) { 322 return doBlockingWithCtx(ctx, hostname, func() ([]dnsmessage.Resource, error) { 323 return cgoResSearch(hostname, rtype, class) 324 }) 325 } 326 327 func cgoResSearch(hostname string, rtype, class int) ([]dnsmessage.Resource, error) { 328 resStateSize := unsafe.Sizeof(_C_struct___res_state{}) 329 var state *_C_struct___res_state 330 if resStateSize > 0 { 331 mem := _C_malloc(resStateSize) 332 defer _C_free(mem) 333 memSlice := unsafe.Slice((*byte)(mem), resStateSize) 334 clear(memSlice) 335 state = (*_C_struct___res_state)(unsafe.Pointer(&memSlice[0])) 336 } 337 if err := _C_res_ninit(state); err != nil { 338 return nil, errors.New("res_ninit failure: " + err.Error()) 339 } 340 defer _C_res_nclose(state) 341 342 // Some res_nsearch implementations (like macOS) do not set errno. 343 // They set h_errno, which is not per-thread and useless to us. 344 // res_nsearch returns the size of the DNS response packet. 345 // But if the DNS response packet contains failure-like response codes, 346 // res_search returns -1 even though it has copied the packet into buf, 347 // giving us no way to find out how big the packet is. 348 // For now, we are willing to take res_search's word that there's nothing 349 // useful in the response, even though there *is* a response. 350 bufSize := maxDNSPacketSize 351 buf := (*_C_uchar)(_C_malloc(uintptr(bufSize))) 352 defer _C_free(unsafe.Pointer(buf)) 353 354 s, err := syscall.BytePtrFromString(hostname) 355 if err != nil { 356 return nil, err 357 } 358 359 var size int 360 for { 361 size := _C_res_nsearch(state, (*_C_char)(unsafe.Pointer(s)), class, rtype, buf, bufSize) 362 if size <= 0 || size > 0xffff { 363 return nil, errors.New("res_nsearch failure") 364 } 365 if size <= bufSize { 366 break 367 } 368 369 // Allocate a bigger buffer to fit the entire msg. 370 _C_free(unsafe.Pointer(buf)) 371 bufSize = size 372 buf = (*_C_uchar)(_C_malloc(uintptr(bufSize))) 373 } 374 375 var p dnsmessage.Parser 376 if _, err := p.Start(unsafe.Slice((*byte)(unsafe.Pointer(buf)), size)); err != nil { 377 return nil, err 378 } 379 p.SkipAllQuestions() 380 resources, err := p.AllAnswers() 381 if err != nil { 382 return nil, err 383 } 384 return resources, nil 385 }