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