| // 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 ( |
| "unsafe" |
| |
| "syscall/zx" |
| ) |
| |
| const ( |
| // MessageChunkSize is the largest size of 'data' in an zxsocket message |
| MessageChunkSize = 900 |
| |
| // MessageSizeMax is the largest size of a zxsocket message |
| MessageSizeMax = uint32(unsafe.Sizeof(Msg{})) |
| |
| // MessageHeaderSize is the size of zxsocket message without 'data' |
| MessageHeaderSize = uint32(unsafe.Sizeof(MsgHeader{})) |
| |
| // MessageMaxHandles is the maximum number of handles a message may carry. |
| // In reality, zx.Sockets can't hold handles, but this is used to support |
| // the deprecated RemoteIO protocol over a socket. |
| MessageMaxHandles = 3 |
| ) |
| |
| // Custom errors for zxsocket RemoteIO. |
| var ( |
| // If returned from dispatcher callback, indicates that the dispatcher should immediately |
| // disconnect with no additional callbacks. |
| ErrDisconnectNoCallback = zx.Error{Status: 1, Text: "fdio.Dispatcher"} |
| // If returned from dispatcher callback, indicates that there are no more messages to read. |
| ErrNoWork = zx.Error{Status: -9999, Text: "fdio.Dispatcher.NoWork"} |
| // If returned from dispatcher callback, indicates that the message was handed to another |
| // server. |
| ErrIndirect = zx.Error{Status: -9998, Text: "fdio.Dispatcher.Indirect"} |
| ) |
| |
| const ( |
| OpFlagOneHandle = uint32(0x100 + iota) |
| ) |
| |
| // Values for op. Each one will signify a request for a different operation. |
| // This is a copy of the list in fdio/message.go, with the non-socket-related |
| // options removed. Note that these are deprecated. We will be migrating to |
| // FIDL2 ordinals soon. |
| const ( |
| _ = uint32(iota) |
| OpClose |
| OpClone = uint32(iota) | OpFlagOneHandle |
| OpOpen = uint32(iota) | OpFlagOneHandle |
| _ = uint32(iota) // OpMisc |
| OpRead |
| OpWrite |
| _ // OpSeek |
| _ // OpStat |
| _ // OpReaddir |
| OpIoctl |
| _ // OpUnlink |
| _ // OpReadAt |
| _ // OpWriteAt |
| _ // OpTruncate |
| _ = uint32(iota) | OpFlagOneHandle // OpRename |
| OpConnect = uint32(iota) |
| OpBind |
| OpListen |
| OpGetSockname |
| OpGetPeerName |
| OpGetSockOpt |
| OpSetSockOpt |
| OpGetAddrInfo |
| _ // OpSetAttr |
| _ // OpSync |
| _ = uint32(iota) | OpFlagOneHandle // OpLink |
| _ = uint32(iota) // OpMmap |
| OpFcntl |
| OpNumOps = uint32(iota) // The total number of operations |
| ) |
| |
| type Protocol uint32 |
| |
| // All available FDIO protocols |
| const ( |
| // The SERVICE protocol allows access to a generic interface. |
| ProtocolService = Protocol(iota) |
| // The FILE protocol allows access to a regular file. |
| ProtocolFile |
| // The DIRECTORY protocol allows access to a directory, which may |
| // contain additional nodes. |
| ProtocolDirectory |
| // The PIPE protocol uses message ports as simple, no-flow-control io pipes. |
| ProtocolPipe |
| // The VMOFILE protocol accesses a file as a VMO. |
| ProtocolVMOFile |
| // The DEVICE protocol allows access to a generic device. |
| ProtocolDevice |
| // The SOCKET protocol provides socket access. |
| ProtocolSocket |
| ProtocolSocketConnected |
| ) |
| |
| const ( |
| OpenFlagDescribe = 0x00800000 |
| ) |
| |
| // FIDL2 message header |
| type FIDLHeader struct { |
| Txid uint32 |
| reserved0 uint32 |
| flags uint32 |
| op uint32 |
| } |
| |
| type MsgHeader struct { |
| FIDLHeader |
| Datalen uint32 |
| Arg int32 |
| arg2 int64 |
| reserved1 int32 |
| Hcount uint32 |
| Handle [4]zx.Handle // 3 handles + reply pipe, unused here though. |
| } |
| |
| func (m *MsgHeader) Op() uint32 { |
| return m.op & 0x3FFF |
| } |
| func (m *MsgHeader) SetOp(v uint32) { |
| m.op = (v & 0x3FFF) |
| } |
| func (m *MsgHeader) OpHandleCount() uint32 { |
| return (m.op >> 8) & 3 |
| } |
| func (m *MsgHeader) Off() int64 { |
| return m.arg2 |
| } |
| func (m *MsgHeader) SetOff(v int64) { |
| m.arg2 = v |
| } |
| func (m *MsgHeader) Mode() uint32 { |
| return uint32(m.arg2) |
| } |
| func (m *MsgHeader) SetMode(v uint32) { |
| m.arg2 = int64(v) |
| } |
| func (m *MsgHeader) Protocol() Protocol { |
| return Protocol(m.arg2) |
| } |
| func (m *MsgHeader) SetProtocol(p Protocol) { |
| m.arg2 = int64(p) |
| } |
| func (m *MsgHeader) IoctlOp() uint32 { |
| return uint32(m.arg2) |
| } |
| func (m *MsgHeader) SetIoctlOp(v uint32) { |
| m.arg2 = int64(v) |
| } |
| func (m *MsgHeader) FcntlFlags() uint32 { |
| return uint32(m.arg2) |
| } |
| func (m *MsgHeader) SetFcntlFlags(v uint32) { |
| m.arg2 = int64(v) |
| } |
| |
| // Msg is the message sent in the zxsocket protocol. |
| type Msg struct { |
| MsgHeader |
| Data [MessageChunkSize]uint8 |
| } |
| |
| func (m *Msg) CallMsg(c *zx.Channel) (numBytes, numHandles uint32, err error) { |
| wData := (*[MessageSizeMax]byte)(unsafe.Pointer(m))[:MessageHeaderSize+m.Datalen] |
| var wHandles []zx.Handle |
| if m.Hcount > 0 { |
| wHandles = m.Handle[:m.Hcount] |
| } |
| rData := (*[MessageSizeMax]byte)(unsafe.Pointer(m))[:] |
| rHandles := m.Handle[:] |
| numBytes, numHandles, err = c.Call(0, zx.TimensecInfinite, wData, wHandles, rData, rHandles) |
| m.Hcount = numHandles |
| return |
| } |
| |
| // ReadMsg reads a message from a socket. |
| func (m *Msg) ReadMsg(s zx.Socket) (numBytes int, numHandles uint32, err error) { |
| msgBytes := (*[MessageSizeMax]byte)(unsafe.Pointer(m)) |
| // TODO: why is numBytes int? |
| numBytes, err = s.Read(msgBytes[:], zx.SocketControl) |
| numHandles = 0 |
| m.Hcount = numHandles |
| return |
| } |
| |
| // WriteMsg writes a message to a socket. |
| func (m *Msg) WriteMsg(s zx.Socket) (int, error) { |
| msgBytes := (*[MessageSizeMax]byte)(unsafe.Pointer(m)) |
| data := msgBytes[:MessageHeaderSize+m.Datalen] |
| return s.Write(data, zx.SocketControl) |
| } |
| |
| // WriteMsg writes a message to a channel. |
| func (m *Msg) WriteChannelMsg(c *zx.Channel) error { |
| msgBytes := (*[MessageSizeMax]byte)(unsafe.Pointer(m)) |
| data := msgBytes[:MessageHeaderSize+m.Datalen] |
| var handles []zx.Handle |
| if m.Hcount > 0 { |
| handles = m.Handle[:m.Hcount] |
| } |
| return c.Write(data, handles, 0) |
| } |
| |
| func (m *Msg) IsValid() bool { |
| if m.Datalen > MessageChunkSize || m.Hcount != 0 { |
| return false |
| } |
| return true |
| } |
| |
| func (m *Msg) IsValidIncoming(size uint32) bool { |
| if (size < MessageHeaderSize) || m.Datalen != (size-MessageHeaderSize) { |
| return false |
| } |
| return m.IsValid() |
| } |