[zx][fdio][net] Convert net FDIO to use FIDL

This change also cleans up a bunch of things.

US-462

Change-Id: If9cc75912bcba92905e8f7162886a34b39d09347
diff --git a/src/net/fd_fuchsia.go b/src/net/fd_fuchsia.go
index f2c9a87..606daa0 100644
--- a/src/net/fd_fuchsia.go
+++ b/src/net/fd_fuchsia.go
@@ -9,29 +9,16 @@
 	"os"
 	"runtime"
 	"syscall"
-	"syscall/zx"
-	"syscall/zx/fdio"
 	"syscall/zx/mxnet"
 	"syscall/zx/zxsocket"
 	"time"
 )
 
-func mxStatus(err error) zx.Status {
-	if err == nil {
-		return zx.ErrOk
-	}
-	if status, ok := err.(zx.Error); ok {
-		return status.Status
-	}
-	return zx.ErrInternal
-}
-
 // Network file descriptor.
 type netFD struct {
 	// locking/lifetime of sysfd + serialize access to Read and Write methods
 	fdmu fdMutex
-	hsrc *os.File
-	m    fdio.FDIO
+	sock *zxsocket.Socket
 
 	// immutable until Close
 	net         string
@@ -55,7 +42,7 @@
 }
 
 func (fd *netFD) Read(b []byte) (n int, err error) {
-	return fd.m.Read(b)
+	return fd.sock.Read(b)
 }
 
 func (fd *netFD) Write(b []byte) (n int, err error) {
@@ -64,7 +51,7 @@
 	// that behavior here:
 	l, t := len(b), 0
 	for {
-		n, err := fd.m.Write(b)
+		n, err := fd.sock.Write(b)
 		t += n
 		if t == l {
 			return t, err
@@ -80,13 +67,13 @@
 }
 
 func (fd *netFD) recvMsg(b []byte) (n, flags int, addr string, port uint16, err error) {
-	data, flags, addr, port, err := fd.m.(*zxsocket.Socket).RecvMsg(len(b))
+	data, flags, addr, port, err := fd.sock.RecvMsg(len(b))
 	n = copy(b, data)
 	return n, flags, addr, port, err
 }
 
 func (fd *netFD) sendMsg(b []byte, addr string, port uint16) (n int, err error) {
-	return fd.m.(*zxsocket.Socket).SendMsg(b, addr, port)
+	return fd.sock.SendMsg(b, addr, port)
 }
 
 func (fd *netFD) closeRead() error {
@@ -98,10 +85,12 @@
 }
 
 func (fd *netFD) Close() error {
-	return fd.hsrc.Close()
+	return fd.sock.Close()
 }
 
 func (fd *netFD) dup() (*os.File, error) {
+	// TODO(mknyszek): fd.sock is an FDIO so one can make an FD from it to
+	// implement this.
 	return nil, errors.New("net: dup not implemented on fuchsia")
 }
 
@@ -123,13 +112,12 @@
 	}
 	defer fd.readUnlock()
 
-	m, err := zxsocket.Accept(fd.m)
+	sock, err := fd.sock.Accept()
 	if err != nil {
 		return nil, err
 	}
 	netfd = &netFD{
-		hsrc:   os.NewFile(uintptr(syscall.OpenFDIO(m)), "socket"),
-		m:      m,
+		sock:   sock,
 		family: fd.family,
 		sotype: fd.sotype,
 		net:    fd.net,
@@ -156,7 +144,7 @@
 }
 
 func (fd *netFD) setAddr() {
-	fd.laddr = fd.asAddr(zxsocket.GetSockName(fd.m))
-	fd.raddr = fd.asAddr(zxsocket.GetPeerName(fd.m))
+	fd.laddr = fd.asAddr(fd.sock.SockName())
+	fd.raddr = fd.asAddr(fd.sock.PeerName())
 	runtime.SetFinalizer(fd, (*netFD).Close)
 }
diff --git a/src/net/ipsock_fuchsia.go b/src/net/ipsock_fuchsia.go
index 59d6110..a3fc167 100644
--- a/src/net/ipsock_fuchsia.go
+++ b/src/net/ipsock_fuchsia.go
@@ -7,7 +7,6 @@
 import (
 	"context"
 	"errors"
-	"os"
 	"sync"
 	"syscall"
 	"syscall/zx"
@@ -93,16 +92,16 @@
 	}
 	proto := syscall.IPPROTO_IP
 
-	var m fdio.FDIO
+	var sock *zxsocket.Socket
 	// Wait for the network stack to publish the socket device.
 	// See similar logic in zircon/system/ulib/fdio/bsdsocket.c.
 	for i := 0; i < 40; i++ {
 		c := zx.Channel(getSocketProvider())
 		if c.Handle().IsValid() {
 			sp := zxnet.LegacySocketProviderInterface(fidl.Proxy{Channel: c})
-			sock, _, err := sp.OpenSocket(zxnet.SocketDomain(family), zxnet.SocketType(sotype), zxnet.SocketProtocol(proto))
+			s, _, err := sp.OpenSocket(zxnet.SocketDomain(family), zxnet.SocketType(sotype), zxnet.SocketProtocol(proto))
 			if err == nil {
-				m, _ = zxsocket.NewSocket(*sock.Handle())
+				sock = zxsocket.NewSocket(s)
 				break
 			}
 		}
@@ -112,12 +111,11 @@
 		return nil, err
 	}
 	if sotype == syscall.SOCK_DGRAM {
-		zxsocket.SetDgram(m)
+		sock.SetDgram()
 	}
 
 	fd = &netFD{
-		hsrc:   os.NewFile(uintptr(syscall.OpenFDIO(m)), "socket"),
-		m:      m,
+		sock:   sock,
 		family: family,
 		sotype: sotype,
 		net:    net,
@@ -128,14 +126,14 @@
 			return nil, err
 		}
 		if addr != "" || port != 0 {
-			if err := zxsocket.Bind(fd.m, addr, port); err != nil {
+			if err := fd.sock.Bind(addr, port); err != nil {
 				return nil, err
 			}
 		}
 		if raddr == nil {
 			switch sotype {
 			case syscall.SOCK_STREAM:
-				if err := zxsocket.ListenStream(m, listenerBacklog); err != nil {
+				if err := fd.sock.ListenStream(listenerBacklog); err != nil {
 					return nil, err
 				}
 			case syscall.SOCK_DGRAM:
@@ -148,7 +146,7 @@
 		if err != nil {
 			return nil, err
 		}
-		if err := zxsocket.Connect(fd.m, addr, port); err != nil {
+		if err := fd.sock.Connect(addr, port); err != nil {
 			return nil, err
 		}
 		fd.isConnected = true
diff --git a/src/syscall/zx/zxsocket/client.go b/src/syscall/zx/zxsocket/client.go
index 0a97fa6..ce575ee 100644
--- a/src/syscall/zx/zxsocket/client.go
+++ b/src/syscall/zx/zxsocket/client.go
@@ -7,11 +7,54 @@
 package zxsocket
 
 import (
+	"errors"
 	"syscall/zx"
-	"syscall/zx/fdio"
+	"syscall/zx/mxerror"
+	"syscall/zx/mxnet"
 	"unsafe"
 )
 
+
+// Bind is BSD Sockets bind on a *Socket.
+func (s *Socket) Bind(addr mxnet.Addr, port uint16) error {
+	b := make([]byte, mxnet.SockaddrLen)
+	addrlen, err := mxnet.EncodeSockaddr(b, addr, port)
+	if err != nil {
+		return err
+	}
+	b = b[:addrlen]
+	_, err = s.Misc(OpBind, 0, b, nil, nil)
+	return err
+}
+
+// Connect is BSD Sockets connect on a *Socket.
+func (s *Socket) Connect(addr mxnet.Addr, port uint16) error {
+	b := make([]byte, mxnet.SockaddrLen)
+	addrlen, err := mxnet.EncodeSockaddr(b, addr, port)
+	if err != nil {
+		return err
+	}
+	b = b[:addrlen]
+
+	_, err = s.Misc(OpConnect, 0, b, nil, nil)
+	if err != nil && mxerror.Status(err) == zx.ErrShouldWait {
+		obs, err := s.Wait(mxnet.MXSIO_SIGNAL_OUTGOING, zx.TimensecInfinite)
+		switch mxerror.Status(err) {
+		case zx.ErrOk:
+			switch {
+			case obs&mxnet.MXSIO_SIGNAL_CONNECTED != 0:
+				return nil
+			default:
+				// TODO: Use opSetSockOpt to get the reason of the error.
+				return errors.New("zxsocket.Connect: OpConnect was not successful")
+			}
+		default:
+			return err
+		}
+	}
+	return err
+}
+
 // txn completes the client-side part of the RIO transaction.
 func (s *Socket) txn(msg *Msg) (uint32, error) {
 	if !msg.IsValid() {
@@ -40,78 +83,68 @@
 	return uint32(status), nil
 }
 
-func (s *Socket) Close() error {
-	var msg Msg
-	msg.SetOp(OpClose)
+func (s *Socket) ListenStream(backlog int) error {
+	b := make([]byte, 4)
+	b[0] = byte(backlog)
+	b[1] = byte(backlog >> 8)
+	b[2] = byte(backlog >> 16)
+	b[3] = byte(backlog >> 24)
+	_, err := s.Misc(OpListen, 0, b, nil, nil)
+	return err
+}
 
-	_, err := s.txn(&msg)
+func (s *Socket) Accept() (*Socket, error) {
+	if s.flags&SocketFlagsDidListen == 0 {
+		return nil, errors.New("zxsocket.Accept: listen was not called")
+	}
 
-	s.socket.Close()
-	s.socket = 0
+	var h zx.Handle
+	for {
+		err := s.socket.Accept(&h)
+		if err == nil {
+			break
+		}
+		if mxerror.Status(err) != zx.ErrShouldWait {
+			return nil, err
+		}
+		s.socket.Handle().SignalPeer(0, zx.SignalUser7)
+		// TODO: non-blocking support, pass this to the poller
+		const ZX_SOCKET_ACCEPT = zx.SignalSocketAccept
+		const ZX_SOCKET_PEER_CLOSED = zx.SignalSocketPeerClosed
+		pending, err := s.Wait(ZX_SOCKET_ACCEPT|ZX_SOCKET_PEER_CLOSED, zx.TimensecInfinite)
+		if err != nil {
+			return nil, err
+		}
+		if pending&ZX_SOCKET_ACCEPT != 0 {
+			continue
+		}
+		if pending&ZX_SOCKET_PEER_CLOSED != 0 {
+			return nil, zx.Error{Status: zx.ErrPeerClosed, Text: "zxsocket"}
+		}
+	}
+	return NewSocket(zx.Socket(h)), nil
+}
 
+func (s *Socket) getName(op uint32, debug string) (addr mxnet.Addr, port uint16, err error) {
+	out := make([]byte, mxnet.SockaddrReplyLen)
+	_, err = s.Misc(OpGetSockname, 0, nil, out, nil)
 	if err != nil {
-		return err
+		return "", 0, err
 	}
-	return nil
-}
-
-func closeAll(handles []zx.Handle) {
-	for i := range handles {
-		handles[i].Close()
-	}
-}
-
-// Consumes handles, wrapping them in fdio object or closing them on error.
-func createFromHandles(handle zx.Handle, info fdio.ObjectInfo) (fdio.FDIO, error) {
-	handles := append([]zx.Handle{handle}, info.GetHandles()...)
-	switch info.Tag {
-	case fdio.ProtocolSocket:
-		handles[0].Close()
-		return NewSocket(handles[1])
-	default:
-		println("Unknown fdio protocol, ", info.Tag)
-		// In the error case, the server may have given us handles -- close them
-		closeAll(handles)
-		return nil, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket"}
-	}
-}
-
-func OpenHandle(rio zx.Channel, path string, flags uint32, mode uint32) (fdio.FDIO, error) {
-	c, ro, err := fdio.GetObject(rio, OpOpen, path, flags, mode)
+	addr, port, err = mxnet.DecodeSockaddr(out)
 	if err != nil {
-		return nil, err
+		return "", 0, err
 	}
-	return createFromHandles(zx.Handle(c), ro.Info)
+
+	return addr, port, nil
 }
 
-// TODO(smklein): Remove; this function is a subset of "Ioctl".
-func (s *Socket) IoctlSetHandle(op uint32, in zx.Handle) error {
-	return zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket.IoctlSetHandle"}
+func (s *Socket) SockName() (addr mxnet.Addr, port uint16, err error) {
+	return s.getName(OpGetSockname, "GetSockName")
 }
 
-func (s *Socket) Ioctl(op uint32, in, out []byte) ([]zx.Handle, error) {
-	var msg Msg
-	if len(in) > fdio.IoctlMaxInput {
-		return nil, zx.Error{Status: zx.ErrInvalidArgs, Text: "zxsocket.Ioctl"}
-	}
-
-	if fdio.IoctlKind(op) != fdio.IoctlKindDefault {
-		return nil, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Ioctl"}
-	}
-
-	msg.SetOp(OpIoctl)
-	msg.Datalen = uint32(len(in))
-	msg.Arg = int32(len(out))
-	msg.SetIoctlOp(op)
-	copy(msg.Data[:], in)
-
-	_, err := s.txn(&msg)
-	if err != nil {
-		return nil, err
-	}
-
-	copy(out, msg.Data[:msg.Datalen])
-	return nil, nil
+func (s *Socket) PeerName() (addr mxnet.Addr, port uint16, err error) {
+	return s.getName(OpGetPeerName, "GetPeerName")
 }
 
 func (s *Socket) Misc(op uint32, off int64, in, out []byte, handles []zx.Handle) (n int, err error) {
diff --git a/src/syscall/zx/zxsocket/message.go b/src/syscall/zx/zxsocket/message.go
index 9bf0c89..b3a89a9 100644
--- a/src/syscall/zx/zxsocket/message.go
+++ b/src/syscall/zx/zxsocket/message.go
@@ -10,7 +10,6 @@
 	"unsafe"
 
 	"syscall/zx"
-	"syscall/zx/fdio"
 )
 
 const (
@@ -21,7 +20,24 @@
 	MessageSizeMax = uint32(unsafe.Sizeof(Msg{}))
 
 	// MessageHeaderSize is the size of zxsocket message without 'data'
-	MessageHeaderSize = fdio.MessageHeaderSize
+	MessageHeaderSize = uint32(unsafe.Sizeof(MsgHeader{}))
+
+	// MessageMaxHandles is the maximum number of handles a message may carry.
+	// In reality, zx.Sockets can't hold handles, but this is used to support
+	// the deprecated RemoteIO protocol over a socket.
+	MessageMaxHandles = 3
+)
+
+// Custom errors for zxsocket RemoteIO.
+var (
+	// If returned from dispatcher callback, indicates that the dispatcher should immediately
+	// disconnect with no additional callbacks.
+	ErrDisconnectNoCallback = zx.Error{Status: 1, Text: "fdio.Dispatcher"}
+	// If returned from dispatcher callback, indicates that there are no more messages to read.
+	ErrNoWork = zx.Error{Status: -9999, Text: "fdio.Dispatcher.NoWork"}
+	// If returned from dispatcher callback, indicates that the message was handed to another
+	// server.
+	ErrIndirect = zx.Error{Status: -9998, Text: "fdio.Dispatcher.Indirect"}
 )
 
 const (
@@ -65,14 +81,93 @@
 	OpNumOps = uint32(iota) // The total number of operations
 )
 
+type Protocol uint32
+
+// All available FDIO protocols
+const (
+	// The SERVICE protocol allows access to a generic interface.
+	ProtocolService = Protocol(iota)
+	// The FILE protocol allows access to a regular file.
+	ProtocolFile
+	// The DIRECTORY protocol allows access to a directory, which may
+	// contain additional nodes.
+	ProtocolDirectory
+	// The PIPE protocol uses message ports as simple, no-flow-control io pipes.
+	ProtocolPipe
+	// The VMOFILE protocol accesses a file as a VMO.
+	ProtocolVMOFile
+	// The DEVICE protocol allows access to a generic device.
+	ProtocolDevice
+	// The SOCKET protocol provides socket access.
+	ProtocolSocket
+	ProtocolSocketConnected
+)
+
 const (
 	OpenFlagDescribe = 0x00800000
 )
 
+// FIDL2 message header
+type FIDLHeader struct {
+	Txid      uint32
+	reserved0 uint32
+	flags     uint32
+	op        uint32
+}
+
+type MsgHeader struct {
+	FIDLHeader
+	Datalen   uint32
+	Arg       int32
+	arg2      int64
+	reserved1 int32
+	Hcount    uint32
+	Handle    [4]zx.Handle // 3 handles + reply pipe, unused here though.
+}
+
+func (m *MsgHeader) Op() uint32 {
+	return m.op & 0x3FFF
+}
+func (m *MsgHeader) SetOp(v uint32) {
+	m.op = (v & 0x3FFF)
+}
+func (m *MsgHeader) OpHandleCount() uint32 {
+	return (m.op >> 8) & 3
+}
+func (m *MsgHeader) Off() int64 {
+	return m.arg2
+}
+func (m *MsgHeader) SetOff(v int64) {
+	m.arg2 = v
+}
+func (m *MsgHeader) Mode() uint32 {
+	return uint32(m.arg2)
+}
+func (m *MsgHeader) SetMode(v uint32) {
+	m.arg2 = int64(v)
+}
+func (m *MsgHeader) Protocol() Protocol {
+	return Protocol(m.arg2)
+}
+func (m *MsgHeader) SetProtocol(p Protocol) {
+	m.arg2 = int64(p)
+}
+func (m *MsgHeader) IoctlOp() uint32 {
+	return uint32(m.arg2)
+}
+func (m *MsgHeader) SetIoctlOp(v uint32) {
+	m.arg2 = int64(v)
+}
+func (m *MsgHeader) FcntlFlags() uint32 {
+	return uint32(m.arg2)
+}
+func (m *MsgHeader) SetFcntlFlags(v uint32) {
+	m.arg2 = int64(v)
+}
+
 // Msg is the message sent in the zxsocket protocol.
-// Its format must mirror fdio.Msg, though its Data field is a different size.
 type Msg struct {
-	fdio.MsgHeader
+	MsgHeader
 	Data [MessageChunkSize]uint8
 }
 
diff --git a/src/syscall/zx/zxsocket/server.go b/src/syscall/zx/zxsocket/server.go
index 0299e8f..211babf 100644
--- a/src/syscall/zx/zxsocket/server.go
+++ b/src/syscall/zx/zxsocket/server.go
@@ -8,7 +8,6 @@
 
 import (
 	"syscall/zx"
-	"syscall/zx/fdio"
 )
 
 // ServerHandler is the type of the callback which contains the remoteio "server". It is called from
@@ -25,19 +24,19 @@
 	serverCb := cb.(ServerHandler)
 	if h == 0 {
 		// The remote side has been closed. Send our final callback and leave.
-		msg.SetOp(fdio.OpClose)
+		msg.SetOp(OpClose)
 		msg.Arg = 0
 		msg.Datalen = 0
 		msg.Hcount = 0
 		serverCb(&msg, 0, cookie)
 		return nil
 	}
-	msg.Hcount = fdio.MessageMaxHandles
+	msg.Hcount = MessageMaxHandles
 	numBytesInt, _, err := msg.ReadMsg(h)
 	numBytes := uint32(numBytesInt)
 	if err != nil {
 		if mxerr, hasStatus := err.(zx.Error); hasStatus && mxerr.Status == zx.ErrBadState {
-			return fdio.ErrNoWork
+			return ErrNoWork
 		}
 		return err
 	}
@@ -45,10 +44,10 @@
 		return zx.Error{Status: zx.ErrInvalidArgs, Text: "zxsocket.Handler"}
 	}
 
-	isClose := (msg.Op() == fdio.OpClose)
+	isClose := (msg.Op() == OpClose)
 	status := serverCb(&msg, h, cookie)
 	msg.Arg = int32(status)
-	if zx.Status(msg.Arg) == fdio.ErrIndirect.Status {
+	if zx.Status(msg.Arg) == ErrIndirect.Status {
 		// The callback is handling the reply itself
 		return nil
 	} else if status < 0 || !msg.IsValid() {
@@ -64,7 +63,7 @@
 	_, err = msg.WriteMsg(h)
 	if isClose {
 		// Signals that a close callback is not necessary -- the op has already completed
-		return fdio.ErrDisconnectNoCallback
+		return ErrDisconnectNoCallback
 	}
 	return err
 }
diff --git a/src/syscall/zx/zxsocket/socket.go b/src/syscall/zx/zxsocket/socket.go
index 01eb97b..4546800 100644
--- a/src/syscall/zx/zxsocket/socket.go
+++ b/src/syscall/zx/zxsocket/socket.go
@@ -7,15 +7,16 @@
 package zxsocket
 
 import (
-	"errors"
 	"io"
 	"syscall/zx"
+	fidlIo "syscall/zx/io"
 	"syscall/zx/fdio"
+	"syscall/zx/mxerror"
 	"syscall/zx/mxnet"
 	"syscall/zx/zxwait"
 )
 
-// Socket is an FDIO socket.
+// Socket is an fdio.FDIO socket.
 type Socket struct {
 	socket zx.Socket
 
@@ -28,48 +29,86 @@
 	SocketFlagsDidListen = 1 << iota // zxsocket._DID_LISTEN
 )
 
-// NewSocket creates a new zxsocket Socket.
-func NewSocket(handle zx.Handle) (*Socket, error) {
-	s := &Socket{
-		socket: zx.Socket(handle),
-	}
-	return s, nil
+func NewSocket(s zx.Socket) *Socket {
+	return &Socket{socket: s}
 }
 
 func (s *Socket) Wait(signals zx.Signals, timeout zx.Time) (observed zx.Signals, err error) {
 	return zxwait.Wait(zx.Handle(s.socket), signals, timeout)
 }
 
-func ReadControl(s zx.Socket, data []byte) (n int, err error) {
-	// c.f. zxsocket._read_control in zircon/system/ulib/fdio/remoteio.c
-	for {
-		n, err = s.Read(data, zx.SocketControl)
-		switch zxStatus(err) {
-		case zx.ErrOk:
-			return n, nil
-		case zx.ErrPeerClosed:
-			return 0, io.EOF
-		case zx.ErrShouldWait:
-			obs, err := zxwait.Wait(zx.Handle(s), zx.SignalSocketControlReadable|zx.SignalSocketPeerClosed, zx.TimensecInfinite)
-			switch zxStatus(err) {
-			case zx.ErrOk:
-				switch {
-				case obs&zx.SignalSocketControlReadable != 0:
-					continue
-				case obs&zx.SignalSocketPeerClosed != 0:
-					return 0, io.EOF
-				}
-			case zx.ErrBadHandle, zx.ErrCanceled:
-				return 0, io.EOF
-			default:
-				return 0, err
-			}
-		default:
-			return 0, err
-		}
-	}
+// Handles returns the underlying handles for this Socket.
+func (s *Socket) Handles() []zx.Handle {
+	return []zx.Handle{zx.Handle(s.socket)}
 }
 
+// Clone makes a clone of the object.
+func (s *Socket) Clone() (fdio.FDIO, error) {
+	return nil, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket.Clone"}
+}
+
+// Close closes the object.
+func (s *Socket) Close() error {
+	if s.socket == 0 {
+		return nil
+	}
+	var msg Msg
+	msg.SetOp(OpClose)
+
+	_, err := s.txn(&msg)
+
+	s.socket.Close()
+	s.socket = 0
+
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// Sync implements fdio.FDIO for Socket.
+func (s *Socket) Sync() error {
+	return zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// GetAttr implements fdio.FDIO for Socket.
+func (s *Socket) GetAttr() (fidlIo.NodeAttributes, error) {
+	return fidlIo.NodeAttributes{}, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// SetAttr implements fdio.FDIO for Socket.
+func (s *Socket) SetAttr(flags uint32, attr fidlIo.NodeAttributes) error {
+	return zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// Ioctl implements fdio.FDIO for Socket.
+func (s *Socket) Ioctl(op uint32, max uint64, in []byte, _ []zx.Handle) ([]byte, []zx.Handle, error) {
+	var msg Msg
+	if len(in) > fdio.IoctlMaxInput {
+		return nil, nil, zx.Error{Status: zx.ErrInvalidArgs, Text: "zxsocket.Ioctl"}
+	}
+
+	if fdio.IoctlKind(op) != fdio.IoctlKindDefault {
+		return nil, nil, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Ioctl"}
+	}
+
+	out := make([]byte, MessageSizeMax)
+	msg.SetOp(OpIoctl)
+	msg.Datalen = uint32(len(in))
+	msg.Arg = int32(len(out))
+	msg.SetIoctlOp(op)
+	copy(msg.Data[:], in)
+
+	_, err := s.txn(&msg)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	copy(out, msg.Data[:msg.Datalen])
+	return out[:msg.Datalen], nil, nil
+}
+
+// Read implements fdio.FDIO for Socket.
 func (s *Socket) Read(data []byte) (n int, err error) {
 	if s.dgram {
 		b, _, _, _, err := s.RecvMsg(len(data))
@@ -79,14 +118,14 @@
 	// c.f. mxsio_read_stream in zircon/system/ulib/fdio/remoteio.c
 	for {
 		n, err = s.socket.Read(data, 0)
-		switch zxStatus(err) {
+		switch mxerror.Status(err) {
 		case zx.ErrOk:
 			return n, nil
 		case zx.ErrPeerClosed:
 			return 0, io.EOF
 		case zx.ErrShouldWait:
 			obs, err := s.Wait(zx.SignalSocketReadable|zx.SignalSocketPeerClosed, zx.TimensecInfinite)
-			switch zxStatus(err) {
+			switch mxerror.Status(err) {
 			case zx.ErrOk:
 				switch {
 				case obs&zx.SignalSocketReadable != 0:
@@ -105,27 +144,12 @@
 	}
 }
 
+// ReadAt implements fdio.FDIO for Socket.
 func (s *Socket) ReadAt(data []byte, off int64) (int, error) {
-	return 0, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket.ReadAt"}
+	return 0, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
 }
 
-func WriteControl(s zx.Socket, data []byte) (int, error) {
-	// c.f. zxsocket._write_control
-	for {
-		n, err := s.Write(data, zx.SocketControl)
-		switch zxStatus(err) {
-		case zx.ErrOk:
-			return n, nil
-		case zx.ErrShouldWait:
-			_, err := zxwait.Wait(zx.Handle(s), zx.SignalSocketControlWriteable, zx.TimensecInfinite)
-			if err != nil {
-				return n, err
-			}
-			continue
-		}
-	}
-}
-
+// Write implements fdio.FDIO for Socket.
 func (s *Socket) Write(data []byte) (int, error) {
 	if s.dgram {
 		n, err := s.SendMsg(data, "", 0)
@@ -138,7 +162,7 @@
 		n, err := s.socket.Write(data, 0)
 		total += n
 
-		switch zxStatus(err) {
+		switch mxerror.Status(err) {
 		case zx.ErrOk:
 			return total, nil
 		case zx.ErrShouldWait:
@@ -161,8 +185,49 @@
 	}
 }
 
+// WriteAt implements fdio.FDIO for Socket.
 func (s *Socket) WriteAt(data []byte, off int64) (int, error) {
-	return 0, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket.WriteAt"}
+	return 0, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// Seek implements fdio.FDIO for Socket.
+func (s *Socket) Seek(offset int64, whence int) (int64, error) {
+	return 0, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// Truncate implements fdio.FDIO for Socket.
+func (s *Socket) Truncate(length uint64) error {
+	return zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// Open implements fdio.FDIO for Socket.
+func (s *Socket) Open(path string, flags uint32, mode uint32) (fdio.FDIO, error) {
+	return nil, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// Link implements fdio.FDIO for Socket.
+func (s *Socket) Link(oldpath, newpath string) error {
+	return zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// Rename implements fdio.FDIO for Socket.
+func (s *Socket) Rename(oldpath, newpath string) error {
+	return zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// Unlink implements fdio.FDIO for Socket.
+func (s *Socket) Unlink(path string) error {
+	return zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// ReadDirents implements fdio.FDIO for Socket.
+func (s *Socket) ReadDirents(max uint64) ([]byte, error) {
+	return nil, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
+}
+
+// Rewind implements fdio.FDIO for Socket.
+func (s *Socket) Rewind() error {
+	return zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket"}
 }
 
 func (s *Socket) RecvMsg(maxLen int) (b []byte, flags int, addr string, port uint16, err error) {
@@ -183,7 +248,7 @@
 func (s *Socket) recvMsg(data []byte) (int, error) {
 	for {
 		n, err := s.socket.Read(data, 0)
-		switch zxStatus(err) {
+		switch mxerror.Status(err) {
 		case zx.ErrOk:
 			return n, err
 		case zx.ErrShouldWait:
@@ -218,7 +283,7 @@
 
 	for {
 		n, err := s.socket.Write(data, 0)
-		switch zxStatus(err) {
+		switch mxerror.Status(err) {
 		case zx.ErrOk:
 			return len(b), nil
 		case zx.ErrShouldWait:
@@ -246,179 +311,59 @@
 	}
 }
 
-func (s *Socket) Seek(offset int64, whence int) (int64, error) {
-	return 0, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket.Seek"}
-}
-
-func (s *Socket) Open(path string, flags uint32, mode uint32) (fdio.FDIO, error) {
-	return nil, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket.Open"}
-}
-
-func (*Socket) Clone() ([]zx.Handle, error) {
-	return nil, zx.Error{Status: zx.ErrNotSupported, Text: "zxsocket.Socket.Clone"}
-}
-
 // SetDgram marks a *Socket as a datagram (UDP) socket.
 // A different protocol is used internally.
-func SetDgram(m fdio.FDIO) error {
-	if m == nil {
-		return errors.New("zxsocket.SetDgram: nil argument")
-	}
-	s, ok := m.(*Socket)
-	if !ok {
-		return errors.New("zxsocket.SetDgram: argument is not a *Socket")
-	}
+func (s *Socket) SetDgram() {
 	s.dgram = true
-	return nil
 }
 
-// Bind is BSD Sockets bind on a *Socket.
-func Bind(m fdio.FDIO, addr mxnet.Addr, port uint16) error {
-	if m == nil {
-		return errors.New("zxsocket.Bind: nil argument")
-	}
-	b := make([]byte, mxnet.SockaddrLen)
-	addrlen, err := mxnet.EncodeSockaddr(b, addr, port)
-	if err != nil {
-		return err
-	}
-	b = b[:addrlen]
-
-	s, ok := m.(*Socket)
-	if !ok {
-		return errors.New("zxsocket.Connect: argument is not a *Socket")
-	}
-	_, err = s.Misc(OpBind, 0, b, nil, nil)
-	return err
-}
-
-// Connect is BSD Sockets connect on a *Socket.
-func Connect(m fdio.FDIO, addr mxnet.Addr, port uint16) error {
-	if m == nil {
-		return errors.New("zxsocket.Connect: nil argument")
-	}
-	b := make([]byte, mxnet.SockaddrLen)
-	addrlen, err := mxnet.EncodeSockaddr(b, addr, port)
-	if err != nil {
-		return err
-	}
-	b = b[:addrlen]
-
-	s, ok := m.(*Socket)
-	if !ok {
-		return errors.New("zxsocket.Connect: argument is not a *Socket")
-	}
-	_, err = s.Misc(OpConnect, 0, b, nil, nil)
-	if err != nil && zxStatus(err) == zx.ErrShouldWait {
-		obs, err := s.Wait(mxnet.MXSIO_SIGNAL_OUTGOING, zx.TimensecInfinite)
-		switch zxStatus(err) {
+func ReadControl(s zx.Socket, data []byte) (n int, err error) {
+	// c.f. zxsocket._read_control in zircon/system/ulib/fdio/remoteio.c
+	for {
+		n, err = s.Read(data, zx.SocketControl)
+		switch mxerror.Status(err) {
 		case zx.ErrOk:
-			switch {
-			case obs&mxnet.MXSIO_SIGNAL_CONNECTED != 0:
-				return nil
+			return n, nil
+		case zx.ErrPeerClosed:
+			return 0, io.EOF
+		case zx.ErrShouldWait:
+			obs, err := zxwait.Wait(
+				zx.Handle(s),
+				zx.SignalSocketControlReadable|zx.SignalSocketPeerClosed,
+				zx.TimensecInfinite,
+			)
+			switch mxerror.Status(err) {
+			case zx.ErrOk:
+				switch {
+				case obs&zx.SignalSocketControlReadable != 0:
+					continue
+				case obs&zx.SignalSocketPeerClosed != 0:
+					return 0, io.EOF
+				}
+			case zx.ErrBadHandle, zx.ErrCanceled:
+				return 0, io.EOF
 			default:
-				// TODO: Use opSetSockOpt to get the reason of the error.
-				return errors.New("zxsocket.Connect: OpConnect was not successful")
+				return 0, err
 			}
 		default:
-			return err
+			return 0, err
 		}
 	}
-	return err
 }
 
-func ListenStream(m fdio.FDIO, backlog int) error {
-	if m == nil {
-		return errors.New("zxsocket.ListenStream: nil argument")
-	}
-	s, ok := m.(*Socket)
-	if !ok {
-		return errors.New("zxsocket.ListenStream: argument is not a *Socket")
-	}
-	b := make([]byte, 4)
-	b[0] = byte(backlog)
-	b[1] = byte(backlog >> 8)
-	b[2] = byte(backlog >> 16)
-	b[3] = byte(backlog >> 24)
-	_, err := s.Misc(OpListen, 0, b, nil, nil)
-	return err
-}
-
-func Accept(m fdio.FDIO) (newm fdio.FDIO, err error) {
-	if m == nil {
-		return nil, errors.New("zxsocket.Accept: nil argument")
-	}
-	s, ok := m.(*Socket)
-	if !ok {
-		return nil, errors.New("zxsocket.Accept: argument is not a *Socket")
-	}
-	if s.flags&SocketFlagsDidListen == 0 {
-		return nil, errors.New("zxsocket.Accept: listen was not called")
-	}
-
-	var h zx.Handle
+func WriteControl(s zx.Socket, data []byte) (int, error) {
+	// c.f. zxsocket._write_control
 	for {
-		err = s.socket.Accept(&h)
-		if err == nil {
-			break
-		}
-		if zxStatus(err) != zx.ErrShouldWait {
-			return nil, err
-		}
-		s.socket.Handle().SignalPeer(0, zx.SignalUser7)
-		// TODO: non-blocking support, pass this to the poller
-		const ZX_SOCKET_ACCEPT = zx.SignalSocketAccept
-		const ZX_SOCKET_PEER_CLOSED = zx.SignalSocketPeerClosed
-		pending, err := s.Wait(ZX_SOCKET_ACCEPT|ZX_SOCKET_PEER_CLOSED, zx.TimensecInfinite)
-		if err != nil {
-			return nil, err
-		}
-		if pending&ZX_SOCKET_ACCEPT != 0 {
+		n, err := s.Write(data, zx.SocketControl)
+		switch mxerror.Status(err) {
+		case zx.ErrOk:
+			return n, nil
+		case zx.ErrShouldWait:
+			_, err := zxwait.Wait(zx.Handle(s), zx.SignalSocketControlWriteable, zx.TimensecInfinite)
+			if err != nil {
+				return n, err
+			}
 			continue
 		}
-		if pending&ZX_SOCKET_PEER_CLOSED != 0 {
-			return nil, zx.Error{Status: zx.ErrPeerClosed, Text: "zxsocket"}
-		}
 	}
-
-	return NewSocket(h)
-}
-
-func getName(m fdio.FDIO, op uint32, debug string) (addr mxnet.Addr, port uint16, err error) {
-	if m == nil {
-		return "", 0, errors.New("zxsocket." + debug + ": nil argument")
-	}
-	s, ok := m.(*Socket)
-	if !ok {
-		return "", 0, errors.New("zxsocket." + debug + ": argument is not a *Socket")
-	}
-	out := make([]byte, mxnet.SockaddrReplyLen)
-	_, err = s.Misc(OpGetSockname, 0, nil, out, nil)
-	if err != nil {
-		return "", 0, err
-	}
-	addr, port, err = mxnet.DecodeSockaddr(out)
-	if err != nil {
-		return "", 0, err
-	}
-
-	return addr, port, nil
-}
-
-func GetSockName(m fdio.FDIO) (addr mxnet.Addr, port uint16, err error) {
-	return getName(m, OpGetSockname, "GetSockName")
-}
-
-func GetPeerName(m fdio.FDIO) (addr mxnet.Addr, port uint16, err error) {
-	return getName(m, OpGetPeerName, "GetPeerName")
-}
-
-func zxStatus(err error) zx.Status {
-	if err == nil {
-		return zx.ErrOk
-	}
-	if status, ok := err.(zx.Error); ok {
-		return status.Status
-	}
-	return zx.ErrInternal
 }