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