// Copyright 2018 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. // Fake networking for js/wasm and wasip1/wasm. // It is intended to allow tests of other package to pass. //go:build js || wasip1 package net import ( "context" "errors" "io" "os" "runtime" "sync" "sync/atomic" "syscall" "time" ) var ( sockets sync.Map // fakeSockAddr → *netFD fakeSocketIDs sync.Map // fakeNetFD.id → *netFD fakePorts sync.Map // int (port #) → *netFD nextPortCounter atomic.Int32 ) const defaultBuffer = 65535 type fakeSockAddr struct { family int address string } func fakeAddr(sa sockaddr) fakeSockAddr { return fakeSockAddr{ family: sa.family(), address: sa.String(), } } // socket returns a network file descriptor that is ready for // I/O using the fake network. func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (*netFD, error) { if raddr != nil && ctrlCtxFn != nil { return nil, os.NewSyscallError("socket", syscall.ENOTSUP) } switch sotype { case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET, syscall.SOCK_DGRAM: default: return nil, os.NewSyscallError("socket", syscall.ENOTSUP) } fd := &netFD{ family: family, sotype: sotype, net: net, } fd.fakeNetFD = newFakeNetFD(fd) if raddr == nil { if err := fakeListen(fd, laddr); err != nil { fd.Close() return nil, err } return fd, nil } if err := fakeConnect(ctx, fd, laddr, raddr); err != nil { fd.Close() return nil, err } return fd, nil } func validateResolvedAddr(net string, family int, sa sockaddr) error { validateIP := func(ip IP) error { switch family { case syscall.AF_INET: if len(ip) != 4 { return &AddrError{ Err: "non-IPv4 address", Addr: ip.String(), } } case syscall.AF_INET6: if len(ip) != 16 { return &AddrError{ Err: "non-IPv6 address", Addr: ip.String(), } } default: panic("net: unexpected address family in validateResolvedAddr") } return nil } switch net { case "tcp", "tcp4", "tcp6": sa, ok := sa.(*TCPAddr) if !ok { return &AddrError{ Err: "non-TCP address for " + net + " network", Addr: sa.String(), } } if err := validateIP(sa.IP); err != nil { return err } if sa.Port <= 0 || sa.Port >= 1<<16 { return &AddrError{ Err: "port out of range", Addr: sa.String(), } } return nil case "udp", "udp4", "udp6": sa, ok := sa.(*UDPAddr) if !ok { return &AddrError{ Err: "non-UDP address for " + net + " network", Addr: sa.String(), } } if err := validateIP(sa.IP); err != nil { return err } if sa.Port <= 0 || sa.Port >= 1<<16 { return &AddrError{ Err: "port out of range", Addr: sa.String(), } } return nil case "unix", "unixgram", "unixpacket": sa, ok := sa.(*UnixAddr) if !ok { return &AddrError{ Err: "non-Unix address for " + net + " network", Addr: sa.String(), } } if sa.Name != "" { i := len(sa.Name) - 1 for i > 0 && !os.IsPathSeparator(sa.Name[i]) { i-- } for i > 0 && os.IsPathSeparator(sa.Name[i]) { i-- } if i <= 0 { return &AddrError{ Err: "unix socket name missing path component", Addr: sa.Name, } } if _, err := os.Stat(sa.Name[:i+1]); err != nil { return &AddrError{ Err: err.Error(), Addr: sa.Name, } } } return nil default: return &AddrError{ Err: syscall.EAFNOSUPPORT.Error(), Addr: sa.String(), } } } func matchIPFamily(family int, addr sockaddr) sockaddr { convertIP := func(ip IP) IP { switch family { case syscall.AF_INET: return ip.To4() case syscall.AF_INET6: return ip.To16() default: return ip } } switch addr := addr.(type) { case *TCPAddr: ip := convertIP(addr.IP) if ip == nil || len(ip) == len(addr.IP) { return addr } return &TCPAddr{IP: ip, Port: addr.Port, Zone: addr.Zone} case *UDPAddr: ip := convertIP(addr.IP) if ip == nil || len(ip) == len(addr.IP) { return addr } return &UDPAddr{IP: ip, Port: addr.Port, Zone: addr.Zone} default: return addr } } type fakeNetFD struct { fd *netFD assignedPort int // 0 if no port has been assigned for this socket queue *packetQueue // incoming packets peer *netFD // connected peer (for outgoing packets); nil for listeners and PacketConns readDeadline atomic.Pointer[deadlineTimer] writeDeadline atomic.Pointer[deadlineTimer] fakeAddr fakeSockAddr // cached fakeSockAddr equivalent of fd.laddr // The incoming channels hold incoming connections that have not yet been accepted. // All of these channels are 1-buffered. incoming chan []*netFD // holds the queue when it has >0 but = q.readBufferBytes: pq.full <- q case q.head == nil: if q.nBytes > 0 { defer panic("net: put with nil packet list and nonzero nBytes") } pq.empty <- q default: pq.ready <- q } } func (pq *packetQueue) closeRead() error { q := pq.get() // Discard any unread packets. for q.head != nil { p := q.head q.head = p.next p.clear() packetPool.Put(p) } q.nBytes = 0 q.readClosed = true pq.put(q) return nil } func (pq *packetQueue) closeWrite() error { q := pq.get() q.writeClosed = true pq.put(q) return nil } func (pq *packetQueue) setLinger(linger bool) error { q := pq.get() defer func() { pq.put(q) }() if q.writeClosed { return ErrClosed } q.noLinger = !linger return nil } func (pq *packetQueue) write(dt *deadlineTimer, b []byte, from sockaddr) (n int, err error) { for { dn := len(b) if dn > maxPacketSize { dn = maxPacketSize } dn, err = pq.send(dt, b[:dn], from, true) n += dn if err != nil { return n, err } b = b[dn:] if len(b) == 0 { return n, nil } } } func (pq *packetQueue) send(dt *deadlineTimer, b []byte, from sockaddr, block bool) (n int, err error) { if from == nil { return 0, os.NewSyscallError("send", syscall.EINVAL) } if len(b) > maxPacketSize { return 0, os.NewSyscallError("send", syscall.EMSGSIZE) } var q packetQueueState var full chan packetQueueState if !block { full = pq.full } // Before we check dt.expired, yield to other goroutines. // This may help to prevent starvation of the goroutine that runs the // deadlineTimer's time.After callback. // // TODO(#65178): Remove this when the runtime scheduler no longer starves // runnable goroutines. runtime.Gosched() select { case <-dt.expired: return 0, os.ErrDeadlineExceeded case q = <-full: pq.put(q) return 0, os.NewSyscallError("send", syscall.ENOBUFS) case q = <-pq.empty: case q = <-pq.ready: } defer func() { pq.put(q) }() // Don't allow a packet to be sent if the deadline has expired, // even if the select above chose a different branch. select { case <-dt.expired: return 0, os.ErrDeadlineExceeded default: } if q.writeClosed { return 0, ErrClosed } else if q.readClosed { return 0, os.NewSyscallError("send", syscall.ECONNRESET) } p := packetPool.Get().(*packet) p.buf = append(p.buf[:0], b...) p.from = from if q.head == nil { q.head = p } else { q.tail.next = p } q.tail = p q.nBytes += len(p.buf) return len(b), nil } func (pq *packetQueue) recvfrom(dt *deadlineTimer, b []byte, wholePacket bool, checkFrom func(sockaddr) error) (n int, from sockaddr, err error) { var q packetQueueState var empty chan packetQueueState if len(b) == 0 { // For consistency with the implementation on Unix platforms, // allow a zero-length Read to proceed if the queue is empty. // (Without this, TestZeroByteRead deadlocks.) empty = pq.empty } // Before we check dt.expired, yield to other goroutines. // This may help to prevent starvation of the goroutine that runs the // deadlineTimer's time.After callback. // // TODO(#65178): Remove this when the runtime scheduler no longer starves // runnable goroutines. runtime.Gosched() select { case <-dt.expired: return 0, nil, os.ErrDeadlineExceeded case q = <-empty: case q = <-pq.ready: case q = <-pq.full: } defer func() { pq.put(q) }() p := q.head if p == nil { switch { case q.readClosed: return 0, nil, ErrClosed case q.writeClosed: if q.noLinger { return 0, nil, os.NewSyscallError("recvfrom", syscall.ECONNRESET) } return 0, nil, io.EOF case len(b) == 0: return 0, nil, nil default: // This should be impossible: pq.full should only contain a non-empty list, // pq.ready should either contain a non-empty list or indicate that the // connection is closed, and we should only receive from pq.empty if // len(b) == 0. panic("net: nil packet list from non-closed packetQueue") } } select { case <-dt.expired: return 0, nil, os.ErrDeadlineExceeded default: } if checkFrom != nil { if err := checkFrom(p.from); err != nil { return 0, nil, err } } n = copy(b, p.buf[p.bufOffset:]) from = p.from if wholePacket || p.bufOffset+n == len(p.buf) { q.head = p.next q.nBytes -= len(p.buf) p.clear() packetPool.Put(p) } else { p.bufOffset += n } return n, from, nil } // setReadBuffer sets a soft limit on the number of bytes available to read // from the pipe. func (pq *packetQueue) setReadBuffer(bytes int) error { if bytes <= 0 { return os.NewSyscallError("setReadBuffer", syscall.EINVAL) } q := pq.get() // Use the queue as a lock. q.readBufferBytes = bytes pq.put(q) return nil } type deadlineTimer struct { timer chan *time.Timer expired chan struct{} } func newDeadlineTimer(deadline time.Time) *deadlineTimer { dt := &deadlineTimer{ timer: make(chan *time.Timer, 1), expired: make(chan struct{}), } dt.timer <- nil dt.Reset(deadline) return dt } // Reset attempts to reset the timer. // If the timer has already expired, Reset returns false. func (dt *deadlineTimer) Reset(deadline time.Time) bool { timer := <-dt.timer defer func() { dt.timer <- timer }() if deadline.Equal(noDeadline) { if timer != nil && timer.Stop() { timer = nil } return timer == nil } d := time.Until(deadline) if d < 0 { // Ensure that a deadline in the past takes effect immediately. defer func() { <-dt.expired }() } if timer == nil { timer = time.AfterFunc(d, func() { close(dt.expired) }) return true } if !timer.Stop() { return false } timer.Reset(d) return true } func sysSocket(family, sotype, proto int) (int, error) { return 0, os.NewSyscallError("sysSocket", syscall.ENOSYS) } func fakeListen(fd *netFD, laddr sockaddr) (err error) { wrapErr := func(err error) error { if errno, ok := err.(syscall.Errno); ok { err = os.NewSyscallError("listen", errno) } if errors.Is(err, syscall.EADDRINUSE) { return err } if laddr != nil { if _, ok := err.(*AddrError); !ok { err = &AddrError{ Err: err.Error(), Addr: laddr.String(), } } } return err } ffd := newFakeNetFD(fd) defer func() { if fd.fakeNetFD != ffd { // Failed to register listener; clean up. ffd.Close() } }() if err := ffd.assignFakeAddr(matchIPFamily(fd.family, laddr)); err != nil { return wrapErr(err) } ffd.fakeAddr = fakeAddr(fd.laddr.(sockaddr)) switch fd.sotype { case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET: ffd.incoming = make(chan []*netFD, 1) ffd.incomingFull = make(chan []*netFD, 1) ffd.incomingEmpty = make(chan bool, 1) ffd.incomingEmpty <- true case syscall.SOCK_DGRAM: ffd.queue = newPacketQueue(defaultBuffer) default: return wrapErr(syscall.EINVAL) } fd.fakeNetFD = ffd if _, dup := sockets.LoadOrStore(ffd.fakeAddr, fd); dup { fd.fakeNetFD = nil return wrapErr(syscall.EADDRINUSE) } return nil } func fakeConnect(ctx context.Context, fd *netFD, laddr, raddr sockaddr) error { wrapErr := func(err error) error { if errno, ok := err.(syscall.Errno); ok { err = os.NewSyscallError("connect", errno) } if errors.Is(err, syscall.EADDRINUSE) { return err } if terr, ok := err.(interface{ Timeout() bool }); !ok || !terr.Timeout() { // For consistency with the net implementation on other platforms, // if we don't need to preserve the Timeout-ness of err we should // wrap it in an AddrError. (Unfortunately we can't wrap errors // that convey structured information, because AddrError reduces // the wrapped Err to a flat string.) if _, ok := err.(*AddrError); !ok { err = &AddrError{ Err: err.Error(), Addr: raddr.String(), } } } return err } if fd.isConnected { return wrapErr(syscall.EISCONN) } if ctx.Err() != nil { return wrapErr(syscall.ETIMEDOUT) } fd.raddr = matchIPFamily(fd.family, raddr) if err := validateResolvedAddr(fd.net, fd.family, fd.raddr.(sockaddr)); err != nil { return wrapErr(err) } if err := fd.fakeNetFD.assignFakeAddr(laddr); err != nil { return wrapErr(err) } fd.fakeNetFD.queue = newPacketQueue(defaultBuffer) switch fd.sotype { case syscall.SOCK_DGRAM: if ua, ok := fd.laddr.(*UnixAddr); !ok || ua.Name != "" { fd.fakeNetFD.fakeAddr = fakeAddr(fd.laddr.(sockaddr)) if _, dup := sockets.LoadOrStore(fd.fakeNetFD.fakeAddr, fd); dup { return wrapErr(syscall.EADDRINUSE) } } fd.isConnected = true return nil case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET: default: return wrapErr(syscall.EINVAL) } fa := fakeAddr(raddr) lni, ok := sockets.Load(fa) if !ok { return wrapErr(syscall.ECONNREFUSED) } ln := lni.(*netFD) if ln.sotype != fd.sotype { return wrapErr(syscall.EPROTOTYPE) } if ln.incoming == nil { return wrapErr(syscall.ECONNREFUSED) } peer := &netFD{ family: ln.family, sotype: ln.sotype, net: ln.net, laddr: ln.laddr, raddr: fd.laddr, isConnected: true, } peer.fakeNetFD = newFakeNetFD(fd) peer.fakeNetFD.queue = newPacketQueue(defaultBuffer) defer func() { if fd.peer != peer { // Failed to connect; clean up. peer.Close() } }() var incoming []*netFD select { case <-ctx.Done(): return wrapErr(syscall.ETIMEDOUT) case ok = <-ln.incomingEmpty: case incoming, ok = <-ln.incoming: } if !ok { return wrapErr(syscall.ECONNREFUSED) } fd.isConnected = true fd.peer = peer peer.peer = fd incoming = append(incoming, peer) if len(incoming) >= listenerBacklog() { ln.incomingFull <- incoming } else { ln.incoming <- incoming } return nil } func (ffd *fakeNetFD) assignFakeAddr(addr sockaddr) error { validate := func(sa sockaddr) error { if err := validateResolvedAddr(ffd.fd.net, ffd.fd.family, sa); err != nil { return err } ffd.fd.laddr = sa return nil } assignIP := func(addr sockaddr) error { var ( ip IP port int zone string ) switch addr := addr.(type) { case *TCPAddr: if addr != nil { ip = addr.IP port = addr.Port zone = addr.Zone } case *UDPAddr: if addr != nil { ip = addr.IP port = addr.Port zone = addr.Zone } default: return validate(addr) } if ip == nil { ip = IPv4(127, 0, 0, 1) } switch ffd.fd.family { case syscall.AF_INET: if ip4 := ip.To4(); ip4 != nil { ip = ip4 } case syscall.AF_INET6: if ip16 := ip.To16(); ip16 != nil { ip = ip16 } } if ip == nil { return syscall.EINVAL } if port == 0 { var prevPort int32 portWrapped := false nextPort := func() (int, bool) { for { port := nextPortCounter.Add(1) if port <= 0 || port >= 1<<16 { // nextPortCounter ran off the end of the port space. // Bump it back into range. for { if nextPortCounter.CompareAndSwap(port, 0) { break } if port = nextPortCounter.Load(); port >= 0 && port < 1<<16 { break } } if portWrapped { // This is the second wraparound, so we've scanned the whole port space // at least once already and it's time to give up. return 0, false } portWrapped = true prevPort = 0 continue } if port <= prevPort { // nextPortCounter has wrapped around since the last time we read it. if portWrapped { // This is the second wraparound, so we've scanned the whole port space // at least once already and it's time to give up. return 0, false } else { portWrapped = true } } prevPort = port return int(port), true } } for { var ok bool port, ok = nextPort() if !ok { ffd.assignedPort = 0 return syscall.EADDRINUSE } ffd.assignedPort = int(port) if _, dup := fakePorts.LoadOrStore(ffd.assignedPort, ffd.fd); !dup { break } } } switch addr.(type) { case *TCPAddr: return validate(&TCPAddr{IP: ip, Port: port, Zone: zone}) case *UDPAddr: return validate(&UDPAddr{IP: ip, Port: port, Zone: zone}) default: panic("unreachable") } } switch ffd.fd.net { case "tcp", "tcp4", "tcp6": if addr == nil { return assignIP(new(TCPAddr)) } return assignIP(addr) case "udp", "udp4", "udp6": if addr == nil { return assignIP(new(UDPAddr)) } return assignIP(addr) case "unix", "unixgram", "unixpacket": uaddr, ok := addr.(*UnixAddr) if !ok && addr != nil { return &AddrError{ Err: "non-Unix address for " + ffd.fd.net + " network", Addr: addr.String(), } } if uaddr == nil { return validate(&UnixAddr{Net: ffd.fd.net}) } return validate(&UnixAddr{Net: ffd.fd.net, Name: uaddr.Name}) default: return &AddrError{ Err: syscall.EAFNOSUPPORT.Error(), Addr: addr.String(), } } } func (ffd *fakeNetFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { if ffd.queue == nil { return 0, nil, os.NewSyscallError("readFrom", syscall.EINVAL) } n, from, err := ffd.queue.recvfrom(ffd.readDeadline.Load(), p, true, nil) if from != nil { // Convert the net.sockaddr to a syscall.Sockaddr type. var saErr error sa, saErr = from.sockaddr(ffd.fd.family) if err == nil { err = saErr } } return n, sa, err } func (ffd *fakeNetFD) readFromInet4(p []byte, sa *syscall.SockaddrInet4) (n int, err error) { n, _, err = ffd.queue.recvfrom(ffd.readDeadline.Load(), p, true, func(from sockaddr) error { fromSA, err := from.sockaddr(syscall.AF_INET) if err != nil { return err } if fromSA == nil { return os.NewSyscallError("readFromInet4", syscall.EINVAL) } *sa = *(fromSA.(*syscall.SockaddrInet4)) return nil }) return n, err } func (ffd *fakeNetFD) readFromInet6(p []byte, sa *syscall.SockaddrInet6) (n int, err error) { n, _, err = ffd.queue.recvfrom(ffd.readDeadline.Load(), p, true, func(from sockaddr) error { fromSA, err := from.sockaddr(syscall.AF_INET6) if err != nil { return err } if fromSA == nil { return os.NewSyscallError("readFromInet6", syscall.EINVAL) } *sa = *(fromSA.(*syscall.SockaddrInet6)) return nil }) return n, err } func (ffd *fakeNetFD) readMsg(p []byte, oob []byte, flags int) (n, oobn, retflags int, sa syscall.Sockaddr, err error) { if flags != 0 { return 0, 0, 0, nil, os.NewSyscallError("readMsg", syscall.ENOTSUP) } n, sa, err = ffd.readFrom(p) return n, 0, 0, sa, err } func (ffd *fakeNetFD) readMsgInet4(p []byte, oob []byte, flags int, sa *syscall.SockaddrInet4) (n, oobn, retflags int, err error) { if flags != 0 { return 0, 0, 0, os.NewSyscallError("readMsgInet4", syscall.ENOTSUP) } n, err = ffd.readFromInet4(p, sa) return n, 0, 0, err } func (ffd *fakeNetFD) readMsgInet6(p []byte, oob []byte, flags int, sa *syscall.SockaddrInet6) (n, oobn, retflags int, err error) { if flags != 0 { return 0, 0, 0, os.NewSyscallError("readMsgInet6", syscall.ENOTSUP) } n, err = ffd.readFromInet6(p, sa) return n, 0, 0, err } func (ffd *fakeNetFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { if len(oob) > 0 { return 0, 0, os.NewSyscallError("writeMsg", syscall.ENOTSUP) } n, err = ffd.writeTo(p, sa) return n, 0, err } func (ffd *fakeNetFD) writeMsgInet4(p []byte, oob []byte, sa *syscall.SockaddrInet4) (n int, oobn int, err error) { return ffd.writeMsg(p, oob, sa) } func (ffd *fakeNetFD) writeMsgInet6(p []byte, oob []byte, sa *syscall.SockaddrInet6) (n int, oobn int, err error) { return ffd.writeMsg(p, oob, sa) } func (ffd *fakeNetFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) { raddr := ffd.fd.raddr if sa != nil { if ffd.fd.isConnected { return 0, os.NewSyscallError("writeTo", syscall.EISCONN) } raddr = ffd.fd.addrFunc()(sa) } if raddr == nil { return 0, os.NewSyscallError("writeTo", syscall.EINVAL) } peeri, _ := sockets.Load(fakeAddr(raddr.(sockaddr))) if peeri == nil { if len(ffd.fd.net) >= 3 && ffd.fd.net[:3] == "udp" { return len(p), nil } return 0, os.NewSyscallError("writeTo", syscall.ECONNRESET) } peer := peeri.(*netFD) if peer.queue == nil { if len(ffd.fd.net) >= 3 && ffd.fd.net[:3] == "udp" { return len(p), nil } return 0, os.NewSyscallError("writeTo", syscall.ECONNRESET) } block := true if len(ffd.fd.net) >= 3 && ffd.fd.net[:3] == "udp" { block = false } return peer.queue.send(ffd.writeDeadline.Load(), p, ffd.fd.laddr.(sockaddr), block) } func (ffd *fakeNetFD) writeToInet4(p []byte, sa *syscall.SockaddrInet4) (n int, err error) { return ffd.writeTo(p, sa) } func (ffd *fakeNetFD) writeToInet6(p []byte, sa *syscall.SockaddrInet6) (n int, err error) { return ffd.writeTo(p, sa) } func (ffd *fakeNetFD) dup() (f *os.File, err error) { return nil, os.NewSyscallError("dup", syscall.ENOSYS) } func (ffd *fakeNetFD) setReadBuffer(bytes int) error { if ffd.queue == nil { return os.NewSyscallError("setReadBuffer", syscall.EINVAL) } ffd.queue.setReadBuffer(bytes) return nil } func (ffd *fakeNetFD) setWriteBuffer(bytes int) error { return os.NewSyscallError("setWriteBuffer", syscall.ENOTSUP) } func (ffd *fakeNetFD) setLinger(sec int) error { if sec < 0 || ffd.peer == nil { return os.NewSyscallError("setLinger", syscall.EINVAL) } ffd.peer.queue.setLinger(sec > 0) return nil }