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