| // Copyright 2013 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 !js | |
| // +build !js | |
| package net | |
| import ( | |
| "errors" | |
| "fmt" | |
| "os" | |
| "sync" | |
| "testing" | |
| "time" | |
| ) | |
| // testUnixAddr uses os.CreateTemp to get a name that is unique. | |
| func testUnixAddr() string { | |
| f, err := os.CreateTemp("", "go-nettest") | |
| if err != nil { | |
| panic(err) | |
| } | |
| addr := f.Name() | |
| f.Close() | |
| os.Remove(addr) | |
| return addr | |
| } | |
| func newLocalListener(network string) (Listener, error) { | |
| switch network { | |
| case "tcp": | |
| if supportsIPv4() { | |
| if ln, err := Listen("tcp4", "127.0.0.1:0"); err == nil { | |
| return ln, nil | |
| } | |
| } | |
| if supportsIPv6() { | |
| return Listen("tcp6", "[::1]:0") | |
| } | |
| case "tcp4": | |
| if supportsIPv4() { | |
| return Listen("tcp4", "127.0.0.1:0") | |
| } | |
| case "tcp6": | |
| if supportsIPv6() { | |
| return Listen("tcp6", "[::1]:0") | |
| } | |
| case "unix", "unixpacket": | |
| return Listen(network, testUnixAddr()) | |
| } | |
| return nil, fmt.Errorf("%s is not supported", network) | |
| } | |
| func newDualStackListener() (lns []*TCPListener, err error) { | |
| var args = []struct { | |
| network string | |
| TCPAddr | |
| }{ | |
| {"tcp4", TCPAddr{IP: IPv4(127, 0, 0, 1)}}, | |
| {"tcp6", TCPAddr{IP: IPv6loopback}}, | |
| } | |
| for i := 0; i < 64; i++ { | |
| var port int | |
| var lns []*TCPListener | |
| for _, arg := range args { | |
| arg.TCPAddr.Port = port | |
| ln, err := ListenTCP(arg.network, &arg.TCPAddr) | |
| if err != nil { | |
| continue | |
| } | |
| port = ln.Addr().(*TCPAddr).Port | |
| lns = append(lns, ln) | |
| } | |
| if len(lns) != len(args) { | |
| for _, ln := range lns { | |
| ln.Close() | |
| } | |
| continue | |
| } | |
| return lns, nil | |
| } | |
| return nil, errors.New("no dualstack port available") | |
| } | |
| type localServer struct { | |
| lnmu sync.RWMutex | |
| Listener | |
| done chan bool // signal that indicates server stopped | |
| cl []Conn // accepted connection list | |
| } | |
| func (ls *localServer) buildup(handler func(*localServer, Listener)) error { | |
| go func() { | |
| handler(ls, ls.Listener) | |
| close(ls.done) | |
| }() | |
| return nil | |
| } | |
| func (ls *localServer) teardown() error { | |
| ls.lnmu.Lock() | |
| defer ls.lnmu.Unlock() | |
| if ls.Listener != nil { | |
| network := ls.Listener.Addr().Network() | |
| address := ls.Listener.Addr().String() | |
| ls.Listener.Close() | |
| for _, c := range ls.cl { | |
| if err := c.Close(); err != nil { | |
| return err | |
| } | |
| } | |
| <-ls.done | |
| ls.Listener = nil | |
| switch network { | |
| case "unix", "unixpacket": | |
| os.Remove(address) | |
| } | |
| } | |
| return nil | |
| } | |
| func newLocalServer(network string) (*localServer, error) { | |
| ln, err := newLocalListener(network) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return &localServer{Listener: ln, done: make(chan bool)}, nil | |
| } | |
| type streamListener struct { | |
| network, address string | |
| Listener | |
| done chan bool // signal that indicates server stopped | |
| } | |
| func (sl *streamListener) newLocalServer() (*localServer, error) { | |
| return &localServer{Listener: sl.Listener, done: make(chan bool)}, nil | |
| } | |
| type dualStackServer struct { | |
| lnmu sync.RWMutex | |
| lns []streamListener | |
| port string | |
| cmu sync.RWMutex | |
| cs []Conn // established connections at the passive open side | |
| } | |
| func (dss *dualStackServer) buildup(handler func(*dualStackServer, Listener)) error { | |
| for i := range dss.lns { | |
| go func(i int) { | |
| handler(dss, dss.lns[i].Listener) | |
| close(dss.lns[i].done) | |
| }(i) | |
| } | |
| return nil | |
| } | |
| func (dss *dualStackServer) teardownNetwork(network string) error { | |
| dss.lnmu.Lock() | |
| for i := range dss.lns { | |
| if network == dss.lns[i].network && dss.lns[i].Listener != nil { | |
| dss.lns[i].Listener.Close() | |
| <-dss.lns[i].done | |
| dss.lns[i].Listener = nil | |
| } | |
| } | |
| dss.lnmu.Unlock() | |
| return nil | |
| } | |
| func (dss *dualStackServer) teardown() error { | |
| dss.lnmu.Lock() | |
| for i := range dss.lns { | |
| if dss.lns[i].Listener != nil { | |
| dss.lns[i].Listener.Close() | |
| <-dss.lns[i].done | |
| } | |
| } | |
| dss.lns = dss.lns[:0] | |
| dss.lnmu.Unlock() | |
| dss.cmu.Lock() | |
| for _, c := range dss.cs { | |
| c.Close() | |
| } | |
| dss.cs = dss.cs[:0] | |
| dss.cmu.Unlock() | |
| return nil | |
| } | |
| func newDualStackServer() (*dualStackServer, error) { | |
| lns, err := newDualStackListener() | |
| if err != nil { | |
| return nil, err | |
| } | |
| _, port, err := SplitHostPort(lns[0].Addr().String()) | |
| if err != nil { | |
| lns[0].Close() | |
| lns[1].Close() | |
| return nil, err | |
| } | |
| return &dualStackServer{ | |
| lns: []streamListener{ | |
| {network: "tcp4", address: lns[0].Addr().String(), Listener: lns[0], done: make(chan bool)}, | |
| {network: "tcp6", address: lns[1].Addr().String(), Listener: lns[1], done: make(chan bool)}, | |
| }, | |
| port: port, | |
| }, nil | |
| } | |
| func (ls *localServer) transponder(ln Listener, ch chan<- error) { | |
| defer close(ch) | |
| switch ln := ln.(type) { | |
| case *TCPListener: | |
| ln.SetDeadline(time.Now().Add(someTimeout)) | |
| case *UnixListener: | |
| ln.SetDeadline(time.Now().Add(someTimeout)) | |
| } | |
| c, err := ln.Accept() | |
| if err != nil { | |
| if perr := parseAcceptError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| ls.cl = append(ls.cl, c) | |
| network := ln.Addr().Network() | |
| if c.LocalAddr().Network() != network || c.RemoteAddr().Network() != network { | |
| ch <- fmt.Errorf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network) | |
| return | |
| } | |
| c.SetDeadline(time.Now().Add(someTimeout)) | |
| c.SetReadDeadline(time.Now().Add(someTimeout)) | |
| c.SetWriteDeadline(time.Now().Add(someTimeout)) | |
| b := make([]byte, 256) | |
| n, err := c.Read(b) | |
| if err != nil { | |
| if perr := parseReadError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| if _, err := c.Write(b[:n]); err != nil { | |
| if perr := parseWriteError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| } | |
| func transceiver(c Conn, wb []byte, ch chan<- error) { | |
| defer close(ch) | |
| c.SetDeadline(time.Now().Add(someTimeout)) | |
| c.SetReadDeadline(time.Now().Add(someTimeout)) | |
| c.SetWriteDeadline(time.Now().Add(someTimeout)) | |
| n, err := c.Write(wb) | |
| if err != nil { | |
| if perr := parseWriteError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| if n != len(wb) { | |
| ch <- fmt.Errorf("wrote %d; want %d", n, len(wb)) | |
| } | |
| rb := make([]byte, len(wb)) | |
| n, err = c.Read(rb) | |
| if err != nil { | |
| if perr := parseReadError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| if n != len(wb) { | |
| ch <- fmt.Errorf("read %d; want %d", n, len(wb)) | |
| } | |
| } | |
| func timeoutReceiver(c Conn, d, min, max time.Duration, ch chan<- error) { | |
| var err error | |
| defer func() { ch <- err }() | |
| t0 := time.Now() | |
| if err = c.SetReadDeadline(time.Now().Add(d)); err != nil { | |
| return | |
| } | |
| b := make([]byte, 256) | |
| var n int | |
| n, err = c.Read(b) | |
| t1 := time.Now() | |
| if n != 0 || err == nil || !err.(Error).Timeout() { | |
| err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err) | |
| return | |
| } | |
| if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { | |
| err = fmt.Errorf("Read took %s; expected %s", dt, d) | |
| return | |
| } | |
| } | |
| func timeoutTransmitter(c Conn, d, min, max time.Duration, ch chan<- error) { | |
| var err error | |
| defer func() { ch <- err }() | |
| t0 := time.Now() | |
| if err = c.SetWriteDeadline(time.Now().Add(d)); err != nil { | |
| return | |
| } | |
| var n int | |
| for { | |
| n, err = c.Write([]byte("TIMEOUT TRANSMITTER")) | |
| if err != nil { | |
| break | |
| } | |
| } | |
| t1 := time.Now() | |
| if err == nil || !err.(Error).Timeout() { | |
| err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err) | |
| return | |
| } | |
| if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { | |
| err = fmt.Errorf("Write took %s; expected %s", dt, d) | |
| return | |
| } | |
| } | |
| func newLocalPacketListener(network string) (PacketConn, error) { | |
| switch network { | |
| case "udp": | |
| if supportsIPv4() { | |
| return ListenPacket("udp4", "127.0.0.1:0") | |
| } | |
| if supportsIPv6() { | |
| return ListenPacket("udp6", "[::1]:0") | |
| } | |
| case "udp4": | |
| if supportsIPv4() { | |
| return ListenPacket("udp4", "127.0.0.1:0") | |
| } | |
| case "udp6": | |
| if supportsIPv6() { | |
| return ListenPacket("udp6", "[::1]:0") | |
| } | |
| case "unixgram": | |
| return ListenPacket(network, testUnixAddr()) | |
| } | |
| return nil, fmt.Errorf("%s is not supported", network) | |
| } | |
| func newDualStackPacketListener() (cs []*UDPConn, err error) { | |
| var args = []struct { | |
| network string | |
| UDPAddr | |
| }{ | |
| {"udp4", UDPAddr{IP: IPv4(127, 0, 0, 1)}}, | |
| {"udp6", UDPAddr{IP: IPv6loopback}}, | |
| } | |
| for i := 0; i < 64; i++ { | |
| var port int | |
| var cs []*UDPConn | |
| for _, arg := range args { | |
| arg.UDPAddr.Port = port | |
| c, err := ListenUDP(arg.network, &arg.UDPAddr) | |
| if err != nil { | |
| continue | |
| } | |
| port = c.LocalAddr().(*UDPAddr).Port | |
| cs = append(cs, c) | |
| } | |
| if len(cs) != len(args) { | |
| for _, c := range cs { | |
| c.Close() | |
| } | |
| continue | |
| } | |
| return cs, nil | |
| } | |
| return nil, errors.New("no dualstack port available") | |
| } | |
| type localPacketServer struct { | |
| pcmu sync.RWMutex | |
| PacketConn | |
| done chan bool // signal that indicates server stopped | |
| } | |
| func (ls *localPacketServer) buildup(handler func(*localPacketServer, PacketConn)) error { | |
| go func() { | |
| handler(ls, ls.PacketConn) | |
| close(ls.done) | |
| }() | |
| return nil | |
| } | |
| func (ls *localPacketServer) teardown() error { | |
| ls.pcmu.Lock() | |
| if ls.PacketConn != nil { | |
| network := ls.PacketConn.LocalAddr().Network() | |
| address := ls.PacketConn.LocalAddr().String() | |
| ls.PacketConn.Close() | |
| <-ls.done | |
| ls.PacketConn = nil | |
| switch network { | |
| case "unixgram": | |
| os.Remove(address) | |
| } | |
| } | |
| ls.pcmu.Unlock() | |
| return nil | |
| } | |
| func newLocalPacketServer(network string) (*localPacketServer, error) { | |
| c, err := newLocalPacketListener(network) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return &localPacketServer{PacketConn: c, done: make(chan bool)}, nil | |
| } | |
| type packetListener struct { | |
| PacketConn | |
| } | |
| func (pl *packetListener) newLocalServer() (*localPacketServer, error) { | |
| return &localPacketServer{PacketConn: pl.PacketConn, done: make(chan bool)}, nil | |
| } | |
| func packetTransponder(c PacketConn, ch chan<- error) { | |
| defer close(ch) | |
| c.SetDeadline(time.Now().Add(someTimeout)) | |
| c.SetReadDeadline(time.Now().Add(someTimeout)) | |
| c.SetWriteDeadline(time.Now().Add(someTimeout)) | |
| b := make([]byte, 256) | |
| n, peer, err := c.ReadFrom(b) | |
| if err != nil { | |
| if perr := parseReadError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| if peer == nil { // for connected-mode sockets | |
| switch c.LocalAddr().Network() { | |
| case "udp": | |
| peer, err = ResolveUDPAddr("udp", string(b[:n])) | |
| case "unixgram": | |
| peer, err = ResolveUnixAddr("unixgram", string(b[:n])) | |
| } | |
| if err != nil { | |
| ch <- err | |
| return | |
| } | |
| } | |
| if _, err := c.WriteTo(b[:n], peer); err != nil { | |
| if perr := parseWriteError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| } | |
| func packetTransceiver(c PacketConn, wb []byte, dst Addr, ch chan<- error) { | |
| defer close(ch) | |
| c.SetDeadline(time.Now().Add(someTimeout)) | |
| c.SetReadDeadline(time.Now().Add(someTimeout)) | |
| c.SetWriteDeadline(time.Now().Add(someTimeout)) | |
| n, err := c.WriteTo(wb, dst) | |
| if err != nil { | |
| if perr := parseWriteError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| if n != len(wb) { | |
| ch <- fmt.Errorf("wrote %d; want %d", n, len(wb)) | |
| } | |
| rb := make([]byte, len(wb)) | |
| n, _, err = c.ReadFrom(rb) | |
| if err != nil { | |
| if perr := parseReadError(err); perr != nil { | |
| ch <- perr | |
| } | |
| ch <- err | |
| return | |
| } | |
| if n != len(wb) { | |
| ch <- fmt.Errorf("read %d; want %d", n, len(wb)) | |
| } | |
| } | |
| func timeoutPacketReceiver(c PacketConn, d, min, max time.Duration, ch chan<- error) { | |
| var err error | |
| defer func() { ch <- err }() | |
| t0 := time.Now() | |
| if err = c.SetReadDeadline(time.Now().Add(d)); err != nil { | |
| return | |
| } | |
| b := make([]byte, 256) | |
| var n int | |
| n, _, err = c.ReadFrom(b) | |
| t1 := time.Now() | |
| if n != 0 || err == nil || !err.(Error).Timeout() { | |
| err = fmt.Errorf("ReadFrom did not return (0, timeout): (%d, %v)", n, err) | |
| return | |
| } | |
| if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() { | |
| err = fmt.Errorf("ReadFrom took %s; expected %s", dt, d) | |
| return | |
| } | |
| } |