1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
15 func probeIPv4Stack() bool {
16 s
, err
:= socketFunc(syscall
.AF_INET
, syscall
.SOCK_STREAM
, syscall
.IPPROTO_TCP
)
18 case syscall
.EAFNOSUPPORT
, syscall
.EPROTONOSUPPORT
:
26 // Should we try to use the IPv4 socket interface if we're
27 // only dealing with IPv4 sockets? As long as the host system
28 // understands IPv6, it's okay to pass IPv4 addresses to the IPv6
29 // interface. That simplifies our code and is most general.
30 // Unfortunately, we need to run on kernels built without IPv6
31 // support too. So probe the kernel to figure it out.
33 // probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4-
34 // mapping capability which is controlled by IPV6_V6ONLY socket
35 // option and/or kernel state "net.inet6.ip6.v6only".
36 // It returns two boolean values. If the first boolean value is
37 // true, kernel supports basic IPv6 functionality. If the second
38 // boolean value is true, kernel supports IPv6 IPv4-mapping.
39 func probeIPv6Stack() (supportsIPv6
, supportsIPv4map
bool) {
40 var probes
= []struct {
44 // IPv6 communication capability
45 {laddr
: TCPAddr
{IP
: ParseIP("::1")}, value
: 1},
46 // IPv4-mapped IPv6 address communication capability
47 {laddr
: TCPAddr
{IP
: IPv4(127, 0, 0, 1)}, value
: 0},
51 case "dragonfly", "openbsd":
52 // Some released versions of DragonFly BSD pretend to
53 // accept IPV6_V6ONLY=0 successfully, but the state
54 // still stays IPV6_V6ONLY=1. Eventually DragonFly BSD
55 // stops pretending, but the transition period would
56 // cause unpredictable behavior and we need to avoid
59 // OpenBSD also doesn't support IPV6_V6ONLY=0 but it
60 // never pretends to accept IPV6_V6OLY=0. It always
61 // returns an error and we don't need to probe the
66 for i
:= range probes
{
67 s
, err
:= socketFunc(syscall
.AF_INET6
, syscall
.SOCK_STREAM
, syscall
.IPPROTO_TCP
)
72 syscall
.SetsockoptInt(s
, syscall
.IPPROTO_IPV6
, syscall
.IPV6_V6ONLY
, probes
[i
].value
)
73 sa
, err
:= probes
[i
].laddr
.sockaddr(syscall
.AF_INET6
)
77 if err
:= syscall
.Bind(s
, sa
); err
!= nil {
83 return supps
[0], supps
[1]
86 // favoriteAddrFamily returns the appropriate address family to
87 // the given net, laddr, raddr and mode. At first it figures
88 // address family out from the net. If mode indicates "listen"
89 // and laddr is a wildcard, it assumes that the user wants to
90 // make a passive connection with a wildcard address family, both
91 // AF_INET and AF_INET6, and a wildcard address like following:
93 // 1. A wild-wild listen, "tcp" + ""
94 // If the platform supports both IPv6 and IPv6 IPv4-mapping
95 // capabilities, or does not support IPv4, we assume that
96 // the user wants to listen on both IPv4 and IPv6 wildcard
97 // addresses over an AF_INET6 socket with IPV6_V6ONLY=0.
98 // Otherwise we prefer an IPv4 wildcard address listen over
101 // 2. A wild-ipv4wild listen, "tcp" + "0.0.0.0"
104 // 3. A wild-ipv6wild listen, "tcp" + "[::]"
105 // Almost same as 1 but we prefer an IPv6 wildcard address
106 // listen over an AF_INET6 socket with IPV6_V6ONLY=0 when
107 // the platform supports IPv6 capability but not IPv6 IPv4-
108 // mapping capability.
110 // 4. A ipv4-ipv4wild listen, "tcp4" + "" or "0.0.0.0"
111 // We use an IPv4 (AF_INET) wildcard address listen.
113 // 5. A ipv6-ipv6wild listen, "tcp6" + "" or "[::]"
114 // We use an IPv6 (AF_INET6, IPV6_V6ONLY=1) wildcard address
117 // Otherwise guess: if the addresses are IPv4 then returns AF_INET,
118 // or else returns AF_INET6. It also returns a boolean value what
119 // designates IPV6_V6ONLY option.
121 // Note that OpenBSD allows neither "net.inet6.ip6.v6only=1" change
122 // nor IPPROTO_IPV6 level IPV6_V6ONLY socket option setting.
123 func favoriteAddrFamily(net
string, laddr
, raddr sockaddr
, mode
string) (family
int, ipv6only
bool) {
124 switch net
[len(net
)-1] {
126 return syscall
.AF_INET
, false
128 return syscall
.AF_INET6
, true
131 if mode
== "listen" && (laddr
== nil || laddr
.isWildcard()) {
132 if supportsIPv4map ||
!supportsIPv4
{
133 return syscall
.AF_INET6
, false
136 return syscall
.AF_INET
, false
138 return laddr
.family(), false
141 if (laddr
== nil || laddr
.family() == syscall
.AF_INET
) &&
142 (raddr
== nil || raddr
.family() == syscall
.AF_INET
) {
143 return syscall
.AF_INET
, false
145 return syscall
.AF_INET6
, false
148 // Internet sockets (TCP, UDP, IP)
149 func internetSocket(ctx context
.Context
, net
string, laddr
, raddr sockaddr
, sotype
, proto
int, mode
string) (fd
*netFD
, err error
) {
150 if (runtime
.GOOS
== "windows" || runtime
.GOOS
== "openbsd" || runtime
.GOOS
== "nacl") && mode
== "dial" && raddr
.isWildcard() {
151 raddr
= raddr
.toLocal(net
)
153 family
, ipv6only
:= favoriteAddrFamily(net
, laddr
, raddr
, mode
)
154 return socket(ctx
, net
, family
, sotype
, proto
, ipv6only
, laddr
, raddr
)
157 func ipToSockaddr(family
int, ip IP
, port
int, zone
string) (syscall
.Sockaddr
, error
) {
159 case syscall
.AF_INET
:
165 return nil, &AddrError
{Err
: "non-IPv4 address", Addr
: ip
.String()}
167 sa
:= &syscall
.SockaddrInet4
{Port
: port
}
168 copy(sa
.Addr
[:], ip4
)
170 case syscall
.AF_INET6
:
171 // In general, an IP wildcard address, which is either
172 // "0.0.0.0" or "::", means the entire IP addressing
173 // space. For some historical reason, it is used to
174 // specify "any available address" on some operations
177 // When the IP node supports IPv4-mapped IPv6 address,
178 // we allow an listener to listen to the wildcard
179 // address of both IP addressing spaces by specifying
180 // IPv6 wildcard address.
181 if len(ip
) == 0 || ip
.Equal(IPv4zero
) {
184 // We accept any IPv6 address including IPv4-mapped
188 return nil, &AddrError
{Err
: "non-IPv6 address", Addr
: ip
.String()}
190 sa
:= &syscall
.SockaddrInet6
{Port
: port
, ZoneId
: uint32(zoneToInt(zone
))}
191 copy(sa
.Addr
[:], ip6
)
194 return nil, &AddrError
{Err
: "invalid address family", Addr
: ip
.String()}