Commit 99f5f796 authored by Mikio Hara's avatar Mikio Hara

net: allow LookupAddr to use getnameinfo when cgo is enabled

This change allows LookupAddr to use getnameinfo through cgo for working
together with various name services other than DNS.

Fixes #7855.

Change-Id: I5b3b4aefe3d1b904541c3350865734d8cbb1c1c4
Reviewed-on: https://go-review.googlesource.com/3420Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent deb6c5b9
// Copyright 2015 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 linux solaris
package net
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
*/
import "C"
import "unsafe"
func cgoNameinfoPTR(b []byte, sa *C.struct_sockaddr, salen C.socklen_t) (int, error) {
gerrno, err := C.getnameinfo(sa, salen, (*C.char)(unsafe.Pointer(&b[0])), C.socklen_t(len(b)), nil, 0, C.NI_NAMEREQD)
return int(gerrno), err
}
// Copyright 2015 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 freebsd dragonfly netbsd openbsd
package net
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
*/
import "C"
import "unsafe"
func cgoNameinfoPTR(b []byte, sa *C.struct_sockaddr, salen C.socklen_t) (int, error) {
gerrno, err := C.getnameinfo(sa, salen, (*C.char)(unsafe.Pointer(&b[0])), C.size_t(len(b)), nil, 0, C.NI_NAMEREQD)
return int(gerrno), err
}
// Copyright 2015 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 android linux solaris
package net
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
*/
import "C"
import (
"syscall"
"unsafe"
)
func cgoSockaddrInet4(ip IP) *C.struct_sockaddr {
sa := syscall.RawSockaddrInet4{Family: syscall.AF_INET}
copy(sa.Addr[:], ip)
return (*C.struct_sockaddr)(unsafe.Pointer(&sa))
}
func cgoSockaddrInet6(ip IP) *C.struct_sockaddr {
sa := syscall.RawSockaddrInet6{Family: syscall.AF_INET6}
copy(sa.Addr[:], ip)
return (*C.struct_sockaddr)(unsafe.Pointer(&sa))
}
// Copyright 2015 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 netbsd openbsd
package net
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
*/
import "C"
import (
"syscall"
"unsafe"
)
func cgoSockaddrInet4(ip IP) *C.struct_sockaddr {
sa := syscall.RawSockaddrInet4{Len: syscall.SizeofSockaddrInet4, Family: syscall.AF_INET}
copy(sa.Addr[:], ip)
return (*C.struct_sockaddr)(unsafe.Pointer(&sa))
}
func cgoSockaddrInet6(ip IP) *C.struct_sockaddr {
sa := syscall.RawSockaddrInet6{Len: syscall.SizeofSockaddrInet6, Family: syscall.AF_INET6}
copy(sa.Addr[:], ip)
return (*C.struct_sockaddr)(unsafe.Pointer(&sa))
}
......@@ -27,3 +27,7 @@ func cgoLookupIP(name string) (addrs []IPAddr, err error, completed bool) {
func cgoLookupCNAME(name string) (cname string, err error, completed bool) {
return "", nil, false
}
func cgoLookupPTR(addr string) (ptrs []string, err error, completed bool) {
return nil, nil, false
}
......@@ -169,6 +169,72 @@ func cgoLookupCNAME(name string) (cname string, err error, completed bool) {
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{string(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()
......
......@@ -479,3 +479,28 @@ func goLookupCNAME(name string) (cname string, err error) {
cname = rr[0].(*dnsRR_CNAME).Cname
return
}
// goLookupPTR is the native Go implementation of LookupAddr.
// Used only if cgoLookupPTR refuses to handle the request (that is,
// only if cgoLookupPTR is the stub in cgo_stub.go).
// Normally we let cgo use the C library resolver instead of depending
// on our lookup code, so that Go and C get the same answers.
func goLookupPTR(addr string) ([]string, error) {
names := lookupStaticAddr(addr)
if len(names) > 0 {
return names, nil
}
arpa, err := reverseaddr(addr)
if err != nil {
return nil, err
}
_, rrs, err := lookup(arpa, dnsTypePTR)
if err != nil {
return nil, err
}
ptrs := make([]string, len(rrs))
for i, rr := range rrs {
ptrs[i] = rr.(*dnsRR_PTR).Ptr
}
return ptrs, nil
}
......@@ -174,7 +174,7 @@ func TestLookupGmailTXT(t *testing.T) {
}
}
var lookupGooglePublicDNSAddrs = []struct {
var lookupGooglePublicDNSAddrTests = []struct {
addr, name string
}{
{"8.8.8.8", ".google.com"},
......@@ -191,7 +191,7 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
t.Skip("both IPv4 and IPv6 are required")
}
for _, tt := range lookupGooglePublicDNSAddrs {
for _, tt := range lookupGooglePublicDNSAddrTests {
names, err := LookupAddr(tt.addr)
if err != nil {
t.Fatal(err)
......
......@@ -148,21 +148,9 @@ func lookupTXT(name string) ([]string, error) {
}
func lookupAddr(addr string) ([]string, error) {
names := lookupStaticAddr(addr)
if len(names) > 0 {
return names, nil
}
arpa, err := reverseaddr(addr)
if err != nil {
return nil, err
}
_, rrs, err := lookup(arpa, dnsTypePTR)
if err != nil {
return nil, err
}
ptrs := make([]string, len(rrs))
for i, rr := range rrs {
ptrs[i] = rr.(*dnsRR_PTR).Ptr
ptrs, err, ok := cgoLookupPTR(addr)
if !ok {
ptrs, err = goLookupPTR(addr)
}
return ptrs, nil
return ptrs, err
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment