net: several family detection fixes for fuchsia

This gets us a few more unit tests into the Go net package.
Some of this logic comes from the _posix.go files.

Change-Id: I9e8b8af19d83eb0a9ba547c09d4c48d8608f2fea
diff --git a/src/net/ipsock_fuchsia.go b/src/net/ipsock_fuchsia.go
index ccc9206..c26e7fe 100644
--- a/src/net/ipsock_fuchsia.go
+++ b/src/net/ipsock_fuchsia.go
@@ -19,7 +19,7 @@
 }
 
 func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
-	return true, false
+	return true, true
 }
 
 // A sockaddr represents a TCP, UDP, IP or Unix network endpoint
@@ -27,21 +27,45 @@
 type sockaddr interface {
 	Addr
 
-	sockaddr() (addr string, port uint16)
+	family() int
+	isWildcard() bool
+	sockaddr(family int) (addr mxnet.Addr, port uint16, err error)
+}
+
+func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) {
+	switch net[len(net)-1] {
+	case '4':
+		return syscall.AF_INET, false
+	case '6':
+		return syscall.AF_INET6, true
+	}
+
+	if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
+		if supportsIPv4map || !supportsIPv4 {
+			return syscall.AF_INET6, false
+		}
+		if laddr == nil {
+			return syscall.AF_INET, false
+		}
+		return laddr.family(), false
+	}
+
+	if (laddr == nil || laddr.family() == syscall.AF_INET) &&
+		(raddr == nil || raddr.family() == syscall.AF_INET) {
+		return syscall.AF_INET, false
+	}
+	return syscall.AF_INET6, false
 }
 
 func dialFuchsia(ctx context.Context, net string, laddr, raddr sockaddr) (fd *netFD, err error) {
-	domain := syscall.AF_INET6
-	if net[len(net)-1] == '4' {
-		domain = syscall.AF_INET
-	}
+	family, _ := favoriteAddrFamily(net, laddr, raddr, "dial")
 
 	sotype := syscall.SOCK_STREAM
 	if len(net) >= 3 && net[:3] == "udp" {
 		sotype = syscall.SOCK_DGRAM
 	}
 	proto := syscall.IPPROTO_IP
-	path := "/dev/socket/socket/" + strconv.Itoa(domain) + "/" + strconv.Itoa(sotype) + "/" + strconv.Itoa(proto)
+	path := "/dev/socket/socket/" + strconv.Itoa(family) + "/" + strconv.Itoa(sotype) + "/" + strconv.Itoa(proto)
 	f, err := os.Open(path)
 	if err != nil {
 		return nil, err
@@ -54,15 +78,20 @@
 	fd = &netFD{
 		hsrc:   f,
 		m:      m,
-		family: domain,
+		family: family,
 		sotype: sotype,
 		net:    net,
 	}
 	if laddr != nil {
-		addr, port := laddr.sockaddr()
-		if err := rio.Bind(fd.m, mxnet.Addr(addr), port); err != nil {
+		addr, port, err := laddr.sockaddr(family)
+		if err != nil {
 			return nil, err
 		}
+		if addr != "" || port != 0 {
+			if err := rio.Bind(fd.m, addr, port); err != nil {
+				return nil, err
+			}
+		}
 		if raddr == nil {
 			switch sotype {
 			case syscall.SOCK_STREAM:
@@ -75,8 +104,11 @@
 		}
 	}
 	if raddr != nil {
-		addr, port := raddr.sockaddr()
-		if err := rio.Connect(fd.m, mxnet.Addr(addr), port); err != nil {
+		addr, port, err := raddr.sockaddr(family)
+		if err != nil {
+			return nil, err
+		}
+		if err := rio.Connect(fd.m, addr, port); err != nil {
 			return nil, err
 		}
 		fd.isConnected = true
@@ -85,3 +117,27 @@
 
 	return fd, nil
 }
+
+func ipToSockaddr(family int, ip IP, port int, zone string) (addr mxnet.Addr, portres uint16, err error) {
+	switch family {
+	case syscall.AF_INET:
+		if len(ip) == 0 {
+			ip = IPv4zero
+		}
+		ip4 := ip.To4()
+		if ip4 == nil {
+			return "", 0, &AddrError{Err: "non-IPv4 address", Addr: ip.String()}
+		}
+		return mxnet.Addr(ip4)[:4], uint16(port), nil
+	case syscall.AF_INET6:
+		if len(ip) == 0 || ip.Equal(IPv4zero) {
+			ip = IPv6zero
+		}
+		ip6 := ip.To16()
+		if ip6 == nil {
+			return "", 0, &AddrError{Err: "non-IPv6 address", Addr: ip.String()}
+		}
+		return mxnet.Addr(ip6), uint16(port), nil
+	}
+	return "", 0, &AddrError{Err: "invalid address family", Addr: ip.String()}
+}
diff --git a/src/net/platform_test.go b/src/net/platform_test.go
index 2a14095..cc03c81 100644
--- a/src/net/platform_test.go
+++ b/src/net/platform_test.go
@@ -33,7 +33,7 @@
 		}
 	case "unix", "unixgram":
 		switch runtime.GOOS {
-		case "android", "nacl", "plan9", "windows":
+		case "android", "fuchsia", "nacl", "plan9", "windows":
 			return false
 		}
 		// iOS does not support unix, unixgram.
@@ -42,7 +42,7 @@
 		}
 	case "unixpacket":
 		switch runtime.GOOS {
-		case "android", "darwin", "nacl", "plan9", "windows":
+		case "android", "darwin", "fuchsia", "nacl", "plan9", "windows":
 			fallthrough
 		case "freebsd": // FreeBSD 8 and below don't support unixpacket
 			return false
diff --git a/src/net/tcpsock_fuchsia.go b/src/net/tcpsock_fuchsia.go
index aa4103b..66405d4 100644
--- a/src/net/tcpsock_fuchsia.go
+++ b/src/net/tcpsock_fuchsia.go
@@ -28,11 +28,11 @@
 	return syscall.AF_INET6
 }
 
-func (a *TCPAddr) sockaddr() (addr string, port uint16) {
+func (a *TCPAddr) sockaddr(family int) (addr mxnet.Addr, port uint16, err error) {
 	if a == nil {
-		return "", 0
+		return "", 0, nil
 	}
-	return string(a.IP), uint16(a.Port)
+	return ipToSockaddr(family, a.IP, a.Port, a.Zone)
 }
 
 func (c *TCPConn) readFrom(r io.Reader) (int64, error) {
diff --git a/src/net/udpsock_fuchsia.go b/src/net/udpsock_fuchsia.go
index 5d59198..b11d952 100644
--- a/src/net/udpsock_fuchsia.go
+++ b/src/net/udpsock_fuchsia.go
@@ -26,11 +26,11 @@
 	return syscall.AF_INET6
 }
 
-func (a *UDPAddr) sockaddr() (addr string, port uint16) {
+func (a *UDPAddr) sockaddr(family int) (addr mxnet.Addr, port uint16, err error) {
 	if a == nil {
-		return "", 0
+		return "", 0, nil
 	}
-	return string(a.IP), uint16(a.Port)
+	return ipToSockaddr(family, a.IP, a.Port, a.Zone)
 }
 
 func (c *UDPConn) readFrom(b []byte) (n int, addr *UDPAddr, err error) {
diff --git a/src/syscall/mx/mxio/rio/socket.go b/src/syscall/mx/mxio/rio/socket.go
index 9c192d0..11d334c 100644
--- a/src/syscall/mx/mxio/rio/socket.go
+++ b/src/syscall/mx/mxio/rio/socket.go
@@ -211,10 +211,11 @@
 		return errors.New("rio.Bind: nil argument")
 	}
 	b := make([]byte, mxnet.SockaddrLen)
-	_, err := mxnet.EncodeSockaddr(b, addr, port)
+	addrlen, err := mxnet.EncodeSockaddr(b, addr, port)
 	if err != nil {
 		return err
 	}
+	b = b[:addrlen]
 
 	s, ok := m.(*Socket)
 	if !ok {
@@ -230,10 +231,11 @@
 		return errors.New("rio.Connect: nil argument")
 	}
 	b := make([]byte, mxnet.SockaddrLen)
-	_, err := mxnet.EncodeSockaddr(b, addr, port)
+	addrlen, err := mxnet.EncodeSockaddr(b, addr, port)
 	if err != nil {
 		return err
 	}
+	b = b[:addrlen]
 
 	s, ok := m.(*Socket)
 	if !ok {