blob: d38b9f557f77a31fcde8a2855efcf6d46e94004d [file] [log] [blame]
// Copyright 2017 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 (
"io"
"syscall/zx"
"syscall/zx/fdio"
fidlIo "syscall/zx/io"
"syscall/zx/mxnet"
"syscall/zx/zxwait"
)
// Status extracts an zx.Status from an error. This is duplicated from syscall/zx/merror,
// but we can't import that directly because it causes an import loop.
func status(err error) zx.Status {
if err == nil {
return zx.ErrOk
}
if s, hasStatus := err.(zx.Error); hasStatus {
return s.Status
}
return zx.ErrInternal
}
// Socket is an fdio.FDIO socket.
type Socket struct {
socket zx.Socket
dgram bool
flags uint32
}
// Socket flags
const (
SocketFlagsDidListen = 1 << iota // zxsocket._DID_LISTEN
)
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)
}
// 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))
n = copy(data, b)
return n, err
}
// c.f. mxsio_read_stream in zircon/system/ulib/fdio/remoteio.c
for {
n, err = s.socket.Read(data, 0)
switch 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 status(err) {
case zx.ErrOk:
switch {
case obs&zx.SignalSocketReadable != 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
}
}
}
// 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"}
}
// Write implements fdio.FDIO for Socket.
func (s *Socket) Write(data []byte) (int, error) {
if s.dgram {
n, err := s.SendMsg(data, "", 0)
return n, err
}
// c.f. zxsio_write_stream
var total int
for {
n, err := s.socket.Write(data, 0)
total += n
switch status(err) {
case zx.ErrOk:
return total, nil
case zx.ErrShouldWait:
obs, err := s.Wait(zx.SignalSocketWritable|zx.SignalSocketPeerClosed|zx.SignalSocketWriteDisabled, zx.TimensecInfinite)
if err != nil {
return total, err
}
if obs&zx.SignalSocketPeerClosed != 0 || obs&zx.SignalSocketWriteDisabled != 0 {
return total, zx.Error{Status: zx.ErrPeerClosed, Text: "zxsocket.Socket.Write"}
}
if obs&zx.SignalSocketWritable != 0 {
data = data[n:]
continue
}
// This case should be impossible:
return total, zx.Error{Status: zx.ErrInternal, Text: "zxsocket.Socket.Write(impossible state)"}
default:
return total, err
}
}
}
// 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"}
}
// 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) {
mlen := maxLen + mxnet.SockmsgHdrLen
msgdata := make([]byte, mlen)
n, err := s.recvMsg(msgdata)
if err != nil {
return nil, 0, "", 0, err
}
msgdata = msgdata[:n]
addrstr, port, flags, err := mxnet.DecodeSockmsgHdr(msgdata)
if err != nil {
return nil, 0, "", 0, err
}
return msgdata[mxnet.SockmsgHdrLen:], flags, string(addrstr), port, nil
}
func (s *Socket) recvMsg(data []byte) (int, error) {
for {
n, err := s.socket.Read(data, 0)
switch status(err) {
case zx.ErrOk:
return n, err
case zx.ErrShouldWait:
if n != 0 {
return n, zx.Error{Status: zx.ErrInternal, Text: "zsocket.Socket.recvMsg(datagram short-read)"}
}
obs, err := s.Wait(zx.SignalSocketReadable|zx.SignalSocketPeerClosed|zx.SignalSocketReadDisabled, zx.TimensecInfinite)
if err != nil {
return n, err
}
if obs&zx.SignalSocketReadable != 0 {
continue
}
if obs&zx.SignalSocketPeerClosed != 0 || obs&zx.SignalSocketReadDisabled != 0 {
return n, zx.Error{Status: zx.ErrPeerClosed, Text: "zxsocket.Socket.recvMsg"}
}
return n, zx.Error{Status: zx.ErrInternal, Text: "zxsocket.Socket.recvMsg"}
default:
return n, err
}
}
}
func (s *Socket) SendMsg(b []byte, addr string, port uint16) (int, error) {
data := make([]byte, len(b)+mxnet.SockmsgHdrLen)
err := mxnet.EncodeSockmsgHdr(data, mxnet.Addr(addr), port, 0)
if err != nil {
return 0, err
}
copy(data[mxnet.SockmsgHdrLen:], b)
for {
n, err := s.socket.Write(data, 0)
switch status(err) {
case zx.ErrOk:
return len(b), nil
case zx.ErrShouldWait:
if n != 0 {
return n, zx.Error{Status: zx.ErrInternal, Text: "zxsocket.Socket.SendMsg(datagram short-write)"}
}
obs, err := s.Wait(zx.SignalSocketWritable|zx.SignalSocketPeerClosed|zx.SignalSocketWriteDisabled, zx.TimensecInfinite)
if err != nil {
return 0, err
}
if obs&zx.SignalSocketWritable != 0 {
continue
}
if obs&zx.SignalSocketPeerClosed != 0 || obs&zx.SignalSocketWriteDisabled != 0 {
return 0, zx.Error{Status: zx.ErrPeerClosed, Text: "zxsocket.Socket.SendMsg"}
}
return 0, zx.Error{Status: zx.ErrInternal, Text: "zxsocket.Socket.SendMsg(impossible state)"}
default:
if n != 0 {
return n, zx.Error{Status: zx.ErrInternal, Text: "zxsocket.Socket.SendMsg(datagram short-write)"}
}
return 0, err
}
}
}
// SetDgram marks a *Socket as a datagram (UDP) socket.
// A different protocol is used internally.
func (s *Socket) SetDgram() {
s.dgram = true
}
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 status(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 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:
return 0, err
}
default:
return 0, err
}
}
}
func WriteControl(s zx.Socket, data []byte) (int, error) {
// c.f. zxsocket._write_control
for {
n, err := s.Write(data, zx.SocketControl)
switch 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
}
}
}