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 <iant@golang.org>
diff --git a/src/net/cgo_android.go b/src/net/cgo_android.go
index 3819ce5..fe9925b 100644
--- a/src/net/cgo_android.go
+++ b/src/net/cgo_android.go
@@ -9,6 +9,4 @@
//#include <netdb.h>
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 388eab4..c5ec9dd 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 @@
*/
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 4ef2d0c..9a5f898 100644
--- a/src/net/cgo_linux.go
+++ b/src/net/cgo_linux.go
@@ -11,12 +11,10 @@
*/
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 09c5ad2..1830913 100644
--- a/src/net/cgo_netbsd.go
+++ b/src/net/cgo_netbsd.go
@@ -11,6 +11,4 @@
*/
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 09c5ad2..1830913 100644
--- a/src/net/cgo_openbsd.go
+++ b/src/net/cgo_openbsd.go
@@ -11,6 +11,4 @@
*/
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 d2d40da..c4937ef 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 "<nil>" }
+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 eba5777..38c3d70 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 @@
"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 @@
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 @@
}
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
+ }
+ 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", net + "/" + service}, 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 @@
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 @@
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 0000000..8968b75
--- /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 "<nil>" }
+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 642790e..8448eb1 100644
--- a/src/net/error_test.go
+++ b/src/net/error_test.go
@@ -84,7 +84,7 @@
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