...

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

View as plain text