blob: 95811ce68f7825b418a4662c71409956804e92ec [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"encoding/binary"
"fmt"
"log"
"strings"
"sync"
"syscall/zx"
"syscall/zx/fdio"
"syscall/zx/mxerror"
"syscall/zx/mxruntime"
"syscall/zx/zxsocket"
"time"
"app/context"
"github.com/google/netstack/dns"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/buffer"
"github.com/google/netstack/tcpip/header"
"github.com/google/netstack/tcpip/network/ipv4"
"github.com/google/netstack/tcpip/network/ipv6"
"github.com/google/netstack/tcpip/stack"
"github.com/google/netstack/tcpip/transport/tcp"
"github.com/google/netstack/tcpip/transport/udp"
"github.com/google/netstack/waiter"
)
const debug = true
const debug2 = false
const ZX_SOCKET_HALF_CLOSE = 1
const ZXSIO_SIGNAL_INCOMING = zx.SignalUser0
const ZXSIO_SIGNAL_OUTGOING = zx.SignalUser1
const ZXSIO_SIGNAL_CONNECTED = zx.SignalUser3
const LOCAL_SIGNAL_CLOSING = zx.SignalUser5
const defaultNIC = 2
// TODO: define these in syscall/zx/mxruntime
const (
handleServicesRequest mxruntime.HandleType = 0x3B
)
var (
ioctlNetcGetIfInfo = fdio.IoctlNum(fdio.IoctlKindDefault, fdio.IoctlFamilyNetconfig, 0)
ioctlNetcGetNumIfs = fdio.IoctlNum(fdio.IoctlKindDefault, fdio.IoctlFamilyNetconfig, 1)
ioctlNetcGetIfInfoAt = fdio.IoctlNum(fdio.IoctlKindDefault, fdio.IoctlFamilyNetconfig, 2)
ioctlNetcGetNodename = fdio.IoctlNum(fdio.IoctlKindDefault, fdio.IoctlFamilyNetconfig, 8)
)
type app struct {
socket socketServer
}
func (a *app) Bind(h zx.Handle) {
if err := a.socket.dispatcher.AddHandler(h, fdio.ServerHandler(a.socket.fdioHandler), 0); err != nil {
h.Close()
}
if err := h.SignalPeer(0, zx.SignalUser0); err != nil {
h.Close()
}
}
func (a *app) Name() string {
return "net.Netstack"
}
func socketDispatcher(stk *stack.Stack, ctx *context.Context) (*socketServer, error) {
d, err := fdio.NewDispatcher(fdio.Handler)
if err != nil {
return nil, err
}
a := &app{
socket: socketServer{
dispatcher: d,
stack: stk,
dnsClient: dns.NewClient(stk, 0),
io: make(map[cookie]*iostate),
next: 1,
},
}
ctx.OutgoingService.AddService(a)
go d.Serve()
return &a.socket, nil
}
func (s *socketServer) setNetstack(ns *netstack) {
s.ns = ns
}
type cookie int64
type iostate struct {
wq *waiter.Queue
ep tcpip.Endpoint
netProto tcpip.NetworkProtocolNumber // IPv4 or IPv6
transProto tcpip.TransportProtocolNumber // TCP or UDP
dataHandle zx.Handle // a zx.Socket, used to communicate with libc
peerDataHandle zx.Handle // other end of dataHandle
mu sync.Mutex
refs int
lastError *tcpip.Error // if not-nil, next error returned via getsockopt
writeLoopDone chan struct{}
controlLoopDone chan struct{}
listenLoopDone chan struct{}
withNewSocket bool // remove when we remove old FDIO support
}
func (ios *iostate) acquire() {
ios.mu.Lock()
ios.refs++
ios.mu.Unlock()
}
func (ios *iostate) release(f func()) {
ios.mu.Lock()
ios.refs--
isLast := ios.refs == 0
ios.mu.Unlock()
if isLast {
f()
}
}
// loopSocketWrite connects libc write to the network stack for TCP sockets.
//
// TODO: replace WaitOne with a method that parks goroutines when waiting
// for a signal on a zx.Socket.
//
// As written, we have two netstack threads per socket.
// That's not so bad for small client work, but even a client OS is
// eventually going to feel the overhead of this.
func (ios *iostate) loopSocketWrite(stk *stack.Stack) {
defer func() { ios.writeLoopDone <- struct{}{} }()
dataHandle := zx.Socket(ios.dataHandle)
// Warm up.
_, err := dataHandle.WaitOne(
zx.SignalSocketReadable|zx.SignalSocketReadDisabled|
zx.SignalSocketPeerClosed|LOCAL_SIGNAL_CLOSING,
zx.TimensecInfinite)
switch mxerror.Status(err) {
case zx.ErrOk:
// NOP
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("loopSocketWrite: warmup failed: %v", err)
}
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
for {
// TODO: obviously allocating for each read is silly.
// A quick hack we can do is store these in a ring buffer,
// as the lifecycle of this buffer.View starts here, and
// ends in nearby code we control in link.go.
v := buffer.NewView(2048)
n, err := dataHandle.Read([]byte(v), 0)
switch mxerror.Status(err) {
case zx.ErrOk:
// Success. Pass the data to the endpoint and loop.
case zx.ErrBadState:
// This side of the socket is closed.
err := ios.ep.Shutdown(tcpip.ShutdownWrite)
if err != nil {
log.Printf("loopSocketWrite: ShutdownWrite failed: %v", err)
}
return
case zx.ErrShouldWait:
obs, err := dataHandle.WaitOne(
zx.SignalSocketReadable|zx.SignalSocketReadDisabled|
zx.SignalSocketPeerClosed|LOCAL_SIGNAL_CLOSING,
zx.TimensecInfinite)
switch mxerror.Status(err) {
case zx.ErrOk:
// Handle signal below.
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("loopSocketWrite: wait failed: %v", err)
return
}
switch {
case obs&zx.SignalSocketReadDisabled != 0:
// The next Read will return zx.BadState.
continue
case obs&zx.SignalSocketReadable != 0:
continue
case obs&LOCAL_SIGNAL_CLOSING != 0:
return
case obs&zx.SignalSocketPeerClosed != 0:
return
}
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("socket read failed: %v", err) // TODO: communicate this
continue
}
if debug2 {
log.Printf("loopSocketWrite: sending packet n=%d, v=%q", n, v[:n])
}
ios.wq.EventRegister(&waitEntry, waiter.EventOut)
for {
_, err := ios.ep.Write(v[:n], nil)
if err == tcpip.ErrWouldBlock {
<-notifyCh
continue
}
break
}
ios.wq.EventUnregister(&waitEntry)
if err != nil {
log.Printf("loopSocketWrite: got endpoint error: %v (TODO)", err)
return
}
}
}
// loopSocketRead connects libc read to the network stack for TCP sockets.
func (ios *iostate) loopSocketRead(stk *stack.Stack) {
dataHandle := zx.Socket(ios.dataHandle)
// Warm up.
obs, err := dataHandle.WaitOne(
zx.SignalSocketWritable|zx.SignalSocketWriteDisabled|
zx.SignalSocketPeerClosed,
zx.TimensecInfinite)
switch mxerror.Status(err) {
case zx.ErrOk:
// NOP
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("loopSocketRead: warmup failed: %v", err)
}
switch {
case obs&zx.SignalSocketPeerClosed != 0:
return
case obs&zx.SignalSocketWriteDisabled != 0:
err := ios.ep.Shutdown(tcpip.ShutdownRead)
if err != nil {
log.Printf("loopSocketRead: ShutdownRead failed: %v", err)
}
return
}
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
for {
ios.wq.EventRegister(&waitEntry, waiter.EventIn)
var v buffer.View
var err *tcpip.Error
for {
v, err = ios.ep.Read(nil)
if err == nil {
break
} else if err == tcpip.ErrWouldBlock || err == tcpip.ErrInvalidEndpointState || err == tcpip.ErrNotConnected {
if debug2 {
log.Printf("loopSocketRead read err=%v", err)
}
<-notifyCh
// TODO: get socket closed message from loopSocketWrite
continue
} else if err == tcpip.ErrClosedForReceive || err == tcpip.ErrConnectionRefused {
if err == tcpip.ErrConnectionRefused {
ios.lastError = err
}
_, err := dataHandle.Write(nil, ZX_SOCKET_HALF_CLOSE)
switch mxerror.Status(err) {
case zx.ErrOk:
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
default:
log.Printf("socket read: send ZX_SOCKET_HALF_CLOSE failed: %v", err)
}
return
}
log.Printf("loopSocketRead got endpoint error: %v (TODO)", err)
return
}
ios.wq.EventUnregister(&waitEntry)
if debug2 {
log.Printf("loopSocketRead: got a buffer, len(v)=%d", len(v))
}
writeLoop:
for len(v) > 0 {
n, err := dataHandle.Write([]byte(v), 0)
v = v[n:]
switch mxerror.Status(err) {
case zx.ErrOk:
// Success. Loop and keep writing.
case zx.ErrBadState:
// This side of the socket is closed.
err := ios.ep.Shutdown(tcpip.ShutdownRead)
if err != nil {
log.Printf("loopSocketRead: ShutdownRead failed: %v", err)
}
return
case zx.ErrShouldWait:
if debug2 {
log.Printf("loopSocketRead: got zx.ErrShouldWait")
}
obs, err := dataHandle.WaitOne(
zx.SignalSocketWritable|zx.SignalSocketWriteDisabled|
zx.SignalSocketPeerClosed,
zx.TimensecInfinite)
switch mxerror.Status(err) {
case zx.ErrOk:
// Handle signal below.
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("loopSocketRead: wait failed: %v", err)
return
}
switch {
case obs&zx.SignalSocketPeerClosed != 0:
return
case obs&zx.SignalSocketWriteDisabled != 0:
// The next Write will return zx.ErrBadState.
continue
case obs&zx.SignalSocketWritable != 0:
continue
}
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("socket write failed: %v", err) // TODO: communicate this
break writeLoop
}
}
}
}
// loopDgramRead connects libc read to the network stack for UDP messages.
func (ios *iostate) loopDgramRead(stk *stack.Stack) {
dataHandle := zx.Socket(ios.dataHandle)
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
for {
ios.wq.EventRegister(&waitEntry, waiter.EventIn)
var sender tcpip.FullAddress
var v buffer.View
var err *tcpip.Error
for {
v, err = ios.ep.Read(&sender)
if err == nil {
break
} else if err == tcpip.ErrWouldBlock {
<-notifyCh
continue
} else if err == tcpip.ErrClosedForReceive {
if debug2 {
log.Printf("TODO loopDgramRead closed")
}
// TODO _, err := ios.dataHandle.Write(nil, ZX_SOCKET_HALF_CLOSE)
return
}
// TODO communicate to user
log.Printf("loopDgramRead got endpoint error: %v (TODO)", err)
return
}
ios.wq.EventUnregister(&waitEntry)
out := make([]byte, c_fdio_socket_msg_hdr_len+len(v))
writeSocketMsgHdr(out, sender)
copy(out[c_fdio_socket_msg_hdr_len:], v)
writeLoop:
for {
_, err := dataHandle.Write(out, 0)
switch mxerror.Status(err) {
case zx.ErrOk:
break writeLoop
case zx.ErrBadState:
return // This side of the socket is closed.
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("socket write failed: %v", err) // TODO: communicate this
break writeLoop
}
}
}
}
// loopDgramWrite connects libc write to the network stack for UDP messages.
func (ios *iostate) loopDgramWrite(stk *stack.Stack) {
defer func() { ios.writeLoopDone <- struct{}{} }()
dataHandle := zx.Socket(ios.dataHandle)
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
for {
v := buffer.NewView(2048)
n, err := dataHandle.Read([]byte(v), 0)
switch mxerror.Status(err) {
case zx.ErrOk:
// Success. Pass the data to the endpoint and loop.
case zx.ErrBadState:
return // This side of the socket is closed.
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
case zx.ErrShouldWait:
obs, err := dataHandle.WaitOne(zx.SignalSocketReadable|zx.SignalSocketPeerClosed|LOCAL_SIGNAL_CLOSING, zx.TimensecInfinite)
switch mxerror.Status(err) {
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
case zx.ErrOk:
switch {
case obs&zx.SignalChannelReadable != 0:
continue
case obs&LOCAL_SIGNAL_CLOSING != 0:
return
case obs&zx.SignalSocketPeerClosed != 0:
return
}
default:
log.Printf("loopDgramWrite wait failed: %v", err)
return
}
default:
log.Printf("loopDgramWrite failed: %v", err) // TODO: communicate this
continue
}
v = v[:n:n]
receiver, err := readSocketMsgHdr(v)
if err != nil {
// TODO communicate
log.Printf("loopDgramWrite: bad socket msg header: %v", err)
continue
}
ios.wq.EventRegister(&waitEntry, waiter.EventOut)
for {
_, err := ios.ep.Write(v[c_fdio_socket_msg_hdr_len:], receiver)
if err == tcpip.ErrWouldBlock {
<-notifyCh
continue
}
break
}
ios.wq.EventUnregister(&waitEntry)
if err != nil {
log.Printf("loopDgramWrite: got endpoint error: %v (TODO)", err)
return
}
}
}
func (ios *iostate) loopControl(s *socketServer, cookie int64) {
defer func() { ios.controlLoopDone <- struct{}{} }()
dataHandle := zx.Socket(ios.dataHandle)
for {
err := zxsocket.Handler(dataHandle, zxsocket.ServerHandler(s.zxsocketHandler), cookie)
switch mxerror.Status(err) {
case zx.ErrOk:
// Success. Pass the data to the endpoint and loop.
case zx.ErrBadState:
return // This side of the socket is closed.
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
case zx.ErrShouldWait:
obs, err := dataHandle.WaitOne(zx.SignalSocketControlReadable|zx.SignalSocketPeerClosed|LOCAL_SIGNAL_CLOSING, zx.TimensecInfinite)
switch mxerror.Status(err) {
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
case zx.ErrOk:
switch {
case obs&zx.SignalSocketControlReadable != 0:
continue
case obs&LOCAL_SIGNAL_CLOSING != 0:
return
case obs&zx.SignalSocketPeerClosed != 0:
return
}
default:
log.Printf("loopControl wait failed: %v", err)
return
}
default:
// ErrDisconnectNoCallback is given when we receive a close operation.
if err != fdio.ErrDisconnectNoCallback {
log.Printf("loopControl failed: %v", err) // TODO: communicate this
}
continue
}
}
}
func (s *socketServer) newIostate(h zx.Handle, iosOrig *iostate, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, wq *waiter.Queue, ep tcpip.Endpoint, withNewSocket bool, isAccept bool) (reterr error) {
var peerS zx.Handle
var ios *iostate
if iosOrig == nil {
ios = &iostate{
netProto: netProto,
transProto: transProto,
wq: wq,
ep: ep,
refs: 1,
writeLoopDone: make(chan struct{}),
controlLoopDone: make(chan struct{}),
withNewSocket: withNewSocket,
}
if ep != nil || withNewSocket {
switch transProto {
case tcp.ProtocolNumber, udp.ProtocolNumber, ipv4.PingProtocolNumber:
var t uint32
if transProto == tcp.ProtocolNumber {
t = zx.SocketStream
} else {
t = zx.SocketDatagram
}
if withNewSocket {
t |= zx.SocketHasControl
if !isAccept {
t |= zx.SocketHasAccept
}
}
s0, s1, err := zx.NewSocket(t)
if err != nil {
return err
}
// TODO: Why cast these to Handle? Why not store as Socket?
ios.dataHandle = zx.Handle(s0)
ios.peerDataHandle = zx.Handle(s1)
peerS, err = ios.peerDataHandle.Duplicate(zx.RightSameRights)
if err != nil {
ios.dataHandle.Close()
ios.peerDataHandle.Close()
return err
}
default:
panic(fmt.Sprintf("unknown transport protocol number: %v", transProto))
}
}
} else {
ios = iosOrig
switch transProto {
case tcp.ProtocolNumber:
var err error
peerS, err = ios.peerDataHandle.Duplicate(zx.RightSameRights)
if err != nil {
return err
}
case udp.ProtocolNumber:
return mxerror.Errorf(zx.ErrNotSupported, "cannot clone an udp socket")
default:
panic(fmt.Sprintf("unknown transport protocol number: %v", transProto))
}
}
s.mu.Lock()
newCookie := s.next
s.next++
s.io[newCookie] = ios
s.mu.Unlock()
defer func() {
if reterr != nil {
ios.dataHandle.Close()
if ios.peerDataHandle != 0 {
ios.peerDataHandle.Close()
}
peerS.Close()
s.mu.Lock()
delete(s.io, newCookie)
s.mu.Unlock()
}
}()
if withNewSocket && isAccept {
if err := zx.Socket(h).Share(peerS); err != nil {
return err
}
} else {
// Before we add a dispatcher for this iostate, respond to the client describing what
// kind of object this is.
ro := fdio.RioDescription{
Status: errStatus(nil),
}
ro.Info.Tag = fdio.ProtocolSocket
ro.SetOp(fdio.OpOnOpen)
ro.Info.Socket().Handle = peerS
ro.Write(h, 0)
}
if withNewSocket {
go ios.loopControl(s, int64(newCookie))
} else if err := s.dispatcher.AddHandler(h, fdio.ServerHandler(s.fdioHandler), int64(newCookie)); err != nil {
h.Close()
return err
}
switch transProto {
case tcp.ProtocolNumber:
if ep != nil {
go ios.loopSocketRead(s.stack)
go ios.loopSocketWrite(s.stack)
}
case udp.ProtocolNumber, ipv4.PingProtocolNumber:
go ios.loopDgramRead(s.stack)
go ios.loopDgramWrite(s.stack)
}
return nil
}
type socketServer struct {
dispatcher *fdio.Dispatcher
stack *stack.Stack
dnsClient *dns.Client
ns *netstack
mu sync.Mutex
next cookie
io map[cookie]*iostate
}
func (s *socketServer) opSocket(h zx.Handle, ios *iostate, msg *fdio.Msg, path string) (err error) {
var domain, typ, protocol int
withNewSocket := false
if n, _ := fmt.Sscanf(path, "socket-v2/%d/%d/%d\x00", &domain, &typ, &protocol); n == 3 {
withNewSocket = true
} else if n, _ := fmt.Sscanf(path, "socket/%d/%d/%d\x00", &domain, &typ, &protocol); n != 3 {
return mxerror.Errorf(zx.ErrInvalidArgs, "socket: bad path %q (n=%d)", path, n)
}
var n tcpip.NetworkProtocolNumber
switch domain {
case AF_INET:
n = ipv4.ProtocolNumber
case AF_INET6:
n = ipv6.ProtocolNumber
default:
return mxerror.Errorf(zx.ErrNotSupported, "socket: unknown network protocol: %d", domain)
}
transProto, err := sockProto(typ, protocol)
if err != nil {
return err
}
wq := new(waiter.Queue)
ep, e := s.stack.NewEndpoint(transProto, n, wq)
if e != nil {
if debug {
log.Printf("socket: new endpoint: %v", e)
}
return mxerror.Errorf(zx.ErrInternal, "socket: new endpoint: %v", err)
}
if n == ipv6.ProtocolNumber {
if err := ep.SetSockOpt(tcpip.V6OnlyOption(0)); err != nil {
log.Printf("socket: setsockopt v6only option failed: %v", err)
}
}
err = s.newIostate(h, nil, n, transProto, wq, ep, withNewSocket, false)
if err != nil {
if debug {
log.Printf("socket: new iostate: %v", err)
}
return err
}
return nil
}
func sockProto(typ, protocol int) (t tcpip.TransportProtocolNumber, err error) {
switch typ {
case SOCK_STREAM:
switch protocol {
case IPPROTO_IP, IPPROTO_TCP:
return tcp.ProtocolNumber, nil
default:
return 0, mxerror.Errorf(zx.ErrNotSupported, "unsupported SOCK_STREAM protocol: %d", protocol)
}
case SOCK_DGRAM:
switch protocol {
case IPPROTO_IP, IPPROTO_UDP:
return udp.ProtocolNumber, nil
case IPPROTO_ICMP:
return ipv4.PingProtocolNumber, nil
default:
return 0, mxerror.Errorf(zx.ErrNotSupported, "unsupported SOCK_DGRAM protocol: %d", protocol)
}
}
return 0, mxerror.Errorf(zx.ErrNotSupported, "unsupported protocol: %d/%d", typ, protocol)
}
var errShouldWait = zx.Error{Status: zx.ErrShouldWait, Text: "netstack"}
func (s *socketServer) opAccept(h zx.Handle, ios *iostate, msg *fdio.Msg, path string) (err error) {
if ios.ep == nil {
return mxerror.Errorf(zx.ErrBadState, "accept: no socket")
}
newep, newwq, e := ios.ep.Accept()
if e == tcpip.ErrWouldBlock {
return errShouldWait
}
if ios.ep.Readiness(waiter.EventIn) == 0 {
// If we just accepted the only queued incoming connection,
// clear the signal so the fdio client knows no incoming
// connection is available.
err := zx.Handle(ios.dataHandle).SignalPeer(ZXSIO_SIGNAL_INCOMING, 0)
switch mxerror.Status(err) {
case zx.ErrOk:
// NOP
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
// Ignore the closure of the origin endpoint here,
// as we have accepted a new endpoint and it can be
// valid.
default:
log.Printf("accept: clearing ZXSIO_SIGNAL_INCOMING: %v", err)
}
}
if e != nil {
if debug {
log.Printf("accept: %v", err)
}
return mxerror.Errorf(zx.ErrInternal, "accept: %v", err)
}
err = s.newIostate(h, nil, ios.netProto, ios.transProto, newwq, newep, false, true)
return err
}
func errStatus(err error) zx.Status {
if err == nil {
return zx.ErrOk
}
if s, ok := err.(zx.Error); ok {
return s.Status
}
log.Printf("%v", err)
return zx.ErrInternal
}
func mxNetError(e *tcpip.Error) zx.Status {
switch e {
case tcpip.ErrUnknownProtocol:
return zx.ErrProtocolNotSupported
case tcpip.ErrDuplicateAddress, tcpip.ErrPortInUse:
return zx.ErrAddressInUse
case tcpip.ErrNoRoute:
return zx.ErrAddressUnreachable
case tcpip.ErrAlreadyBound:
// Note that tcpip.ErrAlreadyBound and zx.ErrAlreadyBound correspond to different
// errors. tcpip.ErrAlreadyBound is returned when attempting to bind socket when
// it's already bound. zx.ErrAlreadyBound is used to indicate that the local
// address is already used by someone else.
return zx.ErrInvalidArgs
case tcpip.ErrInvalidEndpointState, tcpip.ErrAlreadyConnecting, tcpip.ErrAlreadyConnected:
return zx.ErrBadState
case tcpip.ErrNoPortAvailable:
return zx.ErrNoResources
case tcpip.ErrUnknownProtocolOption, tcpip.ErrBadLocalAddress, tcpip.ErrDestinationRequired:
return zx.ErrInvalidArgs
case tcpip.ErrClosedForSend, tcpip.ErrClosedForReceive, tcpip.ErrConnectionReset:
return zx.ErrConnectionReset
case tcpip.ErrWouldBlock:
return zx.ErrShouldWait
case tcpip.ErrConnectionRefused:
return zx.ErrConnectionRefused
case tcpip.ErrTimeout:
return zx.ErrTimedOut
case tcpip.ErrConnectStarted:
return zx.ErrShouldWait
case tcpip.ErrNotSupported, tcpip.ErrQueueSizeNotSupported:
return zx.ErrNotSupported
case tcpip.ErrNotConnected:
return zx.ErrNotConnected
case tcpip.ErrConnectionAborted:
return zx.ErrConnectionAborted
}
log.Printf("%v", e)
return zx.ErrInternal
}
func (s *socketServer) opGetSockOpt(ios *iostate, msg *fdio.Msg) zx.Status {
var val c_mxrio_sockopt_req_reply
if err := val.Decode(msg.Data[:msg.Datalen]); err != nil {
if debug {
log.Printf("getsockopt: decode argument: %v", err)
}
return errStatus(err)
}
if ios.ep == nil {
if debug {
log.Printf("getsockopt: no socket")
}
return zx.ErrBadState
}
if opt := val.Unpack(); opt != nil {
switch o := opt.(type) {
case tcpip.ErrorOption:
ios.mu.Lock()
err := ios.lastError
ios.lastError = nil
ios.mu.Unlock()
if err == nil {
err = ios.ep.GetSockOpt(o)
}
errno := uint32(0)
if err != nil {
// TODO: should this be a unix errno?
errno = uint32(mxNetError(err))
}
binary.LittleEndian.PutUint32(val.optval[:], errno)
val.optlen = c_socklen(4)
case tcpip.SendBufferSizeOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.ReceiveBufferSizeOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.ReceiveQueueSizeOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.NoDelayOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.ReuseAddressOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.V6OnlyOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.MulticastTTLOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.KeepaliveEnabledOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.KeepaliveIdleOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(time.Duration(o).Seconds()))
val.optlen = c_socklen(4)
case tcpip.KeepaliveIntervalOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(time.Duration(o).Seconds()))
val.optlen = c_socklen(4)
case tcpip.KeepaliveCountOption:
ios.ep.GetSockOpt(&o)
binary.LittleEndian.PutUint32(val.optval[:], uint32(o))
val.optlen = c_socklen(4)
case tcpip.InfoOption:
ios.ep.GetSockOpt(&o)
info := c_mxrio_sockopt_tcp_info{
// Microseconds.
rtt: uint32(o.Rtt.Nanoseconds() / 1000),
rttvar: uint32(o.Rttvar.Nanoseconds() / 1000),
}
info.Encode(&val)
default:
binary.LittleEndian.PutUint32(val.optval[:], 0)
val.optlen = c_socklen(4)
}
} else {
val.optlen = 0
}
val.Encode(msg)
return zx.ErrOk
}
func (s *socketServer) opSetSockOpt(ios *iostate, msg *fdio.Msg) zx.Status {
var val c_mxrio_sockopt_req_reply
if err := val.Decode(msg.Data[:msg.Datalen]); err != nil {
if debug {
log.Printf("setsockopt: decode argument: %v", err)
}
return errStatus(err)
}
if ios.ep == nil {
if debug {
log.Printf("setsockopt: no socket")
}
return zx.ErrBadState
}
if opt := val.Unpack(); opt != nil {
if err := ios.ep.SetSockOpt(opt); err != nil {
return mxNetError(err)
}
}
msg.Datalen = 0
msg.SetOff(0)
return zx.ErrOk
}
func (s *socketServer) opBind(ios *iostate, msg *fdio.Msg) (status zx.Status) {
addr, err := readSockaddrIn(msg.Data[:msg.Datalen])
if err != nil {
if debug {
log.Printf("bind: bad input: %v", err)
}
return errStatus(err)
}
if debug2 {
defer func() {
log.Printf("bind(%s): %v", *addr, status)
}()
}
if ios.ep == nil {
if debug {
log.Printf("bind: no socket")
}
return zx.ErrBadState
}
if err := ios.ep.Bind(*addr, nil); err != nil {
return mxNetError(err)
}
msg.Datalen = 0
msg.SetOff(0)
return zx.ErrOk
}
func (s *socketServer) buildIfInfos() *c_netc_get_if_info {
rep := &c_netc_get_if_info{}
s.ns.mu.Lock()
defer s.ns.mu.Unlock()
index := uint32(0)
for nicid, ifs := range s.ns.ifStates {
if ifs.nic.Addr == header.IPv4Loopback {
continue
}
rep.info[index].index = uint16(index + 1)
rep.info[index].flags |= NETC_IFF_UP
copy(rep.info[index].name[:], []byte(fmt.Sprintf("en%d", nicid)))
writeSockaddrStorage(&rep.info[index].addr, tcpip.FullAddress{NIC: nicid, Addr: ifs.nic.Addr})
writeSockaddrStorage(&rep.info[index].netmask, tcpip.FullAddress{NIC: nicid, Addr: tcpip.Address(ifs.nic.Netmask)})
// Long-hand for: broadaddr = ifs.nic.Addr | ^ifs.nic.Netmask
broadaddr := []byte(ifs.nic.Addr)
for i := range broadaddr {
broadaddr[i] |= ^ifs.nic.Netmask[i]
}
writeSockaddrStorage(&rep.info[index].broadaddr, tcpip.FullAddress{NIC: nicid, Addr: tcpip.Address(broadaddr)})
index++
}
rep.n_info = index
return rep
}
// We remember the interface list from the last time ioctlNetcGetNumIfs was called. This avoids
// a race condition if the interface list changes between calls to ioctlNetcGetIfInfoAt.
var lastIfInfo *c_netc_get_if_info
func (s *socketServer) opIoctl(ios *iostate, msg *fdio.Msg) zx.Status {
// TODO: deprecated in favor of FIDL service. Remove.
switch msg.IoctlOp() {
case ioctlNetcGetIfInfo:
rep := s.buildIfInfos()
rep.Encode(msg)
return zx.ErrOk
case ioctlNetcGetNumIfs:
lastIfInfo = s.buildIfInfos()
binary.LittleEndian.PutUint32(msg.Data[:msg.Arg], lastIfInfo.n_info)
msg.Datalen = 4
return zx.ErrOk
case ioctlNetcGetIfInfoAt:
if lastIfInfo == nil {
if debug {
log.Printf("ioctlNetcGetIfInfoAt: called before ioctlNetcGetNumIfs")
}
return zx.ErrBadState
}
d := msg.Data[:msg.Datalen]
if len(d) != 4 {
if debug {
log.Printf("ioctlNetcGetIfInfoAt: bad input length %d", len(d))
}
return zx.ErrInvalidArgs
}
requestedIndex := binary.LittleEndian.Uint32(d)
if requestedIndex >= lastIfInfo.n_info {
if debug {
log.Printf("ioctlNetcGetIfInfoAt: index out of range (%d vs %d)", requestedIndex, lastIfInfo.n_info)
}
return zx.ErrInvalidArgs
}
lastIfInfo.info[requestedIndex].Encode(msg)
return zx.ErrOk
case ioctlNetcGetNodename:
s.ns.mu.Lock()
nodename := s.ns.nodename
s.ns.mu.Unlock()
msg.Datalen = uint32(copy(msg.Data[:msg.Arg], nodename))
msg.Data[msg.Datalen] = 0
return zx.ErrOk
}
if debug {
log.Printf("opIoctl op=0x%x, datalen=%d", msg.Op(), msg.Datalen)
}
return zx.ErrInvalidArgs
}
func fdioSockAddrReply(a tcpip.FullAddress, msg *fdio.Msg) zx.Status {
var err error
rep := c_mxrio_sockaddr_reply{}
rep.len, err = writeSockaddrStorage(&rep.addr, a)
if err != nil {
return errStatus(err)
}
rep.Encode(msg)
msg.SetOff(0)
return zx.ErrOk
}
func (s *socketServer) opGetSockName(ios *iostate, msg *fdio.Msg) zx.Status {
a, err := ios.ep.GetLocalAddress()
if err != nil {
return mxNetError(err)
}
if debug2 {
log.Printf("getsockname(): %v", a)
}
return fdioSockAddrReply(a, msg)
}
func (s *socketServer) opGetPeerName(ios *iostate, msg *fdio.Msg) (status zx.Status) {
if ios.ep == nil {
return zx.ErrBadState
}
a, err := ios.ep.GetRemoteAddress()
if err != nil {
return mxNetError(err)
}
return fdioSockAddrReply(a, msg)
}
func (s *socketServer) loopListen(ios *iostate, inCh chan struct{}) {
ios.listenLoopDone = make(chan struct{})
defer func() { ios.listenLoopDone <- struct{}{} }()
// When an incoming connection is available, wait for the listening socket to
// enter a shareable state, then share it with zircon.
for range inCh {
obs, err := ios.dataHandle.WaitOne(
zx.SignalSocketShare|zx.SignalSocketPeerClosed|LOCAL_SIGNAL_CLOSING,
zx.TimensecInfinite)
switch mxerror.Status(err) {
case zx.ErrOk:
switch {
case obs&zx.SignalSocketShare != 0:
// NOP
case obs&LOCAL_SIGNAL_CLOSING != 0:
return
case obs&zx.SignalSocketPeerClosed != 0:
return
}
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("listen: wait failed: %v", err)
}
newep, newwq, e := ios.ep.Accept()
if e == tcpip.ErrWouldBlock {
log.Printf("listen: internal error. Accept returned ErrWouldBlock")
continue
}
if e != nil {
if debug {
log.Printf("listen: accept failed: %v", e)
}
return
}
err = s.newIostate(ios.dataHandle, nil, ios.netProto, ios.transProto, newwq, newep, true, true)
if err != nil {
if debug {
log.Printf("listen: newIostate failed: %v", e)
}
return
}
}
}
func (s *socketServer) opListen(ios *iostate, msg *fdio.Msg) (status zx.Status) {
d := msg.Data[:msg.Datalen]
if len(d) != 4 {
if debug {
log.Printf("listen: bad input length %d", len(d))
}
return zx.ErrInvalidArgs
}
backlog := binary.LittleEndian.Uint32(d)
if ios.ep == nil {
if debug {
log.Printf("listen: no socket")
}
return zx.ErrBadState
}
inEntry, inCh := waiter.NewChannelEntry(nil)
ios.wq.EventRegister(&inEntry, waiter.EventIn)
if err := ios.ep.Listen(int(backlog)); err != nil {
if debug {
log.Printf("listen: %v", err)
}
return mxNetError(err)
}
if ios.withNewSocket {
go func() {
defer ios.wq.EventUnregister(&inEntry)
s.loopListen(ios, inCh)
}()
} else {
go func() {
defer ios.wq.EventUnregister(&inEntry)
// When an incoming connection is queued up (that is,
// calling accept would return a new connection),
// signal the fdio socket that it exists. This allows
// the socket API client to implement a blocking accept.
for range inCh {
err := zx.Handle(ios.dataHandle).SignalPeer(0, ZXSIO_SIGNAL_INCOMING)
switch mxerror.Status(err) {
case zx.ErrOk:
continue
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return
default:
log.Printf("socket signal ZXSIO_SIGNAL_INCOMING: %v", err)
}
continue
}
}()
}
msg.Datalen = 0
msg.SetOff(0)
return zx.ErrOk
}
func (s *socketServer) opConnect(ios *iostate, msg *fdio.Msg) (status zx.Status) {
if msg.Datalen == 0 {
if ios.transProto == udp.ProtocolNumber {
// connect() can be called with no address to
// disassociate UDP sockets.
ios.ep.Shutdown(tcpip.ShutdownRead)
return zx.ErrOk
}
if debug {
log.Printf("connect: no input")
}
return zx.ErrInvalidArgs
}
addr, err := readSockaddrIn(msg.Data[:msg.Datalen])
if err != nil {
if debug {
log.Printf("connect: bad input: %v", err)
}
return errStatus(err)
}
if debug2 {
defer func() {
log.Printf("connect(%s): %v", *addr, status)
}()
}
if addr.Addr == "" {
// TODO: Not ideal. We should pass an empty addr to the endpoint,
// and netstack should find the first local interface that it can
// connect to. Until that exists, we assume localhost.
switch ios.netProto {
case ipv4.ProtocolNumber:
addr.Addr = header.IPv4Loopback
case ipv6.ProtocolNumber:
addr.Addr = header.IPv6Loopback
}
}
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
ios.wq.EventRegister(&waitEntry, waiter.EventOut)
e := ios.ep.Connect(*addr)
msg.SetOff(0)
msg.Datalen = 0
if e == tcpip.ErrConnectStarted {
go func() {
<-notifyCh
ios.wq.EventUnregister(&waitEntry)
sigs := zx.Signals(ZXSIO_SIGNAL_OUTGOING)
e = ios.ep.GetSockOpt(tcpip.ErrorOption{})
if err != nil {
ios.mu.Lock()
ios.lastError = e
ios.mu.Unlock()
} else {
sigs |= ZXSIO_SIGNAL_CONNECTED
}
zx.Handle(ios.dataHandle).SignalPeer(0, sigs)
}()
return zx.ErrShouldWait
}
ios.wq.EventUnregister(&waitEntry)
if e != nil {
log.Printf("connect: addr=%v, %v", *addr, err)
return mxNetError(e)
}
if debug2 {
log.Printf("connect: connected")
}
if ios.transProto == tcp.ProtocolNumber {
err := zx.Handle(ios.dataHandle).SignalPeer(0, ZXSIO_SIGNAL_CONNECTED)
switch status := mxerror.Status(err); status {
case zx.ErrOk:
// NOP
case zx.ErrBadHandle, zx.ErrCanceled, zx.ErrPeerClosed:
return status
default:
log.Printf("connect: signal failed: %v", err)
}
}
return zx.ErrOk
}
func (s *socketServer) opGetAddrInfo(ios *iostate, msg *fdio.Msg) zx.Status {
var val c_mxrio_gai_req
if err := val.Decode(msg); err != nil {
return errStatus(err)
}
node, service, hints := val.Unpack()
if debug2 {
log.Printf("getaddrinfo node=%q, service=%q", node, service)
}
s.mu.Lock()
dnsClient := s.dnsClient
s.mu.Unlock()
if dnsClient == nil {
log.Println("getaddrinfo called, but no DNS client available.")
rep := c_mxrio_gai_reply{retval: EAI_FAIL}
rep.Encode(msg)
return zx.ErrOk
}
if hints.ai_socktype == 0 {
hints.ai_socktype = SOCK_STREAM
}
if hints.ai_protocol == 0 {
if hints.ai_socktype == SOCK_STREAM {
hints.ai_protocol = IPPROTO_TCP
} else if hints.ai_socktype == SOCK_DGRAM {
hints.ai_protocol = IPPROTO_UDP
}
}
t, err := sockProto(int(hints.ai_socktype), int(hints.ai_protocol))
if err != nil {
return errStatus(err)
}
var port uint16
if service != "" {
port, err = serviceLookup(service, t)
if err != nil {
log.Printf("getaddrinfo: %v", err)
return zx.ErrNotSupported
}
}
var addrs []tcpip.Address
if val.node_is_null == 1 {
addrs = append(addrs, "\x00\x00\x00\x00")
} else {
addrs, err = dnsClient.LookupIP(node)
if err != nil {
if node == "localhost" {
addrs = append(addrs, "\x7f\x00\x00\x01")
} else {
addrs = append(addrs, tcpip.Parse(node))
if debug2 {
log.Printf("getaddrinfo: addr=%v, err=%v", addrs, err)
}
}
}
}
if debug2 {
log.Printf("getaddrinfo: addrs=%v", addrs)
}
if len(addrs) == 0 || len(addrs[0]) == 0 {
rep := c_mxrio_gai_reply{retval: EAI_NONAME}
rep.Encode(msg)
return zx.ErrOk
}
rep := c_mxrio_gai_reply{}
for i := 0; i < len(addrs) && rep.nres < ZXRIO_GAI_REPLY_MAX; i++ {
res := &rep.res[rep.nres]
res.ai.ai_socktype = hints.ai_socktype
res.ai.ai_protocol = hints.ai_protocol
// The 0xdeadbeef constant indicates the other side needs to
// adjust ai_addr with the value passed below.
res.ai.ai_addr = 0xdeadbeef
switch len(addrs[i]) {
case 4:
if hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET {
continue
}
rep.nres++
res.ai.ai_family = AF_INET
res.ai.ai_addrlen = c_socklen(c_sockaddr_in_len)
sockaddr := c_sockaddr_in{sin_family: AF_INET}
sockaddr.sin_port.setPort(port)
copy(sockaddr.sin_addr[:], addrs[i])
writeSockaddrStorage4(&res.addr, &sockaddr)
case 16:
if hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6 {
continue
}
rep.nres++
res.ai.ai_family = AF_INET6
res.ai.ai_addrlen = c_socklen(c_sockaddr_in6_len)
sockaddr := c_sockaddr_in6{sin6_family: AF_INET6}
sockaddr.sin6_port.setPort(port)
copy(sockaddr.sin6_addr[:], addrs[i])
writeSockaddrStorage6(&res.addr, &sockaddr)
default:
if debug {
log.Printf("getaddrinfo: len(addr)=%d, wrong size", len(addrs[i]))
}
// TODO: failing to resolve is a valid reply. fill out retval
return zx.ErrBadState
}
}
rep.Encode(msg)
return zx.ErrOk
}
func (s *socketServer) opFcntl(ios *iostate, msg *fdio.Msg) zx.Status {
cmd := uint32(msg.Arg)
if debug2 {
log.Printf("fcntl: cmd %v, flags %v", cmd, msg.FcntlFlags())
}
switch cmd {
case fdio.OpFcntlCmdGetFL:
// Set flags to 0 as O_NONBLOCK is handled on the client side.
msg.SetFcntlFlags(0)
case fdio.OpFcntlCmdSetFL:
// Do nothing.
default:
return zx.ErrNotSupported
}
msg.Datalen = 0
return zx.ErrOk
}
func (s *socketServer) iosCloseHandler(ios *iostate, cookie cookie) {
s.mu.Lock()
delete(s.io, cookie)
s.mu.Unlock()
if ios.ep != nil {
// Signal that we're about to close. This tells the various message loops to finish
// processing, and let us know when they're done.
err := zx.Handle(ios.dataHandle).Signal(0, LOCAL_SIGNAL_CLOSING)
go func() {
switch mxerror.Status(err) {
case zx.ErrOk:
<-ios.writeLoopDone
if ios.withNewSocket {
<-ios.controlLoopDone
}
if ios.listenLoopDone != nil {
<-ios.listenLoopDone
}
default:
log.Printf("close: signal failed: %v", err)
}
ios.ep.Close()
ios.dataHandle.Close()
if ios.peerDataHandle != 0 {
ios.peerDataHandle.Close()
}
}()
}
}
func (s *socketServer) fdioHandler(msg *fdio.Msg, rh zx.Handle, cookieVal int64) zx.Status {
cookie := cookie(cookieVal)
op := msg.Op()
if debug2 {
log.Printf("socketServer.fdio: op=%v, len=%d, arg=%v, hcount=%d", op, msg.Datalen, msg.Arg, msg.Hcount)
}
s.mu.Lock()
ios := s.io[cookie]
s.mu.Unlock()
if ios == nil {
if op == fdio.OpOpen {
// iostate has not been allocated if the open op is for "none" and "socket",
// continue to the switch below.
// TODO: return an error if the open op is for "accept".
} else if op == fdio.OpClose {
if rh == 0 || cookie == 0 {
// There are two special cases we can simply return here:
// 1. [rh == 0] the close op was synthesized by Dispatcher (because
// the peer channel was closed).
// 2. [rh != 0 and cookie == 0] the close op was for the open handle
// of netstack node in the namespace (which is not a socket).
return zx.ErrOk
}
} else {
log.Printf("fdioHandler: request (op:%v) dropped because of the state mismatch", op)
return zx.ErrBadState
}
}
switch op {
case fdio.OpOpen:
path := string(msg.Data[:msg.Datalen])
var err error
switch {
case strings.HasPrefix(path, "none-v2"): // ZXRIO_SOCKET_DIR_NONE
err = s.newIostate(msg.Handle[0], nil, ipv4.ProtocolNumber, tcp.ProtocolNumber, nil, nil, true, false)
case strings.HasPrefix(path, "socket-v2/"): // ZXRIO_SOCKET_DIR_SOCKET
err = s.opSocket(msg.Handle[0], ios, msg, path)
case strings.HasPrefix(path, "accept-v2"): // ZXRIO_SOCKET_DIR_ACCEPT
log.Printf("open: unimplemented")
err = mxerror.Errorf(zx.ErrNotSupported, "open: unimplemented path=%q", path)
case strings.HasPrefix(path, "none"): // ZXRIO_SOCKET_DIR_NONE
err = s.newIostate(msg.Handle[0], nil, ipv4.ProtocolNumber, tcp.ProtocolNumber, nil, nil, false, false)
case strings.HasPrefix(path, "socket/"): // ZXRIO_SOCKET_DIR_SOCKET
err = s.opSocket(msg.Handle[0], ios, msg, path)
case strings.HasPrefix(path, "accept"): // ZXRIO_SOCKET_DIR_ACCEPT
err = s.opAccept(msg.Handle[0], ios, msg, path)
default:
if debug2 {
log.Printf("open: unknown path=%q", path)
}
log.Printf("open: unknown path=%q", path)
err = mxerror.Errorf(zx.ErrNotSupported, "open: unknown path=%q", path)
}
if err != nil {
ro := fdio.RioDescription{
Status: errStatus(err),
}
ro.SetOp(fdio.OpOnOpen)
ro.Write(msg.Handle[0], 0)
msg.Handle[0].Close()
}
return fdio.ErrIndirect.Status
case fdio.OpClone:
ios.acquire()
err := s.newIostate(msg.Handle[0], ios, ios.netProto, tcp.ProtocolNumber, nil, nil, false, false)
if err != nil {
ios.release(func() { s.iosCloseHandler(ios, cookie) })
ro := fdio.RioDescription{
Status: errStatus(err),
}
ro.SetOp(fdio.OpOnOpen)
ro.Write(msg.Handle[0], 0)
msg.Handle[0].Close()
}
return fdio.ErrIndirect.Status
case fdio.OpConnect:
return s.opConnect(ios, msg) // do_connect
case fdio.OpClose:
ios.release(func() { s.iosCloseHandler(ios, cookie) })
return zx.ErrOk
case fdio.OpRead:
if debug {
log.Printf("unexpected opRead")
}
case fdio.OpWrite:
if debug {
log.Printf("unexpected opWrite")
}
case fdio.OpWriteAt:
case fdio.OpSeek:
return zx.ErrOk
case fdio.OpStat:
case fdio.OpTruncate:
case fdio.OpSync:
case fdio.OpSetAttr:
case fdio.OpBind:
return s.opBind(ios, msg)
case fdio.OpListen:
return s.opListen(ios, msg)
case fdio.OpIoctl:
return s.opIoctl(ios, msg)
case fdio.OpGetAddrInfo:
return s.opGetAddrInfo(ios, msg)
case fdio.OpGetSockname:
return s.opGetSockName(ios, msg)
case fdio.OpGetPeerName:
return s.opGetPeerName(ios, msg)
case fdio.OpGetSockOpt:
return s.opGetSockOpt(ios, msg)
case fdio.OpSetSockOpt:
return s.opSetSockOpt(ios, msg)
case fdio.OpFcntl:
return s.opFcntl(ios, msg)
default:
log.Printf("unknown socket op: %v", op)
return zx.ErrNotSupported
}
return zx.ErrBadState
// TODO do_halfclose
}
func (s *socketServer) zxsocketHandler(msg *zxsocket.Msg, rh zx.Socket, cookieVal int64) zx.Status {
return s.fdioHandler(msg.AsFDIOMsg(), zx.Handle(rh), cookieVal)
}