...

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	// +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