cgo_unix.go 9.7 KB
Newer Older
1
// Copyright 2011 The Go Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

5 6
// +build cgo,!netgo
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
7

8 9 10 11 12 13 14 15 16 17 18 19 20
package net

/*
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
*/

import (
21
	"context"
22 23 24 25
	"syscall"
	"unsafe"
)

26 27 28 29 30 31 32 33
//extern getaddrinfo
func libc_getaddrinfo(node *byte, service *byte, hints *syscall.Addrinfo, res **syscall.Addrinfo) int

//extern freeaddrinfo
func libc_freeaddrinfo(res *syscall.Addrinfo)

//extern gai_strerror
func libc_gai_strerror(errcode int) *byte
34

35 36 37 38 39 40 41 42 43 44 45
// bytePtrToString takes a NUL-terminated array of bytes and convert
// it to a Go string.
func bytePtrToString(p *byte) string {
	a := (*[10000]byte)(unsafe.Pointer(p))
	i := 0
	for a[i] != 0 {
		i++
	}
	return string(a[:i])
}

46 47 48 49 50 51 52 53 54
// An addrinfoErrno represents a getaddrinfo, getnameinfo-specific
// error number. It's a signed number and a zero value is a non-error
// by convention.
type addrinfoErrno int

func (eai addrinfoErrno) Error() string   { return bytePtrToString(libc_gai_strerror(int(eai))) }
func (eai addrinfoErrno) Temporary() bool { return eai == syscall.EAI_AGAIN }
func (eai addrinfoErrno) Timeout() bool   { return false }

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
type portLookupResult struct {
	port int
	err  error
}

type ipLookupResult struct {
	addrs []IPAddr
	cname string
	err   error
}

type reverseLookupResult struct {
	names []string
	err   error
}

func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) {
	addrs, err, completed := cgoLookupIP(ctx, name)
73 74
	for _, addr := range addrs {
		hosts = append(hosts, addr.String())
75 76 77 78
	}
	return
}

79
func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) {
80
	var hints syscall.Addrinfo
81 82
	switch network {
	case "": // no hints
83 84 85 86 87 88 89
	case "tcp", "tcp4", "tcp6":
		hints.Ai_socktype = syscall.SOCK_STREAM
		hints.Ai_protocol = syscall.IPPROTO_TCP
	case "udp", "udp4", "udp6":
		hints.Ai_socktype = syscall.SOCK_DGRAM
		hints.Ai_protocol = syscall.IPPROTO_UDP
	default:
90
		return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
91
	}
92 93
	if len(network) >= 4 {
		switch network[3] {
94 95 96 97 98 99
		case '4':
			hints.Ai_family = syscall.AF_INET
		case '6':
			hints.Ai_family = syscall.AF_INET6
		}
	}
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
	if ctx.Done() == nil {
		port, err := cgoLookupServicePort(&hints, network, service)
		return port, err, true
	}
	result := make(chan portLookupResult, 1)
	go cgoPortLookup(result, &hints, network, service)
	select {
	case r := <-result:
		return r.port, r.err, true
	case <-ctx.Done():
		// Since there isn't a portable way to cancel the lookup,
		// we just let it finish and write to the buffered channel.
		return 0, mapErr(ctx.Err()), false
	}
}
115

116
func cgoLookupServicePort(hints *syscall.Addrinfo, network, service string) (port int, err error) {
117 118 119 120 121 122 123 124 125
	s, err := syscall.BytePtrFromString(service)
	if err != nil {
		return 0, err
	}
	// Lowercase the service name in the memory passed to C.
	for i := 0; i < len(service); i++ {
		bp := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(s)) + uintptr(i)))
		*bp = lowerASCII(*bp)
	}
126
	var res *syscall.Addrinfo
127
	syscall.Entersyscall()
128
	gerrno := libc_getaddrinfo(nil, s, hints, &res)
129
	syscall.Exitsyscall()
130 131 132 133 134 135
	if gerrno != 0 {
		switch gerrno {
		case syscall.EAI_SYSTEM:
			errno := syscall.GetErrno()
			if errno == 0 { // see golang.org/issue/6232
				errno = syscall.EMFILE
136
			}
137 138 139
			err = errno
		default:
			err = addrinfoErrno(gerrno)
140
		}
141
		return 0, &DNSError{Err: err.Error(), Name: network + "/" + service}
142
	}
143 144 145 146 147 148 149
	defer libc_freeaddrinfo(res)

	for r := res; r != nil; r = r.Ai_next {
		switch r.Ai_family {
		case syscall.AF_INET:
			sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
			p := (*[2]byte)(unsafe.Pointer(&sa.Port))
150
			return int(p[0])<<8 | int(p[1]), nil
151 152 153
		case syscall.AF_INET6:
			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
			p := (*[2]byte)(unsafe.Pointer(&sa.Port))
154
			return int(p[0])<<8 | int(p[1]), nil
155 156
		}
	}
157
	return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}
158 159
}

160 161 162 163 164 165
func cgoPortLookup(result chan<- portLookupResult, hints *syscall.Addrinfo, network, service string) {
	port, err := cgoLookupServicePort(hints, network, service)
	result <- portLookupResult{port, err}
}

func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error) {
166 167 168
	acquireThread()
	defer releaseThread()

169
	var hints syscall.Addrinfo
170
	hints.Ai_flags = int32(cgoAddrInfoFlags)
171
	hints.Ai_socktype = syscall.SOCK_STREAM
172 173

	h := syscall.StringBytePtr(name)
174
	var res *syscall.Addrinfo
175
	syscall.Entersyscall()
176
	gerrno := libc_getaddrinfo(h, nil, &hints, &res)
177
	syscall.Exitsyscall()
178
	if gerrno != 0 {
179 180
		switch gerrno {
		case syscall.EAI_SYSTEM:
181 182 183 184 185 186 187 188 189 190 191
			errno := syscall.GetErrno()
			if errno == 0 {
				// err should not be nil, but sometimes getaddrinfo returns
				// gerrno == C.EAI_SYSTEM with err == nil on Linux.
				// The report claims that it happens when we have too many
				// open files, so use syscall.EMFILE (too many open files in system).
				// Most system calls would return ENFILE (too many open files),
				// so at the least EMFILE should be easy to recognize if this
				// comes up again. golang.org/issue/6232.
				errno = syscall.EMFILE
			}
192 193 194 195 196
			err = errno
		case syscall.EAI_NONAME:
			err = errNoSuchHost
		default:
			err = addrinfoErrno(gerrno)
197
		}
198
		return nil, "", &DNSError{Err: err.Error(), Name: name}
199 200
	}
	defer libc_freeaddrinfo(res)
201

202
	if res != nil {
203
		cname = bytePtrToString((*byte)(unsafe.Pointer(res.Ai_canonname)))
204 205 206 207 208 209 210 211
		if cname == "" {
			cname = name
		}
		if len(cname) > 0 && cname[len(cname)-1] != '.' {
			cname += "."
		}
	}
	for r := res; r != nil; r = r.Ai_next {
212
		// We only asked for SOCK_STREAM, but check anyhow.
213 214 215 216 217 218
		if r.Ai_socktype != syscall.SOCK_STREAM {
			continue
		}
		switch r.Ai_family {
		case syscall.AF_INET:
			sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
219 220
			addr := IPAddr{IP: copyIP(sa.Addr[:])}
			addrs = append(addrs, addr)
221 222
		case syscall.AF_INET6:
			sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
223 224
			addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneToString(int(sa.Scope_id))}
			addrs = append(addrs, addr)
225 226
		}
	}
227
	return addrs, cname, nil
228 229
}

230 231 232
func cgoIPLookup(result chan<- ipLookupResult, name string) {
	addrs, cname, err := cgoLookupIPCNAME(name)
	result <- ipLookupResult{addrs, cname, err}
233 234
}

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) {
	if ctx.Done() == nil {
		addrs, _, err = cgoLookupIPCNAME(name)
		return addrs, err, true
	}
	result := make(chan ipLookupResult, 1)
	go cgoIPLookup(result, name)
	select {
	case r := <-result:
		return r.addrs, r.err, true
	case <-ctx.Done():
		return nil, mapErr(ctx.Err()), false
	}
}

func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) {
	if ctx.Done() == nil {
		_, cname, err = cgoLookupIPCNAME(name)
		return cname, err, true
	}
	result := make(chan ipLookupResult, 1)
	go cgoIPLookup(result, name)
	select {
	case r := <-result:
		return r.cname, r.err, true
	case <-ctx.Done():
		return "", mapErr(ctx.Err()), false
	}
263 264
}

265 266 267 268 269 270 271 272 273 274 275 276 277
// These are roughly enough for the following:
//
// Source		Encoding			Maximum length of single name entry
// Unicast DNS		ASCII or			<=253 + a NUL terminator
//			Unicode in RFC 5892		252 * total number of labels + delimiters + a NUL terminator
// Multicast DNS	UTF-8 in RFC 5198 or		<=253 + a NUL terminator
//			the same as unicast DNS ASCII	<=253 + a NUL terminator
// Local database	various				depends on implementation
const (
	nameinfoLen    = 64
	maxNameinfoLen = 4096
)

278
func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, completed bool) {
279 280 281 282 283
	var zone string
	ip := parseIPv4(addr)
	if ip == nil {
		ip, zone = parseIPv6(addr, true)
	}
284 285 286
	if ip == nil {
		return nil, &DNSError{Err: "invalid address", Name: addr}, true
	}
287
	sa, salen := cgoSockaddr(ip, zone)
288 289 290
	if sa == nil {
		return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true
	}
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
	if ctx.Done() == nil {
		names, err := cgoLookupAddrPTR(addr, sa, salen)
		return names, err, true
	}
	result := make(chan reverseLookupResult, 1)
	go cgoReverseLookup(result, addr, sa, salen)
	select {
	case r := <-result:
		return r.names, r.err, true
	case <-ctx.Done():
		return nil, mapErr(ctx.Err()), false
	}
}

func cgoLookupAddrPTR(addr string, sa *syscall.RawSockaddr, salen syscall.Socklen_t) (names []string, err error) {
	acquireThread()
	defer releaseThread()

309
	var gerrno int
310
	var b []byte
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
		b = make([]byte, l)
		gerrno, err = cgoNameinfoPTR(b, sa, salen)
		if gerrno == 0 || gerrno != syscall.EAI_OVERFLOW {
			break
		}
	}
	if gerrno != 0 {
		switch gerrno {
		case syscall.EAI_SYSTEM:
			if err == nil { // see golang.org/issue/6232
				err = syscall.EMFILE
			}
		default:
			err = addrinfoErrno(gerrno)
		}
327
		return nil, &DNSError{Err: err.Error(), Name: addr}
328 329 330 331 332 333 334
	}
	for i := 0; i < len(b); i++ {
		if b[i] == 0 {
			b = b[:i]
			break
		}
	}
335 336 337 338 339 340
	return []string{absDomainName(b)}, nil
}

func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *syscall.RawSockaddr, salen syscall.Socklen_t) {
	names, err := cgoLookupAddrPTR(addr, sa, salen)
	result <- reverseLookupResult{names, err}
341 342
}

343
func cgoSockaddr(ip IP, zone string) (*syscall.RawSockaddr, syscall.Socklen_t) {
344 345 346 347
	if ip4 := ip.To4(); ip4 != nil {
		return cgoSockaddrInet4(ip4), syscall.Socklen_t(syscall.SizeofSockaddrInet4)
	}
	if ip6 := ip.To16(); ip6 != nil {
348
		return cgoSockaddrInet6(ip6, zoneToInt(zone)), syscall.Socklen_t(syscall.SizeofSockaddrInet6)
349 350 351 352
	}
	return nil, 0
}

353
func copyIP(x IP) IP {
354 355 356
	if len(x) < 16 {
		return x.To16()
	}
357 358 359 360
	y := make(IP, len(x))
	copy(y, x)
	return y
}