]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/golang.org/x/net/nettest/nettest.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / golang.org / x / net / nettest / nettest.go
1 // Copyright 2019 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 // Package nettest provides utilities for network testing.
6 package nettest
7
8 import (
9 "errors"
10 "fmt"
11 "io/ioutil"
12 "net"
13 "os"
14 "os/exec"
15 "runtime"
16 "strconv"
17 "strings"
18 "sync"
19 "time"
20 )
21
22 var (
23 stackOnce sync.Once
24 ipv4Enabled bool
25 ipv6Enabled bool
26 unStrmDgramEnabled bool
27 rawSocketSess bool
28
29 aLongTimeAgo = time.Unix(233431200, 0)
30 neverTimeout = time.Time{}
31
32 errNoAvailableInterface = errors.New("no available interface")
33 errNoAvailableAddress = errors.New("no available address")
34 )
35
36 func probeStack() {
37 if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
38 ln.Close()
39 ipv4Enabled = true
40 }
41 if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil {
42 ln.Close()
43 ipv6Enabled = true
44 }
45 rawSocketSess = supportsRawSocket()
46 switch runtime.GOOS {
47 case "aix":
48 // Unix network isn't properly working on AIX 7.2 with
49 // Technical Level < 2.
50 out, _ := exec.Command("oslevel", "-s").Output()
51 if len(out) >= len("7200-XX-ZZ-YYMM") { // AIX 7.2, Tech Level XX, Service Pack ZZ, date YYMM
52 ver := string(out[:4])
53 tl, _ := strconv.Atoi(string(out[5:7]))
54 unStrmDgramEnabled = ver > "7200" || (ver == "7200" && tl >= 2)
55 }
56 default:
57 unStrmDgramEnabled = true
58 }
59 }
60
61 func unixStrmDgramEnabled() bool {
62 stackOnce.Do(probeStack)
63 return unStrmDgramEnabled
64 }
65
66 // SupportsIPv4 reports whether the platform supports IPv4 networking
67 // functionality.
68 func SupportsIPv4() bool {
69 stackOnce.Do(probeStack)
70 return ipv4Enabled
71 }
72
73 // SupportsIPv6 reports whether the platform supports IPv6 networking
74 // functionality.
75 func SupportsIPv6() bool {
76 stackOnce.Do(probeStack)
77 return ipv6Enabled
78 }
79
80 // SupportsRawSocket reports whether the current session is available
81 // to use raw sockets.
82 func SupportsRawSocket() bool {
83 stackOnce.Do(probeStack)
84 return rawSocketSess
85 }
86
87 // TestableNetwork reports whether network is testable on the current
88 // platform configuration.
89 //
90 // See func Dial of the standard library for the supported networks.
91 func TestableNetwork(network string) bool {
92 ss := strings.Split(network, ":")
93 switch ss[0] {
94 case "ip+nopriv":
95 // This is an internal network name for testing on the
96 // package net of the standard library.
97 switch runtime.GOOS {
98 case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
99 return false
100 case "darwin":
101 // iOS doesn't support it.
102 if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
103 return false
104 }
105 }
106 case "ip", "ip4", "ip6":
107 switch runtime.GOOS {
108 case "fuchsia", "hurd", "js", "nacl", "plan9":
109 return false
110 default:
111 if os.Getuid() != 0 {
112 return false
113 }
114 }
115 case "unix", "unixgram":
116 switch runtime.GOOS {
117 case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows":
118 return false
119 case "aix":
120 return unixStrmDgramEnabled()
121 case "darwin":
122 // iOS does not support unix, unixgram.
123 if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
124 return false
125 }
126 }
127 case "unixpacket":
128 switch runtime.GOOS {
129 case "aix", "android", "fuchsia", "hurd", "darwin", "js", "nacl", "plan9", "windows":
130 return false
131 case "netbsd":
132 // It passes on amd64 at least. 386 fails
133 // (Issue 22927). arm is unknown.
134 if runtime.GOARCH == "386" {
135 return false
136 }
137 }
138 }
139 switch ss[0] {
140 case "tcp4", "udp4", "ip4":
141 return SupportsIPv4()
142 case "tcp6", "udp6", "ip6":
143 return SupportsIPv6()
144 }
145 return true
146 }
147
148 // TestableAddress reports whether address of network is testable on
149 // the current platform configuration.
150 func TestableAddress(network, address string) bool {
151 switch ss := strings.Split(network, ":"); ss[0] {
152 case "unix", "unixgram", "unixpacket":
153 // Abstract unix domain sockets, a Linux-ism.
154 if address[0] == '@' && runtime.GOOS != "linux" {
155 return false
156 }
157 }
158 return true
159 }
160
161 // NewLocalListener returns a listener which listens to a loopback IP
162 // address or local file system path.
163 //
164 // The provided network must be "tcp", "tcp4", "tcp6", "unix" or
165 // "unixpacket".
166 func NewLocalListener(network string) (net.Listener, error) {
167 switch network {
168 case "tcp":
169 if SupportsIPv4() {
170 if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
171 return ln, nil
172 }
173 }
174 if SupportsIPv6() {
175 return net.Listen("tcp6", "[::1]:0")
176 }
177 case "tcp4":
178 if SupportsIPv4() {
179 return net.Listen("tcp4", "127.0.0.1:0")
180 }
181 case "tcp6":
182 if SupportsIPv6() {
183 return net.Listen("tcp6", "[::1]:0")
184 }
185 case "unix", "unixpacket":
186 path, err := LocalPath()
187 if err != nil {
188 return nil, err
189 }
190 return net.Listen(network, path)
191 }
192 return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
193 }
194
195 // NewLocalPacketListener returns a packet listener which listens to a
196 // loopback IP address or local file system path.
197 //
198 // The provided network must be "udp", "udp4", "udp6" or "unixgram".
199 func NewLocalPacketListener(network string) (net.PacketConn, error) {
200 switch network {
201 case "udp":
202 if SupportsIPv4() {
203 if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil {
204 return c, nil
205 }
206 }
207 if SupportsIPv6() {
208 return net.ListenPacket("udp6", "[::1]:0")
209 }
210 case "udp4":
211 if SupportsIPv4() {
212 return net.ListenPacket("udp4", "127.0.0.1:0")
213 }
214 case "udp6":
215 if SupportsIPv6() {
216 return net.ListenPacket("udp6", "[::1]:0")
217 }
218 case "unixgram":
219 path, err := LocalPath()
220 if err != nil {
221 return nil, err
222 }
223 return net.ListenPacket(network, path)
224 }
225 return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
226 }
227
228 // LocalPath returns a local path that can be used for Unix-domain
229 // protocol testing.
230 func LocalPath() (string, error) {
231 f, err := ioutil.TempFile("", "go-nettest")
232 if err != nil {
233 return "", err
234 }
235 path := f.Name()
236 f.Close()
237 os.Remove(path)
238 return path, nil
239 }
240
241 // MulticastSource returns a unicast IP address on ifi when ifi is an
242 // IP multicast-capable network interface.
243 //
244 // The provided network must be "ip", "ip4" or "ip6".
245 func MulticastSource(network string, ifi *net.Interface) (net.IP, error) {
246 switch network {
247 case "ip", "ip4", "ip6":
248 default:
249 return nil, errNoAvailableAddress
250 }
251 if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
252 return nil, errNoAvailableAddress
253 }
254 ip, ok := hasRoutableIP(network, ifi)
255 if !ok {
256 return nil, errNoAvailableAddress
257 }
258 return ip, nil
259 }
260
261 // LoopbackInterface returns an available logical network interface
262 // for loopback test.
263 func LoopbackInterface() (*net.Interface, error) {
264 ift, err := net.Interfaces()
265 if err != nil {
266 return nil, errNoAvailableInterface
267 }
268 for _, ifi := range ift {
269 if ifi.Flags&net.FlagLoopback != 0 && ifi.Flags&net.FlagUp != 0 {
270 return &ifi, nil
271 }
272 }
273 return nil, errNoAvailableInterface
274 }
275
276 // RoutedInterface returns a network interface that can route IP
277 // traffic and satisfies flags.
278 //
279 // The provided network must be "ip", "ip4" or "ip6".
280 func RoutedInterface(network string, flags net.Flags) (*net.Interface, error) {
281 switch network {
282 case "ip", "ip4", "ip6":
283 default:
284 return nil, errNoAvailableInterface
285 }
286 ift, err := net.Interfaces()
287 if err != nil {
288 return nil, errNoAvailableInterface
289 }
290 for _, ifi := range ift {
291 if ifi.Flags&flags != flags {
292 continue
293 }
294 if _, ok := hasRoutableIP(network, &ifi); !ok {
295 continue
296 }
297 return &ifi, nil
298 }
299 return nil, errNoAvailableInterface
300 }
301
302 func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) {
303 ifat, err := ifi.Addrs()
304 if err != nil {
305 return nil, false
306 }
307 for _, ifa := range ifat {
308 switch ifa := ifa.(type) {
309 case *net.IPAddr:
310 if ip, ok := routableIP(network, ifa.IP); ok {
311 return ip, true
312 }
313 case *net.IPNet:
314 if ip, ok := routableIP(network, ifa.IP); ok {
315 return ip, true
316 }
317 }
318 }
319 return nil, false
320 }
321
322 func routableIP(network string, ip net.IP) (net.IP, bool) {
323 if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
324 return nil, false
325 }
326 switch network {
327 case "ip4":
328 if ip := ip.To4(); ip != nil {
329 return ip, true
330 }
331 case "ip6":
332 if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
333 return nil, false
334 }
335 if ip := ip.To16(); ip != nil && ip.To4() == nil {
336 return ip, true
337 }
338 default:
339 if ip := ip.To4(); ip != nil {
340 return ip, true
341 }
342 if ip := ip.To16(); ip != nil {
343 return ip, true
344 }
345 }
346 return nil, false
347 }