blob: 59c0112122babe68d5e5af7d31d82f154429e43b [file] [log] [blame]
// Copyright 2011 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 && !plan9
package net
import (
"context"
"fmt"
"internal/testenv"
"os"
"runtime"
"syscall"
"testing"
"time"
)
func (ln *TCPListener) port() string {
_, port, err := SplitHostPort(ln.Addr().String())
if err != nil {
return ""
}
return port
}
func (c *UDPConn) port() string {
_, port, err := SplitHostPort(c.LocalAddr().String())
if err != nil {
return ""
}
return port
}
var tcpListenerTests = []struct {
network string
address string
}{
{"tcp", ""},
{"tcp", "0.0.0.0"},
{"tcp", "::ffff:0.0.0.0"},
{"tcp", "::"},
{"tcp", "127.0.0.1"},
{"tcp", "::ffff:127.0.0.1"},
{"tcp", "::1"},
{"tcp4", ""},
{"tcp4", "0.0.0.0"},
{"tcp4", "::ffff:0.0.0.0"},
{"tcp4", "127.0.0.1"},
{"tcp4", "::ffff:127.0.0.1"},
{"tcp6", ""},
{"tcp6", "::"},
{"tcp6", "::1"},
}
// TestTCPListener tests both single and double listen to a test
// listener with same address family, same listening address and
// same port.
func TestTCPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
for _, tt := range tcpListenerTests {
if !testableListenArgs(tt.network, JoinHostPort(tt.address, "0"), "") {
t.Logf("skipping %s test", tt.network+" "+tt.address)
continue
}
ln1, err := Listen(tt.network, JoinHostPort(tt.address, "0"))
if err != nil {
t.Fatal(err)
}
if err := checkFirstListener(tt.network, ln1); err != nil {
ln1.Close()
t.Fatal(err)
}
ln2, err := Listen(tt.network, JoinHostPort(tt.address, ln1.(*TCPListener).port()))
if err == nil {
ln2.Close()
}
if err := checkSecondListener(tt.network, tt.address, err); err != nil {
ln1.Close()
t.Fatal(err)
}
ln1.Close()
}
}
var udpListenerTests = []struct {
network string
address string
}{
{"udp", ""},
{"udp", "0.0.0.0"},
{"udp", "::ffff:0.0.0.0"},
{"udp", "::"},
{"udp", "127.0.0.1"},
{"udp", "::ffff:127.0.0.1"},
{"udp", "::1"},
{"udp4", ""},
{"udp4", "0.0.0.0"},
{"udp4", "::ffff:0.0.0.0"},
{"udp4", "127.0.0.1"},
{"udp4", "::ffff:127.0.0.1"},
{"udp6", ""},
{"udp6", "::"},
{"udp6", "::1"},
}
// TestUDPListener tests both single and double listen to a test
// listener with same address family, same listening address and
// same port.
func TestUDPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
for _, tt := range udpListenerTests {
if !testableListenArgs(tt.network, JoinHostPort(tt.address, "0"), "") {
t.Logf("skipping %s test", tt.network+" "+tt.address)
continue
}
c1, err := ListenPacket(tt.network, JoinHostPort(tt.address, "0"))
if err != nil {
t.Fatal(err)
}
if err := checkFirstListener(tt.network, c1); err != nil {
c1.Close()
t.Fatal(err)
}
c2, err := ListenPacket(tt.network, JoinHostPort(tt.address, c1.(*UDPConn).port()))
if err == nil {
c2.Close()
}
if err := checkSecondListener(tt.network, tt.address, err); err != nil {
c1.Close()
t.Fatal(err)
}
c1.Close()
}
}
var dualStackTCPListenerTests = []struct {
network1, address1 string // first listener
network2, address2 string // second listener
xerr error // expected error value, nil or other
}{
// Test cases and expected results for the attempting 2nd listen on the same port
// 1st listen 2nd listen darwin freebsd linux openbsd
// ------------------------------------------------------------------------------------
// "tcp" "" "tcp" "" - - - -
// "tcp" "" "tcp" "0.0.0.0" - - - -
// "tcp" "0.0.0.0" "tcp" "" - - - -
// ------------------------------------------------------------------------------------
// "tcp" "" "tcp" "[::]" - - - ok
// "tcp" "[::]" "tcp" "" - - - ok
// "tcp" "0.0.0.0" "tcp" "[::]" - - - ok
// "tcp" "[::]" "tcp" "0.0.0.0" - - - ok
// "tcp" "[::ffff:0.0.0.0]" "tcp" "[::]" - - - ok
// "tcp" "[::]" "tcp" "[::ffff:0.0.0.0]" - - - ok
// ------------------------------------------------------------------------------------
// "tcp4" "" "tcp6" "" ok ok ok ok
// "tcp6" "" "tcp4" "" ok ok ok ok
// "tcp4" "0.0.0.0" "tcp6" "[::]" ok ok ok ok
// "tcp6" "[::]" "tcp4" "0.0.0.0" ok ok ok ok
// ------------------------------------------------------------------------------------
// "tcp" "127.0.0.1" "tcp" "[::1]" ok ok ok ok
// "tcp" "[::1]" "tcp" "127.0.0.1" ok ok ok ok
// "tcp4" "127.0.0.1" "tcp6" "[::1]" ok ok ok ok
// "tcp6" "[::1]" "tcp4" "127.0.0.1" ok ok ok ok
//
// Platform default configurations:
// darwin, kernel version 11.3.0
// net.inet6.ip6.v6only=0 (overridable by sysctl or IPV6_V6ONLY option)
// freebsd, kernel version 8.2
// net.inet6.ip6.v6only=1 (overridable by sysctl or IPV6_V6ONLY option)
// linux, kernel version 3.0.0
// net.ipv6.bindv6only=0 (overridable by sysctl or IPV6_V6ONLY option)
// openbsd, kernel version 5.0
// net.inet6.ip6.v6only=1 (overriding is prohibited)
{"tcp", "", "tcp", "", syscall.EADDRINUSE},
{"tcp", "", "tcp", "0.0.0.0", syscall.EADDRINUSE},
{"tcp", "0.0.0.0", "tcp", "", syscall.EADDRINUSE},
{"tcp", "", "tcp", "::", syscall.EADDRINUSE},
{"tcp", "::", "tcp", "", syscall.EADDRINUSE},
{"tcp", "0.0.0.0", "tcp", "::", syscall.EADDRINUSE},
{"tcp", "::", "tcp", "0.0.0.0", syscall.EADDRINUSE},
{"tcp", "::ffff:0.0.0.0", "tcp", "::", syscall.EADDRINUSE},
{"tcp", "::", "tcp", "::ffff:0.0.0.0", syscall.EADDRINUSE},
{"tcp4", "", "tcp6", "", nil},
{"tcp6", "", "tcp4", "", nil},
{"tcp4", "0.0.0.0", "tcp6", "::", nil},
{"tcp6", "::", "tcp4", "0.0.0.0", nil},
{"tcp", "127.0.0.1", "tcp", "::1", nil},
{"tcp", "::1", "tcp", "127.0.0.1", nil},
{"tcp4", "127.0.0.1", "tcp6", "::1", nil},
{"tcp6", "::1", "tcp4", "127.0.0.1", nil},
}
// TestDualStackTCPListener tests both single and double listen
// to a test listener with various address families, different
// listening address and same port.
//
// On DragonFly BSD, we expect the kernel version of node under test
// to be greater than or equal to 4.4.
func TestDualStackTCPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required")
}
for _, tt := range dualStackTCPListenerTests {
if !testableListenArgs(tt.network1, JoinHostPort(tt.address1, "0"), "") {
t.Logf("skipping %s test", tt.network1+" "+tt.address1)
continue
}
if !supportsIPv4map() && differentWildcardAddr(tt.address1, tt.address2) {
tt.xerr = nil
}
var firstErr, secondErr error
for i := 0; i < 5; i++ {
lns, err := newDualStackListener()
if err != nil {
t.Fatal(err)
}
port := lns[0].port()
for _, ln := range lns {
ln.Close()
}
var ln1 Listener
ln1, firstErr = Listen(tt.network1, JoinHostPort(tt.address1, port))
if firstErr != nil {
continue
}
if err := checkFirstListener(tt.network1, ln1); err != nil {
ln1.Close()
t.Fatal(err)
}
ln2, err := Listen(tt.network2, JoinHostPort(tt.address2, ln1.(*TCPListener).port()))
if err == nil {
ln2.Close()
}
if secondErr = checkDualStackSecondListener(tt.network2, tt.address2, err, tt.xerr); secondErr != nil {
ln1.Close()
continue
}
ln1.Close()
break
}
if firstErr != nil {
t.Error(firstErr)
}
if secondErr != nil {
t.Error(secondErr)
}
}
}
var dualStackUDPListenerTests = []struct {
network1, address1 string // first listener
network2, address2 string // second listener
xerr error // expected error value, nil or other
}{
{"udp", "", "udp", "", syscall.EADDRINUSE},
{"udp", "", "udp", "0.0.0.0", syscall.EADDRINUSE},
{"udp", "0.0.0.0", "udp", "", syscall.EADDRINUSE},
{"udp", "", "udp", "::", syscall.EADDRINUSE},
{"udp", "::", "udp", "", syscall.EADDRINUSE},
{"udp", "0.0.0.0", "udp", "::", syscall.EADDRINUSE},
{"udp", "::", "udp", "0.0.0.0", syscall.EADDRINUSE},
{"udp", "::ffff:0.0.0.0", "udp", "::", syscall.EADDRINUSE},
{"udp", "::", "udp", "::ffff:0.0.0.0", syscall.EADDRINUSE},
{"udp4", "", "udp6", "", nil},
{"udp6", "", "udp4", "", nil},
{"udp4", "0.0.0.0", "udp6", "::", nil},
{"udp6", "::", "udp4", "0.0.0.0", nil},
{"udp", "127.0.0.1", "udp", "::1", nil},
{"udp", "::1", "udp", "127.0.0.1", nil},
{"udp4", "127.0.0.1", "udp6", "::1", nil},
{"udp6", "::1", "udp4", "127.0.0.1", nil},
}
// TestDualStackUDPListener tests both single and double listen
// to a test listener with various address families, different
// listening address and same port.
//
// On DragonFly BSD, we expect the kernel version of node under test
// to be greater than or equal to 4.4.
func TestDualStackUDPListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if !supportsIPv4() || !supportsIPv6() {
t.Skip("both IPv4 and IPv6 are required")
}
for _, tt := range dualStackUDPListenerTests {
if !testableListenArgs(tt.network1, JoinHostPort(tt.address1, "0"), "") {
t.Logf("skipping %s test", tt.network1+" "+tt.address1)
continue
}
if !supportsIPv4map() && differentWildcardAddr(tt.address1, tt.address2) {
tt.xerr = nil
}
var firstErr, secondErr error
for i := 0; i < 5; i++ {
cs, err := newDualStackPacketListener()
if err != nil {
t.Fatal(err)
}
port := cs[0].port()
for _, c := range cs {
c.Close()
}
var c1 PacketConn
c1, firstErr = ListenPacket(tt.network1, JoinHostPort(tt.address1, port))
if firstErr != nil {
continue
}
if err := checkFirstListener(tt.network1, c1); err != nil {
c1.Close()
t.Fatal(err)
}
c2, err := ListenPacket(tt.network2, JoinHostPort(tt.address2, c1.(*UDPConn).port()))
if err == nil {
c2.Close()
}
if secondErr = checkDualStackSecondListener(tt.network2, tt.address2, err, tt.xerr); secondErr != nil {
c1.Close()
continue
}
c1.Close()
break
}
if firstErr != nil {
t.Error(firstErr)
}
if secondErr != nil {
t.Error(secondErr)
}
}
}
func differentWildcardAddr(i, j string) bool {
if (i == "" || i == "0.0.0.0" || i == "::ffff:0.0.0.0") && (j == "" || j == "0.0.0.0" || j == "::ffff:0.0.0.0") {
return false
}
if i == "[::]" && j == "[::]" {
return false
}
return true
}
func checkFirstListener(network string, ln any) error {
switch network {
case "tcp":
fd := ln.(*TCPListener).fd
if err := checkDualStackAddrFamily(fd); err != nil {
return err
}
case "tcp4":
fd := ln.(*TCPListener).fd
if fd.family != syscall.AF_INET {
return fmt.Errorf("%v got %v; want %v", fd.laddr, fd.family, syscall.AF_INET)
}
case "tcp6":
fd := ln.(*TCPListener).fd
if fd.family != syscall.AF_INET6 {
return fmt.Errorf("%v got %v; want %v", fd.laddr, fd.family, syscall.AF_INET6)
}
case "udp":
fd := ln.(*UDPConn).fd
if err := checkDualStackAddrFamily(fd); err != nil {
return err
}
case "udp4":
fd := ln.(*UDPConn).fd
if fd.family != syscall.AF_INET {
return fmt.Errorf("%v got %v; want %v", fd.laddr, fd.family, syscall.AF_INET)
}
case "udp6":
fd := ln.(*UDPConn).fd
if fd.family != syscall.AF_INET6 {
return fmt.Errorf("%v got %v; want %v", fd.laddr, fd.family, syscall.AF_INET6)
}
default:
return UnknownNetworkError(network)
}
return nil
}
func checkSecondListener(network, address string, err error) error {
switch network {
case "tcp", "tcp4", "tcp6":
if err == nil {
return fmt.Errorf("%s should fail", network+" "+address)
}
case "udp", "udp4", "udp6":
if err == nil {
return fmt.Errorf("%s should fail", network+" "+address)
}
default:
return UnknownNetworkError(network)
}
return nil
}
func checkDualStackSecondListener(network, address string, err, xerr error) error {
switch network {
case "tcp", "tcp4", "tcp6":
if xerr == nil && err != nil || xerr != nil && err == nil {
return fmt.Errorf("%s got %v; want %v", network+" "+address, err, xerr)
}
case "udp", "udp4", "udp6":
if xerr == nil && err != nil || xerr != nil && err == nil {
return fmt.Errorf("%s got %v; want %v", network+" "+address, err, xerr)
}
default:
return UnknownNetworkError(network)
}
return nil
}
func checkDualStackAddrFamily(fd *netFD) error {
switch a := fd.laddr.(type) {
case *TCPAddr:
// If a node under test supports both IPv6 capability
// and IPv6 IPv4-mapping capability, we can assume
// that the node listens on a wildcard address with an
// AF_INET6 socket.
if supportsIPv4map() && fd.laddr.(*TCPAddr).isWildcard() {
if fd.family != syscall.AF_INET6 {
return fmt.Errorf("Listen(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, syscall.AF_INET6)
}
} else {
if fd.family != a.family() {
return fmt.Errorf("Listen(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, a.family())
}
}
case *UDPAddr:
// If a node under test supports both IPv6 capability
// and IPv6 IPv4-mapping capability, we can assume
// that the node listens on a wildcard address with an
// AF_INET6 socket.
if supportsIPv4map() && fd.laddr.(*UDPAddr).isWildcard() {
if fd.family != syscall.AF_INET6 {
return fmt.Errorf("ListenPacket(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, syscall.AF_INET6)
}
} else {
if fd.family != a.family() {
return fmt.Errorf("ListenPacket(%s, %v) returns %v; want %v", fd.net, fd.laddr, fd.family, a.family())
}
}
default:
return fmt.Errorf("unexpected protocol address type: %T", a)
}
return nil
}
func TestWildWildcardListener(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
defer func() {
if p := recover(); p != nil {
t.Fatalf("panicked: %v", p)
}
}()
if ln, err := Listen("tcp", ""); err == nil {
ln.Close()
}
if ln, err := ListenPacket("udp", ""); err == nil {
ln.Close()
}
if ln, err := ListenTCP("tcp", nil); err == nil {
ln.Close()
}
if ln, err := ListenUDP("udp", nil); err == nil {
ln.Close()
}
if ln, err := ListenIP("ip:icmp", nil); err == nil {
ln.Close()
}
}
var ipv4MulticastListenerTests = []struct {
net string
gaddr *UDPAddr // see RFC 4727
}{
{"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}},
{"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}},
}
// TestIPv4MulticastListener tests both single and double listen to a
// test listener with same address family, same group address and same
// port.
func TestIPv4MulticastListener(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
switch runtime.GOOS {
case "android", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if !supportsIPv4() {
t.Skip("IPv4 is not supported")
}
closer := func(cs []*UDPConn) {
for _, c := range cs {
if c != nil {
c.Close()
}
}
}
for _, ifi := range []*Interface{loopbackInterface(), nil} {
// Note that multicast interface assignment by system
// is not recommended because it usually relies on
// routing stuff for finding out an appropriate
// nexthop containing both network and link layer
// adjacencies.
if ifi == nil || !*testIPv4 {
continue
}
for _, tt := range ipv4MulticastListenerTests {
var err error
cs := make([]*UDPConn, 2)
if cs[0], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
t.Fatal(err)
}
if err := checkMulticastListener(cs[0], tt.gaddr.IP); err != nil {
closer(cs)
t.Fatal(err)
}
if cs[1], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
closer(cs)
t.Fatal(err)
}
if err := checkMulticastListener(cs[1], tt.gaddr.IP); err != nil {
closer(cs)
t.Fatal(err)
}
closer(cs)
}
}
}
var ipv6MulticastListenerTests = []struct {
net string
gaddr *UDPAddr // see RFC 4727
}{
{"udp", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}},
{"udp", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}},
{"udp", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}},
{"udp", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}},
{"udp", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}},
{"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}},
{"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}},
{"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}},
{"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}},
{"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}},
{"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}},
{"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}},
}
// TestIPv6MulticastListener tests both single and double listen to a
// test listener with same address family, same group address and same
// port.
func TestIPv6MulticastListener(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if !supportsIPv6() {
t.Skip("IPv6 is not supported")
}
if os.Getuid() != 0 {
t.Skip("must be root")
}
closer := func(cs []*UDPConn) {
for _, c := range cs {
if c != nil {
c.Close()
}
}
}
for _, ifi := range []*Interface{loopbackInterface(), nil} {
// Note that multicast interface assignment by system
// is not recommended because it usually relies on
// routing stuff for finding out an appropriate
// nexthop containing both network and link layer
// adjacencies.
if ifi == nil && !*testIPv6 {
continue
}
for _, tt := range ipv6MulticastListenerTests {
var err error
cs := make([]*UDPConn, 2)
if cs[0], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
t.Fatal(err)
}
if err := checkMulticastListener(cs[0], tt.gaddr.IP); err != nil {
closer(cs)
t.Fatal(err)
}
if cs[1], err = ListenMulticastUDP(tt.net, ifi, tt.gaddr); err != nil {
closer(cs)
t.Fatal(err)
}
if err := checkMulticastListener(cs[1], tt.gaddr.IP); err != nil {
closer(cs)
t.Fatal(err)
}
closer(cs)
}
}
}
func checkMulticastListener(c *UDPConn, ip IP) error {
if ok, err := multicastRIBContains(ip); err != nil {
return err
} else if !ok {
return fmt.Errorf("%s not found in multicast rib", ip.String())
}
la := c.LocalAddr()
if la, ok := la.(*UDPAddr); !ok || la.Port == 0 {
return fmt.Errorf("got %v; want a proper address with non-zero port number", la)
}
return nil
}
func multicastRIBContains(ip IP) (bool, error) {
switch runtime.GOOS {
case "aix", "dragonfly", "netbsd", "openbsd", "plan9", "solaris", "illumos", "windows":
return true, nil // not implemented yet
case "linux":
if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" {
return true, nil // not implemented yet
}
}
ift, err := Interfaces()
if err != nil {
return false, err
}
for _, ifi := range ift {
ifmat, err := ifi.MulticastAddrs()
if err != nil {
return false, err
}
for _, ifma := range ifmat {
if ifma.(*IPAddr).IP.Equal(ip) {
return true, nil
}
}
}
return false, nil
}
// Issue 21856.
func TestClosingListener(t *testing.T) {
ln := newLocalListener(t, "tcp")
addr := ln.Addr()
go func() {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}()
// Let the goroutine start. We don't sleep long: if the
// goroutine doesn't start, the test will pass without really
// testing anything, which is OK.
time.Sleep(time.Millisecond)
ln.Close()
ln2, err := Listen("tcp", addr.String())
if err != nil {
t.Fatal(err)
}
ln2.Close()
}
func TestListenConfigControl(t *testing.T) {
switch runtime.GOOS {
case "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
t.Run("StreamListen", func(t *testing.T) {
for _, network := range []string{"tcp", "tcp4", "tcp6", "unix", "unixpacket"} {
if !testableNetwork(network) {
continue
}
ln := newLocalListener(t, network)
address := ln.Addr().String()
// TODO: This is racy. The selected address could be reused in between
// this Close and the subsequent Listen.
ln.Close()
lc := ListenConfig{Control: controlOnConnSetup}
ln, err := lc.Listen(context.Background(), network, address)
if err != nil {
t.Error(err)
continue
}
ln.Close()
}
})
t.Run("PacketListen", func(t *testing.T) {
for _, network := range []string{"udp", "udp4", "udp6", "unixgram"} {
if !testableNetwork(network) {
continue
}
c := newLocalPacketListener(t, network)
address := c.LocalAddr().String()
// TODO: This is racy. The selected address could be reused in between
// this Close and the subsequent ListenPacket.
c.Close()
if network == "unixgram" {
os.Remove(address)
}
lc := ListenConfig{Control: controlOnConnSetup}
c, err := lc.ListenPacket(context.Background(), network, address)
if err != nil {
t.Error(err)
continue
}
c.Close()
if network == "unixgram" {
os.Remove(address)
}
}
})
}