package net
import (
+ "runtime"
"syscall"
"time"
)
+// BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
+// "tcp" and "udp" networks does not listen for both IPv4 and IPv6
+// connections. This is due to the fact that IPv4 traffic will not be
+// routed to an IPv6 socket - two separate sockets are required if
+// both address families are to be supported.
+// See inet6(4) for details.
+
func probeIPv4Stack() bool {
s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
switch err {
var probes = []struct {
laddr TCPAddr
value int
- ok bool
}{
// IPv6 communication capability
{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
// IPv6 IPv4-mapped address communication capability
{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
}
+ var supps [2]bool
+ switch runtime.GOOS {
+ case "dragonfly", "openbsd":
+ // Some released versions of DragonFly BSD pretend to
+ // accept IPV6_V6ONLY=0 successfully, but the state
+ // still stays IPV6_V6ONLY=1. Eventually DragonFly BSD
+ // stops preteding, but the transition period would
+ // cause unpredictable behavior and we need to avoid
+ // it.
+ //
+ // OpenBSD also doesn't support IPV6_V6ONLY=0 but it
+ // never pretends to accept IPV6_V6OLY=0. It always
+ // returns an error and we don't need to probe the
+ // capability.
+ probes = probes[:1]
+ }
for i := range probes {
s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err := syscall.Bind(s, sa); err != nil {
continue
}
- probes[i].ok = true
+ supps[i] = true
}
- return probes[0].ok, probes[1].ok
+ return supps[0], supps[1]
}
// favoriteAddrFamily returns the appropriate address family to
// listening address and same port.
func TestDualStackTCPListener(t *testing.T) {
switch runtime.GOOS {
- case "dragonfly":
- t.Skip("not supported on DragonFly, see golang.org/issue/10729")
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
continue
}
- if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+ if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
tt.xerr = nil
}
var firstErr, secondErr error
continue
}
- if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+ if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
tt.xerr = nil
}
var firstErr, secondErr error
os.Exit(st)
}
+type ipv6LinkLocalUnicastTest struct {
+ network, address string
+ nameLookup bool
+}
+
+var (
+ ipv6LinkLocalUnicastTCPTests []ipv6LinkLocalUnicastTest
+ ipv6LinkLocalUnicastUDPTests []ipv6LinkLocalUnicastTest
+)
+
func setupTestData() {
if supportsIPv4 {
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
}
- if ifi := loopbackInterface(); ifi != nil {
+ ifi := loopbackInterface()
+ if ifi != nil {
index := fmt.Sprintf("%v", ifi.Index)
resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
{"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneToString(ifi.Index)}, nil},
{"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
}...)
}
+
+ addr := ipv6LinkLocalUnicastAddr(ifi)
+ if addr != "" {
+ if runtime.GOOS != "dragonfly" {
+ ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+ {"tcp", "[" + addr + "%" + ifi.Name + "]:0", false},
+ }...)
+ ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+ {"udp", "[" + addr + "%" + ifi.Name + "]:0", false},
+ }...)
+ }
+ ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+ {"tcp6", "[" + addr + "%" + ifi.Name + "]:0", false},
+ }...)
+ ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+ {"udp6", "[" + addr + "%" + ifi.Name + "]:0", false},
+ }...)
+ switch runtime.GOOS {
+ case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
+ ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+ {"tcp", "[localhost%" + ifi.Name + "]:0", true},
+ {"tcp6", "[localhost%" + ifi.Name + "]:0", true},
+ }...)
+ ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+ {"udp", "[localhost%" + ifi.Name + "]:0", true},
+ {"udp6", "[localhost%" + ifi.Name + "]:0", true},
+ }...)
+ case "linux":
+ ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+ {"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
+ {"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
+ }...)
+ ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+ {"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
+ {"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
+ }...)
+ }
+ }
}
func printRunningGoroutines() {
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_PORTRANGE, syscall.IPV6_PORTRANGE_HIGH)
}
}
- if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
+ if supportsIPv4map && family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
// Allow both IP versions even if the OS default
// is otherwise. Note that some operating systems
// never admit this option.
t.Skip("avoid external network")
}
if !supportsIPv6 {
- t.Skip("ipv6 is not supported")
- }
- ifi := loopbackInterface()
- if ifi == nil {
- t.Skip("loopback interface not found")
- }
- laddr := ipv6LinkLocalUnicastAddr(ifi)
- if laddr == "" {
- t.Skip("ipv6 unicast address on loopback not found")
+ t.Skip("IPv6 is not supported")
}
- type test struct {
- net, addr string
- nameLookup bool
- }
- var tests = []test{
- {"tcp", "[" + laddr + "%" + ifi.Name + "]:0", false},
- {"tcp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
- }
- switch runtime.GOOS {
- case "darwin", "freebsd", "openbsd", "netbsd":
- tests = append(tests, []test{
- {"tcp", "[localhost%" + ifi.Name + "]:0", true},
- {"tcp6", "[localhost%" + ifi.Name + "]:0", true},
- }...)
- case "linux":
- tests = append(tests, []test{
- {"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
- {"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
- }...)
- }
- for i, tt := range tests {
- ln, err := Listen(tt.net, tt.addr)
+ for i, tt := range ipv6LinkLocalUnicastTCPTests {
+ ln, err := Listen(tt.network, tt.address)
if err != nil {
// It might return "LookupHost returned no
// suitable address" error on some platforms.
t.Fatalf("got %v; expected a proper address with zone identifier", la)
}
- c, err := Dial(tt.net, ls.Listener.Addr().String())
+ c, err := Dial(tt.network, ls.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
"time"
)
-// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
-// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
-// will not be routed to an IPv6 socket - two separate sockets are required
-// if both AFs are to be supported. See inet6(4) on OpenBSD for details.
-
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
t.Skip("avoid external network")
}
if !supportsIPv6 {
- t.Skip("ipv6 is not supported")
- }
- ifi := loopbackInterface()
- if ifi == nil {
- t.Skip("loopback interface not found")
- }
- laddr := ipv6LinkLocalUnicastAddr(ifi)
- if laddr == "" {
- t.Skip("ipv6 unicast address on loopback not found")
+ t.Skip("IPv6 is not supported")
}
- type test struct {
- net, addr string
- nameLookup bool
- }
- var tests = []test{
- {"udp", "[" + laddr + "%" + ifi.Name + "]:0", false},
- {"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
- }
- // The first udp test fails on DragonFly - see issue 7473.
- if runtime.GOOS == "dragonfly" {
- tests = tests[1:]
- }
- switch runtime.GOOS {
- case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
- tests = append(tests, []test{
- {"udp", "[localhost%" + ifi.Name + "]:0", true},
- {"udp6", "[localhost%" + ifi.Name + "]:0", true},
- }...)
- case "linux":
- tests = append(tests, []test{
- {"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
- {"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
- }...)
- }
- for _, tt := range tests {
- c1, err := ListenPacket(tt.net, tt.addr)
+ for i, tt := range ipv6LinkLocalUnicastUDPTests {
+ c1, err := ListenPacket(tt.network, tt.address)
if err != nil {
// It might return "LookupHost returned no
// suitable address" error on some platforms.
t.Log(err)
continue
}
- defer c1.Close()
+ ls, err := (&packetListener{PacketConn: c1}).newLocalServer()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ls.teardown()
+ ch := make(chan error, 1)
+ handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, ch) }
+ if err := ls.buildup(handler); err != nil {
+ t.Fatal(err)
+ }
if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
t.Fatalf("got %v; expected a proper address with zone identifier", la)
}
- c2, err := Dial(tt.net, c1.LocalAddr().String())
+ c2, err := Dial(tt.network, ls.PacketConn.LocalAddr().String())
if err != nil {
t.Fatal(err)
}
t.Fatal(err)
}
b := make([]byte, 32)
- if _, from, err := c1.ReadFrom(b); err != nil {
+ if _, err := c2.Read(b); err != nil {
t.Fatal(err)
- } else {
- if ra, ok := from.(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
- t.Fatalf("got %v; expected a proper address with zone identifier", ra)
- }
+ }
+
+ for err := range ch {
+ t.Errorf("#%d: %v", i, err)
}
}
}