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