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

View as plain text