diff options
Diffstat (limited to 'src/net/ipsock_posix.go')
-rw-r--r-- | src/net/ipsock_posix.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go new file mode 100644 index 0000000..8763d57 --- /dev/null +++ b/src/net/ipsock_posix.go @@ -0,0 +1,182 @@ +// Copyright 2009 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 aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris windows + +package net + +import ( + "context" + "internal/poll" + "runtime" + "syscall" +) + +// Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication +// capabilities which are controlled by the IPV6_V6ONLY socket option +// and kernel configuration. +// +// Should we try to use the IPv4 socket interface if we're only +// dealing with IPv4 sockets? As long as the host system understands +// IPv4-mapped IPv6, it's okay to pass IPv4-mapeed IPv6 addresses to +// the IPv6 interface. That simplifies our code and is most +// general. Unfortunately, we need to run on kernels built without +// IPv6 support too. So probe the kernel to figure it out. +func (p *ipStackCapabilities) probe() { + s, err := sysSocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + switch err { + case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: + case nil: + poll.CloseFunc(s) + p.ipv4Enabled = true + } + var probes = []struct { + laddr TCPAddr + value int + }{ + // IPv6 communication capability + {laddr: TCPAddr{IP: ParseIP("::1")}, value: 1}, + // IPv4-mapped IPv6 address communication capability + {laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0}, + } + switch runtime.GOOS { + case "dragonfly", "openbsd": + // The latest DragonFly BSD and OpenBSD kernels don't + // support IPV6_V6ONLY=0. They always return an error + // and we don't need to probe the capability. + probes = probes[:1] + } + for i := range probes { + s, err := sysSocket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if err != nil { + continue + } + defer poll.CloseFunc(s) + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value) + sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6) + if err != nil { + continue + } + if err := syscall.Bind(s, sa); err != nil { + continue + } + if i == 0 { + p.ipv6Enabled = true + } else { + p.ipv4MappedIPv6Enabled = true + } + } +} + +// favoriteAddrFamily returns the appropriate address family for the +// given network, laddr, raddr and mode. +// +// If mode indicates "listen" and laddr is a wildcard, we assume that +// the user wants to make a passive-open connection with a wildcard +// address family, both AF_INET and AF_INET6, and a wildcard address +// like the following: +// +// - A listen for a wildcard communication domain, "tcp" or +// "udp", with a wildcard address: If the platform supports +// both IPv6 and IPv4-mapped IPv6 communication capabilities, +// or does not support IPv4, we use a dual stack, AF_INET6 and +// IPV6_V6ONLY=0, wildcard address listen. The dual stack +// wildcard address listen may fall back to an IPv6-only, +// AF_INET6 and IPV6_V6ONLY=1, wildcard address listen. +// Otherwise we prefer an IPv4-only, AF_INET, wildcard address +// listen. +// +// - A listen for a wildcard communication domain, "tcp" or +// "udp", with an IPv4 wildcard address: same as above. +// +// - A listen for a wildcard communication domain, "tcp" or +// "udp", with an IPv6 wildcard address: same as above. +// +// - A listen for an IPv4 communication domain, "tcp4" or "udp4", +// with an IPv4 wildcard address: We use an IPv4-only, AF_INET, +// wildcard address listen. +// +// - A listen for an IPv6 communication domain, "tcp6" or "udp6", +// with an IPv6 wildcard address: We use an IPv6-only, AF_INET6 +// and IPV6_V6ONLY=1, wildcard address listen. +// +// Otherwise guess: If the addresses are IPv4 then returns AF_INET, +// or else returns AF_INET6. It also returns a boolean value what +// designates IPV6_V6ONLY option. +// +// Note that the latest DragonFly BSD and OpenBSD kernels allow +// neither "net.inet6.ip6.v6only=1" change nor IPPROTO_IPV6 level +// IPV6_V6ONLY socket option setting. +func favoriteAddrFamily(network string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) { + switch network[len(network)-1] { + case '4': + return syscall.AF_INET, false + case '6': + return syscall.AF_INET6, true + } + + if mode == "listen" && (laddr == nil || laddr.isWildcard()) { + if supportsIPv4map() || !supportsIPv4() { + return syscall.AF_INET6, false + } + if laddr == nil { + return syscall.AF_INET, false + } + return laddr.family(), false + } + + if (laddr == nil || laddr.family() == syscall.AF_INET) && + (raddr == nil || raddr.family() == syscall.AF_INET) { + return syscall.AF_INET, false + } + return syscall.AF_INET6, false +} + +func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlFn func(string, string, syscall.RawConn) error) (fd *netFD, err error) { + if (runtime.GOOS == "aix" || runtime.GOOS == "windows" || runtime.GOOS == "openbsd") && mode == "dial" && raddr.isWildcard() { + raddr = raddr.toLocal(net) + } + family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode) + return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, ctrlFn) +} + +func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) { + switch family { + case syscall.AF_INET: + if len(ip) == 0 { + ip = IPv4zero + } + ip4 := ip.To4() + if ip4 == nil { + return nil, &AddrError{Err: "non-IPv4 address", Addr: ip.String()} + } + sa := &syscall.SockaddrInet4{Port: port} + copy(sa.Addr[:], ip4) + return sa, nil + case syscall.AF_INET6: + // In general, an IP wildcard address, which is either + // "0.0.0.0" or "::", means the entire IP addressing + // space. For some historical reason, it is used to + // specify "any available address" on some operations + // of IP node. + // + // When the IP node supports IPv4-mapped IPv6 address, + // we allow a listener to listen to the wildcard + // address of both IP addressing spaces by specifying + // IPv6 wildcard address. + if len(ip) == 0 || ip.Equal(IPv4zero) { + ip = IPv6zero + } + // We accept any IPv6 address including IPv4-mapped + // IPv6 address. + ip6 := ip.To16() + if ip6 == nil { + return nil, &AddrError{Err: "non-IPv6 address", Addr: ip.String()} + } + sa := &syscall.SockaddrInet6{Port: port, ZoneId: uint32(zoneCache.index(zone))} + copy(sa.Addr[:], ip6) + return sa, nil + } + return nil, &AddrError{Err: "invalid address family", Addr: ip.String()} +} |