blob: b662b15dff7b58d011c4072259a76b3e52e2a3ef [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"
"strconv"
"syscall"
"syscall/zx"
"syscall/zx/fdio"
fidlIo "syscall/zx/io"
"syscall/zx/mxnet"
"syscall/zx/net"
"syscall/zx/zxwait"
"unsafe"
)
// Socket is an fdio.FDIO socket.
type Socket struct {
net.ControlInterface
socket zx.Socket
dgram bool
flags uint32
}
func NewSocket(control net.ControlInterface) (*Socket, error) {
info, err := control.Describe()
if err != nil {
return nil, err
}
switch w := info.Which(); w {
case fidlIo.NodeInfoService, fidlIo.NodeInfoFile, fidlIo.NodeInfoDirectory, fidlIo.NodeInfoPipe, fidlIo.NodeInfoVmofile, fidlIo.NodeInfoDevice, fidlIo.NodeInfoTty:
return nil, &zx.Error{Status: zx.ErrInternal, Text: "zxsocket.NewSocket"}
case fidlIo.NodeInfoSocket:
socket := info.Socket.Socket
var info zx.InfoSocket
if err := socket.Handle().GetInfo(zx.ObjectInfoSocket, unsafe.Pointer(&info), uint(unsafe.Sizeof(info))); err != nil {
return nil, err
}
return &Socket{
ControlInterface: control,
socket: socket,
dgram: info.Options&zx.SocketDatagram != 0,
}, nil
default:
panic("unknown node info tag " + strconv.FormatInt(int64(w), 10))
}
}
func (s *Socket) Wait(signals zx.Signals, timeout zx.Time) (zx.Signals, error) {
return zxwait.Wait(*s.socket.Handle(), signals, timeout)
}
// Handles returns the underlying handles for this Socket.
func (s *Socket) Handles() []zx.Handle {
return []zx.Handle{*s.ControlInterface.Handle(), *s.socket.Handle()}
}
// 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 code, err := s.ControlInterface.Close(); err != nil {
return err
} else if code != 0 {
return syscall.Errno(code)
}
if err := s.ControlInterface.Channel.Close(); err != nil {
return err
}
return s.socket.Close()
}
// 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"}
}
// 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. zxs_recvmsg_stream in zircon/system/ulib/zxs/zxs.cpp
for {
n, err := s.socket.Read(data, 0)
if err != nil {
if err, ok := err.(*zx.Error); ok {
switch err.Status {
case zx.ErrPeerClosed:
return 0, io.EOF
case zx.ErrShouldWait:
obs, err := s.Wait(zx.SignalSocketReadable|zx.SignalSocketPeerClosed, zx.TimensecInfinite)
if err != nil {
if err, ok := err.(*zx.Error); ok {
switch err.Status {
case zx.ErrBadHandle, zx.ErrCanceled:
return 0, io.EOF
}
}
return 0, err
}
switch {
case obs&zx.SignalSocketReadable != 0:
continue
case obs&zx.SignalSocketPeerClosed != 0:
return 0, io.EOF
}
}
}
return 0, err
}
return n, nil
}
}
// 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_sendmsg_stream
var total int
for {
n, err := s.socket.Write(data, 0)
total += n
if err != nil {
if err, ok := err.(*zx.Error); ok {
switch err.Status {
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)"}
}
}
return total, err
}
return total, nil
}
}
// 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)
if err != nil {
if err, ok := err.(*zx.Error); ok {
switch err.Status {
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.SignalSocketPeerWriteDisabled, zx.TimensecInfinite)
if err != nil {
return n, err
}
if obs&zx.SignalSocketReadable != 0 {
continue
}
if obs&zx.SignalSocketPeerClosed != 0 || obs&zx.SignalSocketPeerWriteDisabled != 0 {
return n, &zx.Error{Status: zx.ErrPeerClosed, Text: "zxsocket.Socket.recvMsg"}
}
return n, &zx.Error{Status: zx.ErrInternal, Text: "zxsocket.Socket.recvMsg(impossible state))"}
}
}
return n, err
}
return n, nil
}
}
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)
if err != nil {
if err, ok := err.(*zx.Error); ok {
switch err.Status {
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)"}
}
}
if n != 0 {
return n, &zx.Error{Status: zx.ErrInternal, Text: "zxsocket.Socket.SendMsg(datagram short-write)"}
}
return 0, err
}
return len(b), nil
}
}