// 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. // +build !plan9 package net import ( "runtime" "syscall" "testing" ) var listenerTests = []struct { net string laddr string ipv6 bool // test with underlying AF_INET6 socket wildcard bool // test with wildcard address }{ {net: "tcp", laddr: "", wildcard: true}, {net: "tcp", laddr: "0.0.0.0", wildcard: true}, {net: "tcp", laddr: "[::ffff:0.0.0.0]", wildcard: true}, {net: "tcp", laddr: "[::]", ipv6: true, wildcard: true}, {net: "tcp", laddr: "127.0.0.1"}, {net: "tcp", laddr: "[::ffff:127.0.0.1]"}, {net: "tcp", laddr: "[::1]", ipv6: true}, {net: "tcp4", laddr: "", wildcard: true}, {net: "tcp4", laddr: "0.0.0.0", wildcard: true}, {net: "tcp4", laddr: "[::ffff:0.0.0.0]", wildcard: true}, {net: "tcp4", laddr: "127.0.0.1"}, {net: "tcp4", laddr: "[::ffff:127.0.0.1]"}, {net: "tcp6", laddr: "", ipv6: true, wildcard: true}, {net: "tcp6", laddr: "[::]", ipv6: true, wildcard: true}, {net: "tcp6", laddr: "[::1]", ipv6: true}, } // 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("skipping test on %q", runtime.GOOS) } for _, tt := range listenerTests { if tt.wildcard && (testing.Short() || !*testExternal) { continue } if tt.ipv6 && !supportsIPv6 { continue } l1, port := usableListenPort(t, tt.net, tt.laddr) checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) l2, err := Listen(tt.net, tt.laddr+":"+port) checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) l1.Close() } } // 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("skipping test on %q", runtime.GOOS) } toudpnet := func(net string) string { switch net { case "tcp": return "udp" case "tcp4": return "udp4" case "tcp6": return "udp6" } return "" } for _, tt := range listenerTests { if tt.wildcard && (testing.Short() || !*testExternal) { continue } if tt.ipv6 && !supportsIPv6 { continue } tt.net = toudpnet(tt.net) l1, port := usableListenPacketPort(t, tt.net, tt.laddr) checkFirstListener(t, tt.net, tt.laddr+":"+port, l1) l2, err := ListenPacket(tt.net, tt.laddr+":"+port) checkSecondListener(t, tt.net, tt.laddr+":"+port, err, l2) l1.Close() } } var dualStackListenerTests = []struct { net1 string // first listener laddr1 string net2 string // second listener laddr2 string wildcard bool // test with wildcard address xerr error // expected error value, nil or other }{ // Test cases and expected results for the attemping 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) {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp", laddr1: "", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp", laddr1: "0.0.0.0", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "0.0.0.0", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp", laddr1: "[::ffff:0.0.0.0]", net2: "tcp", laddr2: "[::]", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp", laddr1: "[::]", net2: "tcp", laddr2: "[::ffff:0.0.0.0]", wildcard: true, xerr: syscall.EADDRINUSE}, {net1: "tcp4", laddr1: "", net2: "tcp6", laddr2: "", wildcard: true}, {net1: "tcp6", laddr1: "", net2: "tcp4", laddr2: "", wildcard: true}, {net1: "tcp4", laddr1: "0.0.0.0", net2: "tcp6", laddr2: "[::]", wildcard: true}, {net1: "tcp6", laddr1: "[::]", net2: "tcp4", laddr2: "0.0.0.0", wildcard: true}, {net1: "tcp", laddr1: "127.0.0.1", net2: "tcp", laddr2: "[::1]"}, {net1: "tcp", laddr1: "[::1]", net2: "tcp", laddr2: "127.0.0.1"}, {net1: "tcp4", laddr1: "127.0.0.1", net2: "tcp6", laddr2: "[::1]"}, {net1: "tcp6", laddr1: "[::1]", net2: "tcp4", laddr2: "127.0.0.1"}, } // TestDualStackTCPListener tests both single and double listen // to a test listener with various address families, different // listening address and same port. func TestDualStackTCPListener(t *testing.T) { if testing.Short() { t.Skip("skipping in -short mode, see issue 5001") } switch runtime.GOOS { case "plan9": t.Skipf("skipping test on %q", runtime.GOOS) } if !supportsIPv6 { t.Skip("ipv6 is not supported") } for _, tt := range dualStackListenerTests { if tt.wildcard && !*testExternal { continue } switch runtime.GOOS { case "openbsd": if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) { tt.xerr = nil } } l1, port := usableListenPort(t, tt.net1, tt.laddr1) laddr := tt.laddr1 + ":" + port checkFirstListener(t, tt.net1, laddr, l1) laddr = tt.laddr2 + ":" + port l2, err := Listen(tt.net2, laddr) checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2) l1.Close() } } // TestDualStackUDPListener tests both single and double listen // to a test listener with various address families, differnet // listening address and same port. func TestDualStackUDPListener(t *testing.T) { switch runtime.GOOS { case "plan9": t.Skipf("skipping test on %q", runtime.GOOS) } if !supportsIPv6 { t.Skip("ipv6 is not supported") } toudpnet := func(net string) string { switch net { case "tcp": return "udp" case "tcp4": return "udp4" case "tcp6": return "udp6" } return "" } for _, tt := range dualStackListenerTests { if tt.wildcard && (testing.Short() || !*testExternal) { continue } tt.net1 = toudpnet(tt.net1) tt.net2 = toudpnet(tt.net2) switch runtime.GOOS { case "openbsd": if tt.wildcard && differentWildcardAddr(tt.laddr1, tt.laddr2) { tt.xerr = nil } } l1, port := usableListenPacketPort(t, tt.net1, tt.laddr1) laddr := tt.laddr1 + ":" + port checkFirstListener(t, tt.net1, laddr, l1) laddr = tt.laddr2 + ":" + port l2, err := ListenPacket(tt.net2, laddr) checkDualStackSecondListener(t, tt.net2, laddr, tt.xerr, err, l2) l1.Close() } } func usableListenPort(t *testing.T, net, laddr string) (l Listener, port string) { var nladdr string var err error switch net { default: panic("usableListenPort net=" + net) case "tcp", "tcp4", "tcp6": l, err = Listen(net, laddr+":0") if err != nil { t.Fatalf("Probe Listen(%q, %q) failed: %v", net, laddr, err) } nladdr = l.(*TCPListener).Addr().String() } _, port, err = SplitHostPort(nladdr) if err != nil { t.Fatalf("SplitHostPort failed: %v", err) } return l, port } func usableListenPacketPort(t *testing.T, net, laddr string) (l PacketConn, port string) { var nladdr string var err error switch net { default: panic("usableListenPacketPort net=" + net) case "udp", "udp4", "udp6": l, err = ListenPacket(net, laddr+":0") if err != nil { t.Fatalf("Probe ListenPacket(%q, %q) failed: %v", net, laddr, err) } nladdr = l.(*UDPConn).LocalAddr().String() } _, port, err = SplitHostPort(nladdr) if err != nil { t.Fatalf("SplitHostPort failed: %v", err) } return l, port } 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(t *testing.T, net, laddr string, l interface{}) { switch net { case "tcp": fd := l.(*TCPListener).fd checkDualStackAddrFamily(t, net, laddr, fd) case "tcp4": fd := l.(*TCPListener).fd if fd.family != syscall.AF_INET { t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET) } case "tcp6": fd := l.(*TCPListener).fd if fd.family != syscall.AF_INET6 { t.Fatalf("First Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) } case "udp": fd := l.(*UDPConn).fd checkDualStackAddrFamily(t, net, laddr, fd) case "udp4": fd := l.(*UDPConn).fd if fd.family != syscall.AF_INET { t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET) } case "udp6": fd := l.(*UDPConn).fd if fd.family != syscall.AF_INET6 { t.Fatalf("First ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) } default: t.Fatalf("Unexpected network: %q", net) } } func checkSecondListener(t *testing.T, net, laddr string, err error, l interface{}) { switch net { case "tcp", "tcp4", "tcp6": if err == nil { l.(*TCPListener).Close() t.Fatalf("Second Listen(%q, %q) should fail", net, laddr) } case "udp", "udp4", "udp6": if err == nil { l.(*UDPConn).Close() t.Fatalf("Second ListenPacket(%q, %q) should fail", net, laddr) } default: t.Fatalf("Unexpected network: %q", net) } } func checkDualStackSecondListener(t *testing.T, net, laddr string, xerr, err error, l interface{}) { switch net { case "tcp", "tcp4", "tcp6": if xerr == nil && err != nil || xerr != nil && err == nil { t.Fatalf("Second Listen(%q, %q) returns %v, expected %v", net, laddr, err, xerr) } if err == nil { l.(*TCPListener).Close() } case "udp", "udp4", "udp6": if xerr == nil && err != nil || xerr != nil && err == nil { t.Fatalf("Second ListenPacket(%q, %q) returns %v, expected %v", net, laddr, err, xerr) } if err == nil { l.(*UDPConn).Close() } default: t.Fatalf("Unexpected network: %q", net) } } func checkDualStackAddrFamily(t *testing.T, net, laddr string, fd *netFD) { 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 { t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) } } else { if fd.family != a.family() { t.Fatalf("Listen(%q, %q) returns address family %v, expected %v", net, 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 { t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, syscall.AF_INET6) } } else { if fd.family != a.family() { t.Fatalf("ListenPacket(%q, %q) returns address family %v, expected %v", net, laddr, fd.family, a.family()) } } default: t.Fatalf("Unexpected protocol address type: %T", a) } } var prohibitionaryDialArgTests = []struct { net string addr string }{ {"tcp6", "127.0.0.1"}, {"tcp6", "[::ffff:127.0.0.1]"}, } func TestProhibitionaryDialArgs(t *testing.T) { switch runtime.GOOS { case "plan9": t.Skipf("skipping test on %q", runtime.GOOS) } // This test requires both IPv6 and IPv6 IPv4-mapping functionality. if !supportsIPv4map || testing.Short() || !*testExternal { return } l, port := usableListenPort(t, "tcp", "[::]") defer l.Close() for _, tt := range prohibitionaryDialArgTests { c, err := Dial(tt.net, tt.addr+":"+port) if err == nil { c.Close() t.Fatalf("Dial(%q, %q) should fail", tt.net, tt.addr) } } } func TestWildWildcardListener(t *testing.T) { switch runtime.GOOS { case "plan9": t.Skipf("skipping test on %q", runtime.GOOS) } if testing.Short() || !*testExternal { t.Skip("skipping test to avoid external network") } defer func() { if p := recover(); p != nil { t.Fatalf("Listen, ListenPacket or protocol-specific Listen 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() } }