// 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 <SOMAXCONN pending connections; closed when the Listener is closed
	incomingFull  chan []*netFD // holds the queue when it has SOMAXCONN pending connections
	incomingEmpty chan bool     // holds true when the incoming queue is empty
}

func newFakeNetFD(fd *netFD) *fakeNetFD {
	ffd := &fakeNetFD{fd: fd}
	ffd.readDeadline.Store(newDeadlineTimer(noDeadline))
	ffd.writeDeadline.Store(newDeadlineTimer(noDeadline))
	return ffd
}

func (ffd *fakeNetFD) Read(p []byte) (n int, err error) {
	n, _, err = ffd.queue.recvfrom(ffd.readDeadline.Load(), p, false, nil)
	return n, err
}

func (ffd *fakeNetFD) Write(p []byte) (nn int, err error) {
	peer := ffd.peer
	if peer == nil {
		if ffd.fd.raddr == nil {
			return 0, os.NewSyscallError("write", syscall.ENOTCONN)
		}
		peeri, _ := sockets.Load(fakeAddr(ffd.fd.raddr.(sockaddr)))
		if peeri == nil {
			return 0, os.NewSyscallError("write", syscall.ECONNRESET)
		}
		peer = peeri.(*netFD)
		if peer.queue == nil {
			return 0, os.NewSyscallError("write", syscall.ECONNRESET)
		}
	}

	if peer.fakeNetFD == nil {
		return 0, os.NewSyscallError("write", syscall.EINVAL)
	}
	return peer.queue.write(ffd.writeDeadline.Load(), p, ffd.fd.laddr.(sockaddr))
}

func (ffd *fakeNetFD) Close() (err error) {
	if ffd.fakeAddr != (fakeSockAddr{}) {
		sockets.CompareAndDelete(ffd.fakeAddr, ffd.fd)
	}

	if ffd.queue != nil {
		if closeErr := ffd.queue.closeRead(); err == nil {
			err = closeErr
		}
	}
	if ffd.peer != nil {
		if closeErr := ffd.peer.queue.closeWrite(); err == nil {
			err = closeErr
		}
	}
	ffd.readDeadline.Load().Reset(noDeadline)
	ffd.writeDeadline.Load().Reset(noDeadline)

	if ffd.incoming != nil {
		var (
			incoming []*netFD
			ok       bool
		)
		select {
		case _, ok = <-ffd.incomingEmpty:
		case incoming, ok = <-ffd.incoming:
		case incoming, ok = <-ffd.incomingFull:
		}
		if ok {
			// Sends on ffd.incoming require a receive first.
			// Since we successfully received, no other goroutine may
			// send on it at this point, and we may safely close it.
			close(ffd.incoming)

			for _, c := range incoming {
				c.Close()
			}
		}
	}

	if ffd.assignedPort != 0 {
		fakePorts.CompareAndDelete(ffd.assignedPort, ffd.fd)
	}

	return err
}

func (ffd *fakeNetFD) closeRead() error {
	return ffd.queue.closeRead()
}

func (ffd *fakeNetFD) closeWrite() error {
	if ffd.peer == nil {
		return os.NewSyscallError("closeWrite", syscall.ENOTCONN)
	}
	return ffd.peer.queue.closeWrite()
}

func (ffd *fakeNetFD) accept(laddr Addr) (*netFD, error) {
	if ffd.incoming == nil {
		return nil, os.NewSyscallError("accept", syscall.EINVAL)
	}

	var (
		incoming []*netFD
		ok       bool
	)
	select {
	case <-ffd.readDeadline.Load().expired:
		return nil, os.ErrDeadlineExceeded
	case incoming, ok = <-ffd.incoming:
		if !ok {
			return nil, ErrClosed
		}
	case incoming, ok = <-ffd.incomingFull:
	}

	peer := incoming[0]
	incoming = incoming[1:]
	if len(incoming) == 0 {
		ffd.incomingEmpty <- true
	} else {
		ffd.incoming <- incoming
	}
	return peer, nil
}

func (ffd *fakeNetFD) SetDeadline(t time.Time) error {
	err1 := ffd.SetReadDeadline(t)
	err2 := ffd.SetWriteDeadline(t)
	if err1 != nil {
		return err1
	}
	return err2
}

func (ffd *fakeNetFD) SetReadDeadline(t time.Time) error {
	dt := ffd.readDeadline.Load()
	if !dt.Reset(t) {
		ffd.readDeadline.Store(newDeadlineTimer(t))
	}
	return nil
}

func (ffd *fakeNetFD) SetWriteDeadline(t time.Time) error {
	dt := ffd.writeDeadline.Load()
	if !dt.Reset(t) {
		ffd.writeDeadline.Store(newDeadlineTimer(t))
	}
	return nil
}

const maxPacketSize = 65535

type packet struct {
	buf       []byte
	bufOffset int
	next      *packet
	from      sockaddr
}

func (p *packet) clear() {
	p.buf = p.buf[:0]
	p.bufOffset = 0
	p.next = nil
	p.from = nil
}

var packetPool = sync.Pool{
	New: func() any { return new(packet) },
}

type packetQueueState struct {
	head, tail      *packet // unqueued packets
	nBytes          int     // number of bytes enqueued in the packet buffers starting from head
	readBufferBytes int     // soft limit on nbytes; no more packets may be enqueued when the limit is exceeded
	readClosed      bool    // true if the reader of the queue has stopped reading
	writeClosed     bool    // true if the writer of the queue has stopped writing; the reader sees either io.EOF or syscall.ECONNRESET when they have read all buffered packets
	noLinger        bool    // if true, the reader sees ECONNRESET instead of EOF
}

// A packetQueue is a set of 1-buffered channels implementing a FIFO queue
// of packets.
type packetQueue struct {
	empty chan packetQueueState // contains configuration parameters when the queue is empty and not closed
	ready chan packetQueueState // contains the packets when non-empty or closed
	full  chan packetQueueState // contains the packets when buffer is full and not closed
}

func newPacketQueue(readBufferBytes int) *packetQueue {
	pq := &packetQueue{
		empty: make(chan packetQueueState, 1),
		ready: make(chan packetQueueState, 1),
		full:  make(chan packetQueueState, 1),
	}
	pq.put(packetQueueState{
		readBufferBytes: readBufferBytes,
	})
	return pq
}

func (pq *packetQueue) get() packetQueueState {
	var q packetQueueState
	select {
	case q = <-pq.empty:
	case q = <-pq.ready:
	case q = <-pq.full:
	}
	return q
}

func (pq *packetQueue) put(q packetQueueState) {
	switch {
	case q.readClosed || q.writeClosed:
		pq.ready <- q
	case q.nBytes >= 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
}
