1// Copyright 2023 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build js || wasip1 6 7package net 8 9import ( 10 "internal/poll" 11 "runtime" 12 "time" 13) 14 15const ( 16 readSyscallName = "fd_read" 17 writeSyscallName = "fd_write" 18) 19 20// Network file descriptor. 21type netFD struct { 22 pfd poll.FD 23 24 // immutable until Close 25 family int 26 sotype int 27 isConnected bool // handshake completed or use of association with peer 28 net string 29 laddr Addr 30 raddr Addr 31 32 // The only networking available in WASI preview 1 is the ability to 33 // sock_accept on a pre-opened socket, and then fd_read, fd_write, 34 // fd_close, and sock_shutdown on the resulting connection. We 35 // intercept applicable netFD calls on this instance, and then pass 36 // the remainder of the netFD calls to fakeNetFD. 37 *fakeNetFD 38} 39 40func newFD(net string, sysfd int) *netFD { 41 return newPollFD(net, poll.FD{ 42 Sysfd: sysfd, 43 IsStream: true, 44 ZeroReadIsEOF: true, 45 }) 46} 47 48func newPollFD(net string, pfd poll.FD) *netFD { 49 var laddr Addr 50 var raddr Addr 51 // WASI preview 1 does not have functions like getsockname/getpeername, 52 // so we cannot get access to the underlying IP address used by connections. 53 // 54 // However, listeners created by FileListener are of type *TCPListener, 55 // which can be asserted by a Go program. The (*TCPListener).Addr method 56 // documents that the returned value will be of type *TCPAddr, we satisfy 57 // the documented behavior by creating addresses of the expected type here. 58 switch net { 59 case "tcp": 60 laddr = new(TCPAddr) 61 raddr = new(TCPAddr) 62 case "udp": 63 laddr = new(UDPAddr) 64 raddr = new(UDPAddr) 65 default: 66 laddr = unknownAddr{} 67 raddr = unknownAddr{} 68 } 69 return &netFD{ 70 pfd: pfd, 71 net: net, 72 laddr: laddr, 73 raddr: raddr, 74 } 75} 76 77func (fd *netFD) init() error { 78 return fd.pfd.Init(fd.net, true) 79} 80 81func (fd *netFD) name() string { 82 return "unknown" 83} 84 85func (fd *netFD) accept() (netfd *netFD, err error) { 86 if fd.fakeNetFD != nil { 87 return fd.fakeNetFD.accept(fd.laddr) 88 } 89 d, _, errcall, err := fd.pfd.Accept() 90 if err != nil { 91 if errcall != "" { 92 err = wrapSyscallError(errcall, err) 93 } 94 return nil, err 95 } 96 netfd = newFD("tcp", d) 97 if err = netfd.init(); err != nil { 98 netfd.Close() 99 return nil, err 100 } 101 return netfd, nil 102} 103 104func (fd *netFD) setAddr(laddr, raddr Addr) { 105 fd.laddr = laddr 106 fd.raddr = raddr 107 runtime.SetFinalizer(fd, (*netFD).Close) 108} 109 110func (fd *netFD) Close() error { 111 if fd.fakeNetFD != nil { 112 return fd.fakeNetFD.Close() 113 } 114 runtime.SetFinalizer(fd, nil) 115 return fd.pfd.Close() 116} 117 118func (fd *netFD) shutdown(how int) error { 119 if fd.fakeNetFD != nil { 120 return nil 121 } 122 err := fd.pfd.Shutdown(how) 123 runtime.KeepAlive(fd) 124 return wrapSyscallError("shutdown", err) 125} 126 127func (fd *netFD) Read(p []byte) (n int, err error) { 128 if fd.fakeNetFD != nil { 129 return fd.fakeNetFD.Read(p) 130 } 131 n, err = fd.pfd.Read(p) 132 runtime.KeepAlive(fd) 133 return n, wrapSyscallError(readSyscallName, err) 134} 135 136func (fd *netFD) Write(p []byte) (nn int, err error) { 137 if fd.fakeNetFD != nil { 138 return fd.fakeNetFD.Write(p) 139 } 140 nn, err = fd.pfd.Write(p) 141 runtime.KeepAlive(fd) 142 return nn, wrapSyscallError(writeSyscallName, err) 143} 144 145func (fd *netFD) SetDeadline(t time.Time) error { 146 if fd.fakeNetFD != nil { 147 return fd.fakeNetFD.SetDeadline(t) 148 } 149 return fd.pfd.SetDeadline(t) 150} 151 152func (fd *netFD) SetReadDeadline(t time.Time) error { 153 if fd.fakeNetFD != nil { 154 return fd.fakeNetFD.SetReadDeadline(t) 155 } 156 return fd.pfd.SetReadDeadline(t) 157} 158 159func (fd *netFD) SetWriteDeadline(t time.Time) error { 160 if fd.fakeNetFD != nil { 161 return fd.fakeNetFD.SetWriteDeadline(t) 162 } 163 return fd.pfd.SetWriteDeadline(t) 164} 165 166type unknownAddr struct{} 167 168func (unknownAddr) Network() string { return "unknown" } 169func (unknownAddr) String() string { return "unknown" } 170