777 lines
21 KiB
Go
777 lines
21 KiB
Go
// 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)
|
|
}
|
|
}
|
|
})
|
|
}
|