1
0
mirror of https://github.com/golang/go synced 2024-10-05 16:51:21 -06:00
go/src/net/cgo_unix.go
Mikio Hara 4d6a69f227 net: force LookupAddr results to be rooted DNS paths even in the case of local source
The builtin name resolver using various resolution techniques is a bit
complicated and we sometimes fotget to take care of all the go and cgo
code paths and exchanging information to local and remote sources. This
change makes LookupAddr return absolute domain names even in the case of
local source.

Updates #12189.
Fixes #12240.

Change-Id: Icdd3375bcddc7f5d4d3b24f134d93815073736fc
Reviewed-on: https://go-review.googlesource.com/17216
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2015-11-28 08:43:55 +00:00

246 lines
6.4 KiB
Go

// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build cgo,!netgo
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
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 "C"
import (
"syscall"
"unsafe"
)
// 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 C.GoString(C.gai_strerror(C.int(eai))) }
func (eai addrinfoErrno) Temporary() bool { return eai == C.EAI_AGAIN }
func (eai addrinfoErrno) Timeout() bool { return false }
func cgoLookupHost(name string) (hosts []string, err error, completed bool) {
addrs, err, completed := cgoLookupIP(name)
for _, addr := range addrs {
hosts = append(hosts, addr.String())
}
return
}
func cgoLookupPort(network, service string) (port int, err error, completed bool) {
acquireThread()
defer releaseThread()
var hints C.struct_addrinfo
switch network {
case "": // no hints
case "tcp", "tcp4", "tcp6":
hints.ai_socktype = C.SOCK_STREAM
hints.ai_protocol = C.IPPROTO_TCP
case "udp", "udp4", "udp6":
hints.ai_socktype = C.SOCK_DGRAM
hints.ai_protocol = C.IPPROTO_UDP
default:
return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
}
if len(network) >= 4 {
switch network[3] {
case '4':
hints.ai_family = C.AF_INET
case '6':
hints.ai_family = C.AF_INET6
}
}
s := C.CString(service)
var res *C.struct_addrinfo
defer C.free(unsafe.Pointer(s))
gerrno, err := C.getaddrinfo(nil, s, &hints, &res)
if gerrno != 0 {
switch gerrno {
case C.EAI_SYSTEM:
if err == nil { // see golang.org/issue/6232
err = syscall.EMFILE
}
default:
err = addrinfoErrno(gerrno)
}
return 0, &DNSError{Err: err.Error(), Name: network + "/" + service}, true
}
defer C.freeaddrinfo(res)
for r := res; r != nil; r = r.ai_next {
switch r.ai_family {
case C.AF_INET:
sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
p := (*[2]byte)(unsafe.Pointer(&sa.Port))
return int(p[0])<<8 | int(p[1]), nil, true
case C.AF_INET6:
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
p := (*[2]byte)(unsafe.Pointer(&sa.Port))
return int(p[0])<<8 | int(p[1]), nil, true
}
}
return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}, true
}
func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, completed bool) {
acquireThread()
defer releaseThread()
var hints C.struct_addrinfo
hints.ai_flags = cgoAddrInfoFlags
hints.ai_socktype = C.SOCK_STREAM
h := C.CString(name)
defer C.free(unsafe.Pointer(h))
var res *C.struct_addrinfo
gerrno, err := C.getaddrinfo(h, nil, &hints, &res)
if gerrno != 0 {
switch gerrno {
case C.EAI_SYSTEM:
if err == nil {
// 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.
err = syscall.EMFILE
}
case C.EAI_NONAME:
err = errNoSuchHost
default:
err = addrinfoErrno(gerrno)
}
return nil, "", &DNSError{Err: err.Error(), Name: name}, true
}
defer C.freeaddrinfo(res)
if res != nil {
cname = C.GoString(res.ai_canonname)
if cname == "" {
cname = name
}
if len(cname) > 0 && cname[len(cname)-1] != '.' {
cname += "."
}
}
for r := res; r != nil; r = r.ai_next {
// We only asked for SOCK_STREAM, but check anyhow.
if r.ai_socktype != C.SOCK_STREAM {
continue
}
switch r.ai_family {
case C.AF_INET:
sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
addr := IPAddr{IP: copyIP(sa.Addr[:])}
addrs = append(addrs, addr)
case C.AF_INET6:
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneToString(int(sa.Scope_id))}
addrs = append(addrs, addr)
}
}
return addrs, cname, nil, true
}
func cgoLookupIP(name string) (addrs []IPAddr, err error, completed bool) {
addrs, _, err, completed = cgoLookupIPCNAME(name)
return
}
func cgoLookupCNAME(name string) (cname string, err error, completed bool) {
_, cname, err, completed = cgoLookupIPCNAME(name)
return
}
// 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
)
func cgoLookupPTR(addr string) ([]string, error, bool) {
acquireThread()
defer releaseThread()
ip := ParseIP(addr)
if ip == nil {
return nil, &DNSError{Err: "invalid address", Name: addr}, true
}
sa, salen := cgoSockaddr(ip)
if sa == nil {
return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true
}
var err error
var b []byte
var gerrno int
for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
b = make([]byte, l)
gerrno, err = cgoNameinfoPTR(b, sa, salen)
if gerrno == 0 || gerrno != C.EAI_OVERFLOW {
break
}
}
if gerrno != 0 {
switch gerrno {
case C.EAI_SYSTEM:
if err == nil { // see golang.org/issue/6232
err = syscall.EMFILE
}
default:
err = addrinfoErrno(gerrno)
}
return nil, &DNSError{Err: err.Error(), Name: addr}, true
}
for i := 0; i < len(b); i++ {
if b[i] == 0 {
b = b[:i]
break
}
}
return []string{absDomainName(b)}, nil, true
}
func cgoSockaddr(ip IP) (*C.struct_sockaddr, C.socklen_t) {
if ip4 := ip.To4(); ip4 != nil {
return cgoSockaddrInet4(ip4), C.socklen_t(syscall.SizeofSockaddrInet4)
}
if ip6 := ip.To16(); ip6 != nil {
return cgoSockaddrInet6(ip6), C.socklen_t(syscall.SizeofSockaddrInet6)
}
return nil, 0
}
func copyIP(x IP) IP {
if len(x) < 16 {
return x.To16()
}
y := make(IP, len(x))
copy(y, x)
return y
}