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.
4
5// +build darwin dragonfly freebsd linux netbsd openbsd windows
6
7// Internet protocol family sockets for POSIX
8
9package net
10
11import (
12	"syscall"
13	"time"
14)
15
16func probeIPv4Stack() bool {
17	s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
18	switch err {
19	case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
20		return false
21	case nil:
22		closesocket(s)
23	}
24	return true
25}
26
27// Should we try to use the IPv4 socket interface if we're
28// only dealing with IPv4 sockets?  As long as the host system
29// understands IPv6, it's okay to pass IPv4 addresses to the IPv6
30// interface.  That simplifies our code and is most general.
31// Unfortunately, we need to run on kernels built without IPv6
32// support too.  So probe the kernel to figure it out.
33//
34// probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4-
35// mapping capability which is controlled by IPV6_V6ONLY socket
36// option and/or kernel state "net.inet6.ip6.v6only".
37// It returns two boolean values.  If the first boolean value is
38// true, kernel supports basic IPv6 functionality.  If the second
39// boolean value is true, kernel supports IPv6 IPv4-mapping.
40func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
41	var probes = []struct {
42		laddr TCPAddr
43		ok    bool
44	}{
45		// IPv6 communication capability
46		{TCPAddr{IP: ParseIP("::1")}, false},
47		// IPv6 IPv4-mapped address communication capability
48		{TCPAddr{IP: IPv4(127, 0, 0, 1)}, false},
49	}
50
51	for i := range probes {
52		s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
53		if err != nil {
54			continue
55		}
56		defer closesocket(s)
57		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
58		sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6)
59		if err != nil {
60			continue
61		}
62		if err := syscall.Bind(s, sa); err != nil {
63			continue
64		}
65		probes[i].ok = true
66	}
67
68	return probes[0].ok, probes[1].ok
69}
70
71// favoriteAddrFamily returns the appropriate address family to
72// the given net, laddr, raddr and mode.  At first it figures
73// address family out from the net.  If mode indicates "listen"
74// and laddr is a wildcard, it assumes that the user wants to
75// make a passive connection with a wildcard address family, both
76// AF_INET and AF_INET6, and a wildcard address like following:
77//
78//	1. A wild-wild listen, "tcp" + ""
79//	If the platform supports both IPv6 and IPv6 IPv4-mapping
80//	capabilities, we assume that the user want to listen on
81//	both IPv4 and IPv6 wildcard address over an AF_INET6
82//	socket with IPV6_V6ONLY=0.  Otherwise we prefer an IPv4
83//	wildcard address listen over an AF_INET socket.
84//
85//	2. A wild-ipv4wild listen, "tcp" + "0.0.0.0"
86//	Same as 1.
87//
88//	3. A wild-ipv6wild listen, "tcp" + "[::]"
89//	Almost same as 1 but we prefer an IPv6 wildcard address
90//	listen over an AF_INET6 socket with IPV6_V6ONLY=0 when
91//	the platform supports IPv6 capability but not IPv6 IPv4-
92//	mapping capability.
93//
94//	4. A ipv4-ipv4wild listen, "tcp4" + "" or "0.0.0.0"
95//	We use an IPv4 (AF_INET) wildcard address listen.
96//
97//	5. A ipv6-ipv6wild listen, "tcp6" + "" or "[::]"
98//	We use an IPv6 (AF_INET6, IPV6_V6ONLY=1) wildcard address
99//	listen.
100//
101// Otherwise guess: if the addresses are IPv4 then returns AF_INET,
102// or else returns AF_INET6.  It also returns a boolean value what
103// designates IPV6_V6ONLY option.
104//
105// Note that OpenBSD allows neither "net.inet6.ip6.v6only=1" change
106// nor IPPROTO_IPV6 level IPV6_V6ONLY socket option setting.
107func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) {
108	switch net[len(net)-1] {
109	case '4':
110		return syscall.AF_INET, false
111	case '6':
112		return syscall.AF_INET6, true
113	}
114
115	if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
116		if supportsIPv4map {
117			return syscall.AF_INET6, false
118		}
119		if laddr == nil {
120			return syscall.AF_INET, false
121		}
122		return laddr.family(), false
123	}
124
125	if (laddr == nil || laddr.family() == syscall.AF_INET) &&
126		(raddr == nil || raddr.family() == syscall.AF_INET) {
127		return syscall.AF_INET, false
128	}
129	return syscall.AF_INET6, false
130}
131
132// Internet sockets (TCP, UDP, IP)
133
134func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
135	family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
136	return socket(net, family, sotype, proto, ipv6only, laddr, raddr, deadline, toAddr)
137}
138
139func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
140	switch family {
141	case syscall.AF_INET:
142		if len(ip) == 0 {
143			ip = IPv4zero
144		}
145		if ip = ip.To4(); ip == nil {
146			return nil, InvalidAddrError("non-IPv4 address")
147		}
148		sa := new(syscall.SockaddrInet4)
149		for i := 0; i < IPv4len; i++ {
150			sa.Addr[i] = ip[i]
151		}
152		sa.Port = port
153		return sa, nil
154	case syscall.AF_INET6:
155		if len(ip) == 0 {
156			ip = IPv6zero
157		}
158		// IPv4 callers use 0.0.0.0 to mean "announce on any available address".
159		// In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
160		// which it refuses to do.  Rewrite to the IPv6 unspecified address.
161		if ip.Equal(IPv4zero) {
162			ip = IPv6zero
163		}
164		if ip = ip.To16(); ip == nil {
165			return nil, InvalidAddrError("non-IPv6 address")
166		}
167		sa := new(syscall.SockaddrInet6)
168		for i := 0; i < IPv6len; i++ {
169			sa.Addr[i] = ip[i]
170		}
171		sa.Port = port
172		sa.ZoneId = uint32(zoneToInt(zone))
173		return sa, nil
174	}
175	return nil, InvalidAddrError("unexpected socket family")
176}
177