mirror of
https://github.com/autc04/Retro68.git
synced 2024-08-31 09:29:37 +00:00
488 lines
10 KiB
Go
488 lines
10 KiB
Go
// 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
|
|
|
|
package net
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// testUnixAddr uses os.MkdirTemp to get a name that is unique.
|
|
func testUnixAddr(t testing.TB) string {
|
|
// Pass an empty pattern to get a directory name that is as short as possible.
|
|
// If we end up with a name longer than the sun_path field in the sockaddr_un
|
|
// struct, we won't be able to make the syscall to open the socket.
|
|
d, err := os.MkdirTemp("", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := os.RemoveAll(d); err != nil {
|
|
t.Error(err)
|
|
}
|
|
})
|
|
return filepath.Join(d, "sock")
|
|
}
|
|
|
|
func newLocalListener(t testing.TB, network string) Listener {
|
|
listen := func(net, addr string) Listener {
|
|
ln, err := Listen(net, addr)
|
|
if err != nil {
|
|
t.Helper()
|
|
t.Fatal(err)
|
|
}
|
|
return ln
|
|
}
|
|
|
|
switch network {
|
|
case "tcp":
|
|
if supportsIPv4() {
|
|
if !supportsIPv6() {
|
|
return listen("tcp4", "127.0.0.1:0")
|
|
}
|
|
if ln, err := Listen("tcp4", "127.0.0.1:0"); err == nil {
|
|
return ln
|
|
}
|
|
}
|
|
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(t))
|
|
}
|
|
|
|
t.Helper()
|
|
t.Fatalf("%s is not supported", network)
|
|
return nil
|
|
}
|
|
|
|
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(t testing.TB, network string) *localServer {
|
|
t.Helper()
|
|
ln := newLocalListener(t, network)
|
|
return &localServer{Listener: ln, done: make(chan bool)}
|
|
}
|
|
|
|
type streamListener struct {
|
|
network, address string
|
|
Listener
|
|
done chan bool // signal that indicates server stopped
|
|
}
|
|
|
|
func (sl *streamListener) newLocalServer() *localServer {
|
|
return &localServer{Listener: sl.Listener, done: make(chan bool)}
|
|
}
|
|
|
|
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 newLocalPacketListener(t testing.TB, network string) PacketConn {
|
|
listenPacket := func(net, addr string) PacketConn {
|
|
c, err := ListenPacket(net, addr)
|
|
if err != nil {
|
|
t.Helper()
|
|
t.Fatal(err)
|
|
}
|
|
return c
|
|
}
|
|
|
|
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(t))
|
|
}
|
|
|
|
t.Helper()
|
|
t.Fatalf("%s is not supported", network)
|
|
return nil
|
|
}
|
|
|
|
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(t testing.TB, network string) *localPacketServer {
|
|
t.Helper()
|
|
c := newLocalPacketListener(t, network)
|
|
return &localPacketServer{PacketConn: c, done: make(chan bool)}
|
|
}
|
|
|
|
type packetListener struct {
|
|
PacketConn
|
|
}
|
|
|
|
func (pl *packetListener) newLocalServer() *localPacketServer {
|
|
return &localPacketServer{PacketConn: pl.PacketConn, done: make(chan bool)}
|
|
}
|
|
|
|
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))
|
|
}
|
|
}
|