]> Cypherpunks.ru repositories - gostls13.git/blob - src/net/ipsock_posix.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / net / ipsock_posix.go
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 //go:build unix || js || wasip1 || windows
6
7 package net
8
9 import (
10         "context"
11         "internal/poll"
12         "net/netip"
13         "runtime"
14         "syscall"
15 )
16
17 // probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
18 // capabilities which are controlled by the IPV6_V6ONLY socket option
19 // and kernel configuration.
20 //
21 // Should we try to use the IPv4 socket interface if we're only
22 // dealing with IPv4 sockets? As long as the host system understands
23 // IPv4-mapped IPv6, it's okay to pass IPv4-mapped IPv6 addresses to
24 // the IPv6 interface. That simplifies our code and is most
25 // general. Unfortunately, we need to run on kernels built without
26 // IPv6 support too. So probe the kernel to figure it out.
27 func (p *ipStackCapabilities) probe() {
28         switch runtime.GOOS {
29         case "js", "wasip1":
30                 // Both ipv4 and ipv6 are faked; see net_fake.go.
31                 p.ipv4Enabled = true
32                 p.ipv6Enabled = true
33                 p.ipv4MappedIPv6Enabled = true
34                 return
35         }
36
37         s, err := sysSocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
38         switch err {
39         case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
40         case nil:
41                 poll.CloseFunc(s)
42                 p.ipv4Enabled = true
43         }
44         var probes = []struct {
45                 laddr TCPAddr
46                 value int
47         }{
48                 // IPv6 communication capability
49                 {laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
50                 // IPv4-mapped IPv6 address communication capability
51                 {laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
52         }
53         switch runtime.GOOS {
54         case "dragonfly", "openbsd":
55                 // The latest DragonFly BSD and OpenBSD kernels don't
56                 // support IPV6_V6ONLY=0. They always return an error
57                 // and we don't need to probe the capability.
58                 probes = probes[:1]
59         }
60         for i := range probes {
61                 s, err := sysSocket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
62                 if err != nil {
63                         continue
64                 }
65                 defer poll.CloseFunc(s)
66                 syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value)
67                 sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6)
68                 if err != nil {
69                         continue
70                 }
71                 if err := syscall.Bind(s, sa); err != nil {
72                         continue
73                 }
74                 if i == 0 {
75                         p.ipv6Enabled = true
76                 } else {
77                         p.ipv4MappedIPv6Enabled = true
78                 }
79         }
80 }
81
82 // favoriteAddrFamily returns the appropriate address family for the
83 // given network, laddr, raddr and mode.
84 //
85 // If mode indicates "listen" and laddr is a wildcard, we assume that
86 // the user wants to make a passive-open connection with a wildcard
87 // address family, both AF_INET and AF_INET6, and a wildcard address
88 // like the following:
89 //
90 //   - A listen for a wildcard communication domain, "tcp" or
91 //     "udp", with a wildcard address: If the platform supports
92 //     both IPv6 and IPv4-mapped IPv6 communication capabilities,
93 //     or does not support IPv4, we use a dual stack, AF_INET6 and
94 //     IPV6_V6ONLY=0, wildcard address listen. The dual stack
95 //     wildcard address listen may fall back to an IPv6-only,
96 //     AF_INET6 and IPV6_V6ONLY=1, wildcard address listen.
97 //     Otherwise we prefer an IPv4-only, AF_INET, wildcard address
98 //     listen.
99 //
100 //   - A listen for a wildcard communication domain, "tcp" or
101 //     "udp", with an IPv4 wildcard address: same as above.
102 //
103 //   - A listen for a wildcard communication domain, "tcp" or
104 //     "udp", with an IPv6 wildcard address: same as above.
105 //
106 //   - A listen for an IPv4 communication domain, "tcp4" or "udp4",
107 //     with an IPv4 wildcard address: We use an IPv4-only, AF_INET,
108 //     wildcard address listen.
109 //
110 //   - A listen for an IPv6 communication domain, "tcp6" or "udp6",
111 //     with an IPv6 wildcard address: We use an IPv6-only, AF_INET6
112 //     and IPV6_V6ONLY=1, wildcard address listen.
113 //
114 // Otherwise guess: If the addresses are IPv4 then returns AF_INET,
115 // or else returns AF_INET6. It also returns a boolean value what
116 // designates IPV6_V6ONLY option.
117 //
118 // Note that the latest DragonFly BSD and OpenBSD kernels allow
119 // neither "net.inet6.ip6.v6only=1" change nor IPPROTO_IPV6 level
120 // IPV6_V6ONLY socket option setting.
121 func favoriteAddrFamily(network string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) {
122         switch network[len(network)-1] {
123         case '4':
124                 return syscall.AF_INET, false
125         case '6':
126                 return syscall.AF_INET6, true
127         }
128
129         if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
130                 if supportsIPv4map() || !supportsIPv4() {
131                         return syscall.AF_INET6, false
132                 }
133                 if laddr == nil {
134                         return syscall.AF_INET, false
135                 }
136                 return laddr.family(), false
137         }
138
139         if (laddr == nil || laddr.family() == syscall.AF_INET) &&
140                 (raddr == nil || raddr.family() == syscall.AF_INET) {
141                 return syscall.AF_INET, false
142         }
143         return syscall.AF_INET6, false
144 }
145
146 func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd *netFD, err error) {
147         switch runtime.GOOS {
148         case "aix", "windows", "openbsd", "js", "wasip1":
149                 if mode == "dial" && raddr.isWildcard() {
150                         raddr = raddr.toLocal(net)
151                 }
152         }
153         family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
154         return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, ctrlCtxFn)
155 }
156
157 func ipToSockaddrInet4(ip IP, port int) (syscall.SockaddrInet4, error) {
158         if len(ip) == 0 {
159                 ip = IPv4zero
160         }
161         ip4 := ip.To4()
162         if ip4 == nil {
163                 return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: ip.String()}
164         }
165         sa := syscall.SockaddrInet4{Port: port}
166         copy(sa.Addr[:], ip4)
167         return sa, nil
168 }
169
170 func ipToSockaddrInet6(ip IP, port int, zone string) (syscall.SockaddrInet6, error) {
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
175         // of IP node.
176         //
177         // When the IP node supports IPv4-mapped IPv6 address,
178         // we allow a 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) {
182                 ip = IPv6zero
183         }
184         // We accept any IPv6 address including IPv4-mapped
185         // IPv6 address.
186         ip6 := ip.To16()
187         if ip6 == nil {
188                 return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: ip.String()}
189         }
190         sa := syscall.SockaddrInet6{Port: port, ZoneId: uint32(zoneCache.index(zone))}
191         copy(sa.Addr[:], ip6)
192         return sa, nil
193 }
194
195 func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
196         switch family {
197         case syscall.AF_INET:
198                 sa, err := ipToSockaddrInet4(ip, port)
199                 if err != nil {
200                         return nil, err
201                 }
202                 return &sa, nil
203         case syscall.AF_INET6:
204                 sa, err := ipToSockaddrInet6(ip, port, zone)
205                 if err != nil {
206                         return nil, err
207                 }
208                 return &sa, nil
209         }
210         return nil, &AddrError{Err: "invalid address family", Addr: ip.String()}
211 }
212
213 func addrPortToSockaddrInet4(ap netip.AddrPort) (syscall.SockaddrInet4, error) {
214         // ipToSockaddrInet4 has special handling here for zero length slices.
215         // We do not, because netip has no concept of a generic zero IP address.
216         addr := ap.Addr()
217         if !addr.Is4() {
218                 return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: addr.String()}
219         }
220         sa := syscall.SockaddrInet4{
221                 Addr: addr.As4(),
222                 Port: int(ap.Port()),
223         }
224         return sa, nil
225 }
226
227 func addrPortToSockaddrInet6(ap netip.AddrPort) (syscall.SockaddrInet6, error) {
228         // ipToSockaddrInet6 has special handling here for zero length slices.
229         // We do not, because netip has no concept of a generic zero IP address.
230         //
231         // addr is allowed to be an IPv4 address, because As16 will convert it
232         // to an IPv4-mapped IPv6 address.
233         // The error message is kept consistent with ipToSockaddrInet6.
234         addr := ap.Addr()
235         if !addr.IsValid() {
236                 return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: addr.String()}
237         }
238         sa := syscall.SockaddrInet6{
239                 Addr:   addr.As16(),
240                 Port:   int(ap.Port()),
241                 ZoneId: uint32(zoneCache.index(addr.Zone())),
242         }
243         return sa, nil
244 }