...

Source file src/net/cgo_unix.go

Documentation: net

		 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  }
	 352  

View as plain text