From 6d3a7e79a4660b1ecce8cc9d930c3a9f6222a6f2 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Fri, 17 Apr 2015 17:56:58 +0900 Subject: [PATCH] net: clean up cgo This change adds a type addrinfoErrno to represent getaddrinfo, getnameinfo-specific errors, and uses it in cgo-based lookup functions. Also retags cgo files for clarification and does minor cleanup. Change-Id: I6db7130ad7bf35bbd4e8839a97759e1364c43828 Reviewed-on: https://go-review.googlesource.com/9020 Reviewed-by: Ian Lance Taylor --- src/net/cgo_android.go | 4 +- src/net/cgo_bsd.go | 6 +-- src/net/cgo_linux.go | 16 ++++---- src/net/cgo_netbsd.go | 4 +- src/net/cgo_openbsd.go | 4 +- src/net/cgo_stub.go | 8 +++- src/net/cgo_unix.go | 93 ++++++++++++++++++++++++------------------ src/net/cgo_windows.go | 13 ++++++ src/net/error_test.go | 2 +- 9 files changed, 86 insertions(+), 64 deletions(-) create mode 100644 src/net/cgo_windows.go diff --git a/src/net/cgo_android.go b/src/net/cgo_android.go index 3819ce56a4..fe9925b840 100644 --- a/src/net/cgo_android.go +++ b/src/net/cgo_android.go @@ -9,6 +9,4 @@ package net //#include import "C" -func cgoAddrInfoFlags() C.int { - return C.AI_CANONNAME -} +const cgoAddrInfoFlags = C.AI_CANONNAME diff --git a/src/net/cgo_bsd.go b/src/net/cgo_bsd.go index 388eab4fe1..c5ec9dd9c8 100644 --- a/src/net/cgo_bsd.go +++ b/src/net/cgo_bsd.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !netgo +// +build cgo,!netgo // +build darwin dragonfly freebsd package net @@ -12,6 +12,4 @@ package net */ import "C" -func cgoAddrInfoFlags() C.int { - return (C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK -} +const cgoAddrInfoFlags = (C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK diff --git a/src/net/cgo_linux.go b/src/net/cgo_linux.go index 4ef2d0cd12..9a5f898c26 100644 --- a/src/net/cgo_linux.go +++ b/src/net/cgo_linux.go @@ -11,12 +11,10 @@ package net */ import "C" -func cgoAddrInfoFlags() C.int { - // NOTE(rsc): In theory there are approximately balanced - // arguments for and against including AI_ADDRCONFIG - // in the flags (it includes IPv4 results only on IPv4 systems, - // and similarly for IPv6), but in practice setting it causes - // getaddrinfo to return the wrong canonical name on Linux. - // So definitely leave it out. - return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL -} +// NOTE(rsc): In theory there are approximately balanced +// arguments for and against including AI_ADDRCONFIG +// in the flags (it includes IPv4 results only on IPv4 systems, +// and similarly for IPv6), but in practice setting it causes +// getaddrinfo to return the wrong canonical name on Linux. +// So definitely leave it out. +const cgoAddrInfoFlags = C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL diff --git a/src/net/cgo_netbsd.go b/src/net/cgo_netbsd.go index 09c5ad2d9f..183091366c 100644 --- a/src/net/cgo_netbsd.go +++ b/src/net/cgo_netbsd.go @@ -11,6 +11,4 @@ package net */ import "C" -func cgoAddrInfoFlags() C.int { - return C.AI_CANONNAME -} +const cgoAddrInfoFlags = C.AI_CANONNAME diff --git a/src/net/cgo_openbsd.go b/src/net/cgo_openbsd.go index 09c5ad2d9f..183091366c 100644 --- a/src/net/cgo_openbsd.go +++ b/src/net/cgo_openbsd.go @@ -11,6 +11,4 @@ package net */ import "C" -func cgoAddrInfoFlags() C.int { - return C.AI_CANONNAME -} +const cgoAddrInfoFlags = C.AI_CANONNAME diff --git a/src/net/cgo_stub.go b/src/net/cgo_stub.go index d2d40da74f..c4937efde2 100644 --- a/src/net/cgo_stub.go +++ b/src/net/cgo_stub.go @@ -4,10 +4,14 @@ // +build !cgo netgo -// Stub cgo routines for systems that do not use cgo to do network lookups. - package net +type addrinfoErrno int + +func (eai addrinfoErrno) Error() string { return "" } +func (eai addrinfoErrno) Temporary() bool { return false } +func (eai addrinfoErrno) Timeout() bool { return false } + func cgoLookupHost(name string) (addrs []string, err error, completed bool) { return nil, nil, false } diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go index eba5777347..38c3d70d55 100644 --- a/src/net/cgo_unix.go +++ b/src/net/cgo_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !netgo +// +build cgo,!netgo // +build darwin dragonfly freebsd linux netbsd openbsd package net @@ -23,24 +23,30 @@ import ( "unsafe" ) -func cgoLookupHost(name string) (addrs []string, err error, completed bool) { - ip, err, completed := cgoLookupIP(name) - for _, p := range ip { - addrs = append(addrs, p.String()) +// 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(net, service string) (port int, err error, completed bool) { +func cgoLookupPort(network, service string) (port int, err error, completed bool) { acquireThread() defer releaseThread() - var res *C.struct_addrinfo var hints C.struct_addrinfo - - switch net { - case "": - // no hints + switch network { + case "": // no hints case "tcp", "tcp4", "tcp6": hints.ai_socktype = C.SOCK_STREAM hints.ai_protocol = C.IPPROTO_TCP @@ -48,10 +54,10 @@ func cgoLookupPort(net, service string) (port int, err error, completed bool) { hints.ai_socktype = C.SOCK_DGRAM hints.ai_protocol = C.IPPROTO_UDP default: - return 0, UnknownNetworkError(net), true + return 0, UnknownNetworkError(network), true } - if len(net) >= 4 { - switch net[3] { + if len(network) >= 4 { + switch network[3] { case '4': hints.ai_family = C.AF_INET case '6': @@ -60,45 +66,53 @@ func cgoLookupPort(net, service string) (port int, err error, completed bool) { } s := C.CString(service) + var res *C.struct_addrinfo defer C.free(unsafe.Pointer(s)) - if C.getaddrinfo(nil, s, &hints, &res) == 0 { - defer C.freeaddrinfo(res) - for r := res; r != nil; r = r.ai_next { - switch r.ai_family { - default: - continue - 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 + 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, err, true } - return 0, &AddrError{"unknown port", net + "/" + 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, &AddrError{"unknown port", network + "/" + service}, true } func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, completed bool) { acquireThread() defer releaseThread() - var res *C.struct_addrinfo var hints C.struct_addrinfo - - hints.ai_flags = cgoAddrInfoFlags() + 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 { var str string - if gerrno == C.EAI_NONAME { - str = noSuchHost - } else if gerrno == C.EAI_SYSTEM { + 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. @@ -110,12 +124,15 @@ func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, com err = syscall.EMFILE } str = err.Error() - } else { - str = C.GoString(C.gai_strerror(gerrno)) + case C.EAI_NONAME: + str = noSuchHost + default: + str = addrinfoErrno(gerrno).Error() } return nil, "", &DNSError{Err: str, Name: name}, true } defer C.freeaddrinfo(res) + if res != nil { cname = C.GoString(res.ai_canonname) if cname == "" { @@ -131,8 +148,6 @@ func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, com continue } switch r.ai_family { - default: - continue case C.AF_INET: sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr)) addr := IPAddr{IP: copyIP(sa.Addr[:])} diff --git a/src/net/cgo_windows.go b/src/net/cgo_windows.go new file mode 100644 index 0000000000..8968b757a9 --- /dev/null +++ b/src/net/cgo_windows.go @@ -0,0 +1,13 @@ +// 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 + +package net + +type addrinfoErrno int + +func (eai addrinfoErrno) Error() string { return "" } +func (eai addrinfoErrno) Temporary() bool { return false } +func (eai addrinfoErrno) Timeout() bool { return false } diff --git a/src/net/error_test.go b/src/net/error_test.go index 642790e68b..8448eb1c39 100644 --- a/src/net/error_test.go +++ b/src/net/error_test.go @@ -84,7 +84,7 @@ second: return nil } switch err := nestedErr.(type) { - case *AddrError, *DNSError, InvalidAddrError, *ParseError, UnknownNetworkError, *timeoutError: + case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *timeoutError, UnknownNetworkError: return nil case *DNSConfigError: nestedErr = err.Err -- 2.48.1