Source file src/net/cgo_unix.go

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

View as plain text