github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/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 // Lowercase the service name in the C-allocated memory. 100 for i := 0; i < len(service); i++ { 101 bp := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(s)) + uintptr(i))) 102 *bp = lowerASCII(*bp) 103 } 104 var res *C.struct_addrinfo 105 defer C.free(unsafe.Pointer(s)) 106 gerrno, err := C.getaddrinfo(nil, s, hints, &res) 107 if gerrno != 0 { 108 switch gerrno { 109 case C.EAI_SYSTEM: 110 if err == nil { // see golang.org/issue/6232 111 err = syscall.EMFILE 112 } 113 default: 114 err = addrinfoErrno(gerrno) 115 } 116 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service} 117 } 118 defer C.freeaddrinfo(res) 119 120 for r := res; r != nil; r = r.ai_next { 121 switch r.ai_family { 122 case C.AF_INET: 123 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr)) 124 p := (*[2]byte)(unsafe.Pointer(&sa.Port)) 125 return int(p[0])<<8 | int(p[1]), nil 126 case C.AF_INET6: 127 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr)) 128 p := (*[2]byte)(unsafe.Pointer(&sa.Port)) 129 return int(p[0])<<8 | int(p[1]), nil 130 } 131 } 132 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service} 133 } 134 135 func cgoPortLookup(result chan<- portLookupResult, hints *C.struct_addrinfo, network, service string) { 136 port, err := cgoLookupServicePort(hints, network, service) 137 result <- portLookupResult{port, err} 138 } 139 140 func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error) { 141 acquireThread() 142 defer releaseThread() 143 144 var hints C.struct_addrinfo 145 hints.ai_flags = cgoAddrInfoFlags 146 hints.ai_socktype = C.SOCK_STREAM 147 148 h := C.CString(name) 149 defer C.free(unsafe.Pointer(h)) 150 var res *C.struct_addrinfo 151 gerrno, err := C.getaddrinfo(h, nil, &hints, &res) 152 if gerrno != 0 { 153 switch gerrno { 154 case C.EAI_SYSTEM: 155 if err == nil { 156 // err should not be nil, but sometimes getaddrinfo returns 157 // gerrno == C.EAI_SYSTEM with err == nil on Linux. 158 // The report claims that it happens when we have too many 159 // open files, so use syscall.EMFILE (too many open files in system). 160 // Most system calls would return ENFILE (too many open files), 161 // so at the least EMFILE should be easy to recognize if this 162 // comes up again. golang.org/issue/6232. 163 err = syscall.EMFILE 164 } 165 case C.EAI_NONAME: 166 err = errNoSuchHost 167 default: 168 err = addrinfoErrno(gerrno) 169 } 170 return nil, "", &DNSError{Err: err.Error(), Name: name} 171 } 172 defer C.freeaddrinfo(res) 173 174 if res != nil { 175 cname = C.GoString(res.ai_canonname) 176 if cname == "" { 177 cname = name 178 } 179 if len(cname) > 0 && cname[len(cname)-1] != '.' { 180 cname += "." 181 } 182 } 183 for r := res; r != nil; r = r.ai_next { 184 // We only asked for SOCK_STREAM, but check anyhow. 185 if r.ai_socktype != C.SOCK_STREAM { 186 continue 187 } 188 switch r.ai_family { 189 case C.AF_INET: 190 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr)) 191 addr := IPAddr{IP: copyIP(sa.Addr[:])} 192 addrs = append(addrs, addr) 193 case C.AF_INET6: 194 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr)) 195 addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneCache.name(int(sa.Scope_id))} 196 addrs = append(addrs, addr) 197 } 198 } 199 return addrs, cname, nil 200 } 201 202 func cgoIPLookup(result chan<- ipLookupResult, name string) { 203 addrs, cname, err := cgoLookupIPCNAME(name) 204 result <- ipLookupResult{addrs, cname, err} 205 } 206 207 func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) { 208 if ctx.Done() == nil { 209 addrs, _, err = cgoLookupIPCNAME(name) 210 return addrs, err, true 211 } 212 result := make(chan ipLookupResult, 1) 213 go cgoIPLookup(result, name) 214 select { 215 case r := <-result: 216 return r.addrs, r.err, true 217 case <-ctx.Done(): 218 return nil, mapErr(ctx.Err()), false 219 } 220 } 221 222 func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) { 223 if ctx.Done() == nil { 224 _, cname, err = cgoLookupIPCNAME(name) 225 return cname, err, true 226 } 227 result := make(chan ipLookupResult, 1) 228 go cgoIPLookup(result, name) 229 select { 230 case r := <-result: 231 return r.cname, r.err, true 232 case <-ctx.Done(): 233 return "", mapErr(ctx.Err()), false 234 } 235 } 236 237 // These are roughly enough for the following: 238 // 239 // Source Encoding Maximum length of single name entry 240 // Unicast DNS ASCII or <=253 + a NUL terminator 241 // Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator 242 // Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator 243 // the same as unicast DNS ASCII <=253 + a NUL terminator 244 // Local database various depends on implementation 245 const ( 246 nameinfoLen = 64 247 maxNameinfoLen = 4096 248 ) 249 250 func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, completed bool) { 251 var zone string 252 ip := parseIPv4(addr) 253 if ip == nil { 254 ip, zone = parseIPv6(addr, true) 255 } 256 if ip == nil { 257 return nil, &DNSError{Err: "invalid address", Name: addr}, true 258 } 259 sa, salen := cgoSockaddr(ip, zone) 260 if sa == nil { 261 return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true 262 } 263 if ctx.Done() == nil { 264 names, err := cgoLookupAddrPTR(addr, sa, salen) 265 return names, err, true 266 } 267 result := make(chan reverseLookupResult, 1) 268 go cgoReverseLookup(result, addr, sa, salen) 269 select { 270 case r := <-result: 271 return r.names, r.err, true 272 case <-ctx.Done(): 273 return nil, mapErr(ctx.Err()), false 274 } 275 } 276 277 func cgoLookupAddrPTR(addr string, sa *C.struct_sockaddr, salen C.socklen_t) (names []string, err error) { 278 acquireThread() 279 defer releaseThread() 280 281 var gerrno int 282 var b []byte 283 for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 { 284 b = make([]byte, l) 285 gerrno, err = cgoNameinfoPTR(b, sa, salen) 286 if gerrno == 0 || gerrno != C.EAI_OVERFLOW { 287 break 288 } 289 } 290 if gerrno != 0 { 291 switch gerrno { 292 case C.EAI_SYSTEM: 293 if err == nil { // see golang.org/issue/6232 294 err = syscall.EMFILE 295 } 296 default: 297 err = addrinfoErrno(gerrno) 298 } 299 return nil, &DNSError{Err: err.Error(), Name: addr} 300 } 301 for i := 0; i < len(b); i++ { 302 if b[i] == 0 { 303 b = b[:i] 304 break 305 } 306 } 307 return []string{absDomainName(b)}, nil 308 } 309 310 func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *C.struct_sockaddr, salen C.socklen_t) { 311 names, err := cgoLookupAddrPTR(addr, sa, salen) 312 result <- reverseLookupResult{names, err} 313 } 314 315 func cgoSockaddr(ip IP, zone string) (*C.struct_sockaddr, C.socklen_t) { 316 if ip4 := ip.To4(); ip4 != nil { 317 return cgoSockaddrInet4(ip4), C.socklen_t(syscall.SizeofSockaddrInet4) 318 } 319 if ip6 := ip.To16(); ip6 != nil { 320 return cgoSockaddrInet6(ip6, zoneCache.index(zone)), C.socklen_t(syscall.SizeofSockaddrInet6) 321 } 322 return nil, 0 323 } 324 325 func copyIP(x IP) IP { 326 if len(x) < 16 { 327 return x.To16() 328 } 329 y := make(IP, len(x)) 330 copy(y, x) 331 return y 332 }