blob: 525ff3229683342315cbdc66eb400f1c52e00785 [file] [log] [blame]
// 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
}