| // 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 |
| } |
| } |
| } |