...
Run Format

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

View as plain text