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

View as plain text