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