Black Lives Matter. Support the Equal Justice Initiative.

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