| // 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. |
| |
| //go:build fuchsia |
| |
| // TODO: move to using fd_unix for fuchsia? |
| |
| package net |
| |
| import ( |
| "errors" |
| "fmt" |
| "internal/poll" |
| "net/netip" |
| "os" |
| "runtime" |
| "syscall" |
| "syscall/zx/net" |
| "syscall/zx/posix/socket" |
| "syscall/zx/zxsocket" |
| "time" |
| ) |
| |
| // Network file descriptor. |
| type netFD struct { |
| pfd poll.FD |
| |
| // immutable until Close |
| sock zxsocket.Socket |
| net string |
| family int |
| isConnected bool |
| laddr Addr |
| raddr Addr |
| } |
| |
| func domainToFamily(domain socket.Domain) int { |
| switch domain { |
| case socket.DomainIpv4: |
| return syscall.AF_INET |
| case socket.DomainIpv6: |
| return syscall.AF_INET6 |
| default: |
| panic(fmt.Sprintf("unrecognized socket domain %s(%d)", domain, domain)) |
| } |
| } |
| |
| func familyToDomain(family int) socket.Domain { |
| switch family { |
| case syscall.AF_INET: |
| return socket.DomainIpv4 |
| case syscall.AF_INET6: |
| return socket.DomainIpv6 |
| default: |
| panic(fmt.Sprintf("unrecognized socket family %d", family)) |
| } |
| } |
| |
| func newFD(sock zxsocket.Socket, domain socket.Domain, net string) *netFD { |
| return &netFD{ |
| pfd: poll.FD{ |
| Sysfd: syscall.OpenFDIO(sock), |
| }, |
| sock: sock, |
| family: domainToFamily(domain), |
| net: net, |
| } |
| } |
| |
| func (fd *netFD) init() error { |
| // TODO: flip to true after implementing netpoller for real |
| return fd.pfd.Init(fd.net, false) |
| } |
| |
| func (fd *netFD) isTCP() bool { |
| return len(fd.net) >= 3 && fd.net[:3] == "tcp" |
| } |
| |
| func (fd *netFD) Read(b []byte) (n int, err error) { |
| n, err = fd.pfd.Read(b) |
| runtime.KeepAlive(fd) |
| return n, err |
| } |
| |
| func (fd *netFD) Write(b []byte) (n int, err error) { |
| n, err = fd.pfd.Write(b) |
| runtime.KeepAlive(fd) |
| return n, err |
| } |
| |
| func (fd *netFD) readMsg(b []byte) (n, flags int, addr net.SocketAddress, err error) { |
| // TODO: move call to pfd |
| data, addr, err := fd.sock.(*zxsocket.DatagramSocket).RecvMsg(len(b)) |
| runtime.KeepAlive(fd) |
| n = copy(b, data) |
| return n, 0, addr, err |
| } |
| |
| func (fd *netFD) sendMsg(b []byte, addr net.SocketAddress) (n int, err error) { |
| // TODO: move call to pfd |
| n, err = fd.sock.(*zxsocket.DatagramSocket).SendMsg(b, addr) |
| runtime.KeepAlive(fd) |
| return n, err |
| } |
| |
| func (fd *netFD) closeRead() error { |
| return errors.New("net: closeRead not implemented on fuchsia") |
| } |
| |
| func (fd *netFD) closeWrite() error { |
| return errors.New("net: closeWrite not implemented on fuchsia") |
| } |
| |
| func (fd *netFD) Close() error { |
| return fd.pfd.Close() |
| } |
| |
| func (fd *netFD) dup() (*os.File, error) { |
| fdio, err := fd.sock.Clone() |
| if err != nil { |
| return nil, err |
| } |
| return os.NewFile(uintptr(syscall.OpenFDIO(fdio)), fd.name()), nil |
| } |
| |
| func (fd *netFD) accept() (*netFD, error) { |
| newm, addr, err := fd.pfd.Accept() |
| if err != nil { |
| return nil, err |
| } |
| { |
| fd := newFD(newm, familyToDomain(fd.family), fd.net) |
| if err := fd.init(); err != nil { |
| fd.Close() |
| return nil, err |
| } |
| fd.setAddr(fd.asAddr(addr, nil)) |
| return fd, nil |
| } |
| } |
| |
| func socketAddrToIpPort(addr net.SocketAddress) (IP, int) { |
| switch addr.Which() { |
| case net.SocketAddressIpv4: |
| return addr.Ipv4.Address.Addr[:], int(addr.Ipv4.Port) |
| case net.SocketAddressIpv6: |
| return addr.Ipv6.Address.Addr[:], int(addr.Ipv6.Port) |
| default: |
| panic(fmt.Sprintf("unrecognized SocketAddress variant %d", addr.Which())) |
| } |
| } |
| |
| func socketAddrToAddrPort(addr net.SocketAddress) netip.AddrPort { |
| switch addr.Which() { |
| case net.SocketAddressIpv4: |
| ipaddr := netip.AddrFrom4(addr.Ipv4.Address.Addr) |
| return netip.AddrPortFrom(ipaddr, addr.Ipv4.Port) |
| case net.SocketAddressIpv6: |
| ipaddr := netip.AddrFrom16(addr.Ipv6.Address.Addr) |
| return netip.AddrPortFrom(ipaddr, addr.Ipv6.Port) |
| default: |
| panic(fmt.Sprintf("unrecognized SocketAddress variant %d", addr.Which())) |
| } |
| } |
| |
| func (fd *netFD) asAddr(addr net.SocketAddress, err error) sockaddr { |
| if err != nil { |
| return nil |
| } |
| |
| ip, port := socketAddrToIpPort(addr) |
| if isZeros(ip) && port == 0 { |
| return nil |
| } |
| |
| switch fd.sock.(type) { |
| case *zxsocket.DatagramSocket: |
| return &UDPAddr{IP: ip, Port: port} |
| case *zxsocket.StreamSocket: |
| return &TCPAddr{IP: ip, Port: port} |
| default: |
| panic(fmt.Sprintf("unrecognized socket type %T", fd.sock)) |
| } |
| } |
| |
| func (fd *netFD) setAddr(peerAddr sockaddr) { |
| fd.laddr = fd.asAddr(fd.sock.GetSockName()) |
| fd.raddr = peerAddr |
| runtime.SetFinalizer(fd, (*netFD).Close) |
| } |
| |
| func (fd *netFD) name() string { |
| var ls, rs string |
| if fd.laddr != nil { |
| ls = fd.laddr.String() |
| } |
| if fd.raddr != nil { |
| rs = fd.raddr.String() |
| } |
| return fd.net + ":" + ls + "->" + rs |
| } |
| |
| func (fd *netFD) SetDeadline(t time.Time) error { |
| return fd.pfd.SetDeadline(t) |
| } |
| |
| func (fd *netFD) SetReadDeadline(t time.Time) error { |
| return fd.pfd.SetReadDeadline(t) |
| } |
| |
| func (fd *netFD) SetWriteDeadline(t time.Time) error { |
| return fd.pfd.SetWriteDeadline(t) |
| } |