blob: 03b1135dcdb2d0406130fa824373508a3329c6de [file] [log] [blame]
// Copyright 2016 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 fuchsia
package zxsocket
import (
"errors"
"syscall/zx"
"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 && status(err) == zx.ErrShouldWait {
obs, err := s.Wait(mxnet.MXSIO_SIGNAL_OUTGOING, zx.TimensecInfinite)
switch 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() {
return 0, zx.Error{Status: zx.ErrInvalidArgs, Text: "zxsocket.txn"}
}
msgBytes := (*[MessageSizeMax]byte)(unsafe.Pointer(msg))
numBytes, err := WriteControl(s.socket, msgBytes[:MessageHeaderSize+msg.Datalen])
if err != nil {
return 0, err
}
numBytes, err = ReadControl(s.socket, msgBytes[:])
if err != nil {
return 0, err
}
if !msg.IsValidIncoming(uint32(numBytes)) {
return 0, zx.Error{Status: zx.ErrIO, Text: "zxsocket.txn"}
}
// Check for remote error
status := zx.Status(msg.Arg)
if status < 0 {
return 0, zx.Error{Status: status, Text: "zxsocket.txn"}
}
return uint32(status), nil
}
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
}
func (s *Socket) Accept() (*Socket, error) {
if s.flags&SocketFlagsDidListen == 0 {
return nil, errors.New("zxsocket.Accept: listen was not called")
}
var h zx.Handle
for {
err := s.socket.Accept(&h)
if err == nil {
break
}
if 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 "", 0, err
}
addr, port, err = mxnet.DecodeSockaddr(out)
if err != nil {
return "", 0, err
}
return addr, port, nil
}
func (s *Socket) SockName() (addr mxnet.Addr, port uint16, err error) {
return s.getName(OpGetSockname, "GetSockName")
}
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) {
if len(in) > MessageChunkSize || len(out) > MessageChunkSize {
return 0, zx.Error{Status: zx.ErrInvalidArgs, Text: "zxsocket.Misc"}
}
var msg Msg
msg.SetOp(op)
msg.Arg = int32(len(out))
msg.Datalen = uint32(len(in))
msg.SetOff(off)
copy(msg.Data[:], in)
if len(handles) > 0 {
copy(msg.Handle[:], handles)
msg.Hcount = uint32(len(handles))
}
if _, err = s.txn(&msg); err != nil {
return 0, err
}
if int(msg.Datalen) > len(out) {
return 0, zx.Error{Status: zx.ErrIO, Text: "zxsocket.Misc"}
}
n = copy(out, msg.Data[:msg.Datalen])
if op == OpListen {
s.flags |= SocketFlagsDidListen
}
return n, nil
}