Home | History | Annotate | Download | only in 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 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 	"syscall"
     23 	"unsafe"
     24 )
     25 
     26 // An addrinfoErrno represents a getaddrinfo, getnameinfo-specific
     27 // error number. It's a signed number and a zero value is a non-error
     28 // by convention.
     29 type addrinfoErrno int
     30 
     31 func (eai addrinfoErrno) Error() string   { return C.GoString(C.gai_strerror(C.int(eai))) }
     32 func (eai addrinfoErrno) Temporary() bool { return eai == C.EAI_AGAIN }
     33 func (eai addrinfoErrno) Timeout() bool   { return false }
     34 
     35 func cgoLookupHost(name string) (hosts []string, err error, completed bool) {
     36 	addrs, err, completed := cgoLookupIP(name)
     37 	for _, addr := range addrs {
     38 		hosts = append(hosts, addr.String())
     39 	}
     40 	return
     41 }
     42 
     43 func cgoLookupPort(network, service string) (port int, err error, completed bool) {
     44 	acquireThread()
     45 	defer releaseThread()
     46 
     47 	var hints C.struct_addrinfo
     48 	switch network {
     49 	case "": // no hints
     50 	case "tcp", "tcp4", "tcp6":
     51 		hints.ai_socktype = C.SOCK_STREAM
     52 		hints.ai_protocol = C.IPPROTO_TCP
     53 	case "udp", "udp4", "udp6":
     54 		hints.ai_socktype = C.SOCK_DGRAM
     55 		hints.ai_protocol = C.IPPROTO_UDP
     56 	default:
     57 		return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
     58 	}
     59 	if len(network) >= 4 {
     60 		switch network[3] {
     61 		case '4':
     62 			hints.ai_family = C.AF_INET
     63 		case '6':
     64 			hints.ai_family = C.AF_INET6
     65 		}
     66 	}
     67 
     68 	s := C.CString(service)
     69 	var res *C.struct_addrinfo
     70 	defer C.free(unsafe.Pointer(s))
     71 	gerrno, err := C.getaddrinfo(nil, s, &hints, &res)
     72 	if gerrno != 0 {
     73 		switch gerrno {
     74 		case C.EAI_SYSTEM:
     75 			if err == nil { // see golang.org/issue/6232
     76 				err = syscall.EMFILE
     77 			}
     78 		default:
     79 			err = addrinfoErrno(gerrno)
     80 		}
     81 		return 0, &DNSError{Err: err.Error(), Name: network + "/" + service}, true
     82 	}
     83 	defer C.freeaddrinfo(res)
     84 
     85 	for r := res; r != nil; r = r.ai_next {
     86 		switch r.ai_family {
     87 		case C.AF_INET:
     88 			sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
     89 			p := (*[2]byte)(unsafe.Pointer(&sa.Port))
     90 			return int(p[0])<<8 | int(p[1]), nil, true
     91 		case C.AF_INET6:
     92 			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
     93 			p := (*[2]byte)(unsafe.Pointer(&sa.Port))
     94 			return int(p[0])<<8 | int(p[1]), nil, true
     95 		}
     96 	}
     97 	return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}, true
     98 }
     99 
    100 func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, completed bool) {
    101 	acquireThread()
    102 	defer releaseThread()
    103 
    104 	var hints C.struct_addrinfo
    105 	hints.ai_flags = cgoAddrInfoFlags
    106 	hints.ai_socktype = C.SOCK_STREAM
    107 
    108 	h := C.CString(name)
    109 	defer C.free(unsafe.Pointer(h))
    110 	var res *C.struct_addrinfo
    111 	gerrno, err := C.getaddrinfo(h, nil, &hints, &res)
    112 	if gerrno != 0 {
    113 		switch gerrno {
    114 		case C.EAI_SYSTEM:
    115 			if err == nil {
    116 				// err should not be nil, but sometimes getaddrinfo returns
    117 				// gerrno == C.EAI_SYSTEM with err == nil on Linux.
    118 				// The report claims that it happens when we have too many
    119 				// open files, so use syscall.EMFILE (too many open files in system).
    120 				// Most system calls would return ENFILE (too many open files),
    121 				// so at the least EMFILE should be easy to recognize if this
    122 				// comes up again. golang.org/issue/6232.
    123 				err = syscall.EMFILE
    124 			}
    125 		case C.EAI_NONAME:
    126 			err = errNoSuchHost
    127 		default:
    128 			err = addrinfoErrno(gerrno)
    129 		}
    130 		return nil, "", &DNSError{Err: err.Error(), Name: name}, true
    131 	}
    132 	defer C.freeaddrinfo(res)
    133 
    134 	if res != nil {
    135 		cname = C.GoString(res.ai_canonname)
    136 		if cname == "" {
    137 			cname = name
    138 		}
    139 		if len(cname) > 0 && cname[len(cname)-1] != '.' {
    140 			cname += "."
    141 		}
    142 	}
    143 	for r := res; r != nil; r = r.ai_next {
    144 		// We only asked for SOCK_STREAM, but check anyhow.
    145 		if r.ai_socktype != C.SOCK_STREAM {
    146 			continue
    147 		}
    148 		switch r.ai_family {
    149 		case C.AF_INET:
    150 			sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
    151 			addr := IPAddr{IP: copyIP(sa.Addr[:])}
    152 			addrs = append(addrs, addr)
    153 		case C.AF_INET6:
    154 			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
    155 			addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneToString(int(sa.Scope_id))}
    156 			addrs = append(addrs, addr)
    157 		}
    158 	}
    159 	return addrs, cname, nil, true
    160 }
    161 
    162 func cgoLookupIP(name string) (addrs []IPAddr, err error, completed bool) {
    163 	addrs, _, err, completed = cgoLookupIPCNAME(name)
    164 	return
    165 }
    166 
    167 func cgoLookupCNAME(name string) (cname string, err error, completed bool) {
    168 	_, cname, err, completed = cgoLookupIPCNAME(name)
    169 	return
    170 }
    171 
    172 // These are roughly enough for the following:
    173 //
    174 // Source		Encoding			Maximum length of single name entry
    175 // Unicast DNS		ASCII or			<=253 + a NUL terminator
    176 //			Unicode in RFC 5892		252 * total number of labels + delimiters + a NUL terminator
    177 // Multicast DNS	UTF-8 in RFC 5198 or		<=253 + a NUL terminator
    178 //			the same as unicast DNS ASCII	<=253 + a NUL terminator
    179 // Local database	various				depends on implementation
    180 const (
    181 	nameinfoLen    = 64
    182 	maxNameinfoLen = 4096
    183 )
    184 
    185 func cgoLookupPTR(addr string) ([]string, error, bool) {
    186 	acquireThread()
    187 	defer releaseThread()
    188 
    189 	ip := ParseIP(addr)
    190 	if ip == nil {
    191 		return nil, &DNSError{Err: "invalid address", Name: addr}, true
    192 	}
    193 	sa, salen := cgoSockaddr(ip)
    194 	if sa == nil {
    195 		return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true
    196 	}
    197 	var err error
    198 	var b []byte
    199 	var gerrno int
    200 	for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
    201 		b = make([]byte, l)
    202 		gerrno, err = cgoNameinfoPTR(b, sa, salen)
    203 		if gerrno == 0 || gerrno != C.EAI_OVERFLOW {
    204 			break
    205 		}
    206 	}
    207 	if gerrno != 0 {
    208 		switch gerrno {
    209 		case C.EAI_SYSTEM:
    210 			if err == nil { // see golang.org/issue/6232
    211 				err = syscall.EMFILE
    212 			}
    213 		default:
    214 			err = addrinfoErrno(gerrno)
    215 		}
    216 		return nil, &DNSError{Err: err.Error(), Name: addr}, true
    217 	}
    218 
    219 	for i := 0; i < len(b); i++ {
    220 		if b[i] == 0 {
    221 			b = b[:i]
    222 			break
    223 		}
    224 	}
    225 	// Add trailing dot to match pure Go reverse resolver
    226 	// and all other lookup routines. See golang.org/issue/12189.
    227 	if len(b) > 0 && b[len(b)-1] != '.' {
    228 		b = append(b, '.')
    229 	}
    230 	return []string{string(b)}, nil, true
    231 }
    232 
    233 func cgoSockaddr(ip IP) (*C.struct_sockaddr, C.socklen_t) {
    234 	if ip4 := ip.To4(); ip4 != nil {
    235 		return cgoSockaddrInet4(ip4), C.socklen_t(syscall.SizeofSockaddrInet4)
    236 	}
    237 	if ip6 := ip.To16(); ip6 != nil {
    238 		return cgoSockaddrInet6(ip6), C.socklen_t(syscall.SizeofSockaddrInet6)
    239 	}
    240 	return nil, 0
    241 }
    242 
    243 func copyIP(x IP) IP {
    244 	if len(x) < 16 {
    245 		return x.To16()
    246 	}
    247 	y := make(IP, len(x))
    248 	copy(y, x)
    249 	return y
    250 }
    251