| // 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" |
| "syscall/zx" |
| "syscall/zx/fdio" |
| fidlIo "syscall/zx/io" |
| "syscall/zx/mxnet" |
| "syscall/zx/net" |
| "syscall/zx/zxwait" |
| ) |
| |
| // Socket is an fdio.FDIO socket. |
| type Socket struct { |
| net.SocketControlInterface |
| |
| dgram bool |
| flags uint32 |
| } |
| |
| func NewSocket(s zx.Socket) *Socket { |
| return &Socket{ |
| SocketControlInterface: net.SocketControlInterface{ |
| Socket: s, |
| }, |
| } |
| } |
| |
| func (s *Socket) Wait(signals zx.Signals, timeout zx.Time) (zx.Signals, 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 == zx.Socket(zx.HandleInvalid) { |
| return nil |
| } |
| if code, err := s.SocketControlInterface.Close(); err != nil { |
| return err |
| } else if code != 0 { |
| return syscall.Errno(code) |
| } |
| |
| s.Socket.Close() |
| s.Socket = zx.Socket(zx.HandleInvalid) |
| |
| 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) { |
| code, out, err := s.SocketControlInterface.Ioctl(int16(op), in) |
| if err != nil { |
| return nil, nil, err |
| } |
| if code != 0 { |
| return nil, nil, syscall.Errno(code) |
| } |
| return out, 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. 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.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(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 |
| } |
| } |
| |
| // SetDgram marks a *Socket as a datagram (UDP) socket. |
| // A different protocol is used internally. |
| func (s *Socket) SetDgram() { |
| s.dgram = true |
| } |