gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/net/nettest/nettest.go (about)

     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", "ios", "js", "nacl", "plan9", "windows":
    99  			return false
   100  		}
   101  	case "ip", "ip4", "ip6":
   102  		switch runtime.GOOS {
   103  		case "fuchsia", "hurd", "js", "nacl", "plan9":
   104  			return false
   105  		default:
   106  			if os.Getuid() != 0 {
   107  				return false
   108  			}
   109  		}
   110  	case "unix", "unixgram":
   111  		switch runtime.GOOS {
   112  		case "android", "fuchsia", "hurd", "ios", "js", "nacl", "plan9", "windows":
   113  			return false
   114  		case "aix":
   115  			return unixStrmDgramEnabled()
   116  		}
   117  	case "unixpacket":
   118  		switch runtime.GOOS {
   119  		case "aix", "android", "fuchsia", "hurd", "darwin", "ios", "js", "nacl", "plan9", "windows", "zos":
   120  			return false
   121  		case "netbsd":
   122  			// It passes on amd64 at least. 386 fails
   123  			// (Issue 22927). arm is unknown.
   124  			if runtime.GOARCH == "386" {
   125  				return false
   126  			}
   127  		}
   128  	}
   129  	switch ss[0] {
   130  	case "tcp4", "udp4", "ip4":
   131  		return SupportsIPv4()
   132  	case "tcp6", "udp6", "ip6":
   133  		return SupportsIPv6()
   134  	}
   135  	return true
   136  }
   137  
   138  // TestableAddress reports whether address of network is testable on
   139  // the current platform configuration.
   140  func TestableAddress(network, address string) bool {
   141  	switch ss := strings.Split(network, ":"); ss[0] {
   142  	case "unix", "unixgram", "unixpacket":
   143  		// Abstract unix domain sockets, a Linux-ism.
   144  		if address[0] == '@' && runtime.GOOS != "linux" {
   145  			return false
   146  		}
   147  	}
   148  	return true
   149  }
   150  
   151  // NewLocalListener returns a listener which listens to a loopback IP
   152  // address or local file system path.
   153  //
   154  // The provided network must be "tcp", "tcp4", "tcp6", "unix" or
   155  // "unixpacket".
   156  func NewLocalListener(network string) (net.Listener, error) {
   157  	switch network {
   158  	case "tcp":
   159  		if SupportsIPv4() {
   160  			if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
   161  				return ln, nil
   162  			}
   163  		}
   164  		if SupportsIPv6() {
   165  			return net.Listen("tcp6", "[::1]:0")
   166  		}
   167  	case "tcp4":
   168  		if SupportsIPv4() {
   169  			return net.Listen("tcp4", "127.0.0.1:0")
   170  		}
   171  	case "tcp6":
   172  		if SupportsIPv6() {
   173  			return net.Listen("tcp6", "[::1]:0")
   174  		}
   175  	case "unix", "unixpacket":
   176  		path, err := LocalPath()
   177  		if err != nil {
   178  			return nil, err
   179  		}
   180  		return net.Listen(network, path)
   181  	}
   182  	return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
   183  }
   184  
   185  // NewLocalPacketListener returns a packet listener which listens to a
   186  // loopback IP address or local file system path.
   187  //
   188  // The provided network must be "udp", "udp4", "udp6" or "unixgram".
   189  func NewLocalPacketListener(network string) (net.PacketConn, error) {
   190  	switch network {
   191  	case "udp":
   192  		if SupportsIPv4() {
   193  			if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil {
   194  				return c, nil
   195  			}
   196  		}
   197  		if SupportsIPv6() {
   198  			return net.ListenPacket("udp6", "[::1]:0")
   199  		}
   200  	case "udp4":
   201  		if SupportsIPv4() {
   202  			return net.ListenPacket("udp4", "127.0.0.1:0")
   203  		}
   204  	case "udp6":
   205  		if SupportsIPv6() {
   206  			return net.ListenPacket("udp6", "[::1]:0")
   207  		}
   208  	case "unixgram":
   209  		path, err := LocalPath()
   210  		if err != nil {
   211  			return nil, err
   212  		}
   213  		return net.ListenPacket(network, path)
   214  	}
   215  	return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH)
   216  }
   217  
   218  // LocalPath returns a local path that can be used for Unix-domain
   219  // protocol testing.
   220  func LocalPath() (string, error) {
   221  	f, err := ioutil.TempFile("", "go-nettest")
   222  	if err != nil {
   223  		return "", err
   224  	}
   225  	path := f.Name()
   226  	f.Close()
   227  	os.Remove(path)
   228  	return path, nil
   229  }
   230  
   231  // MulticastSource returns a unicast IP address on ifi when ifi is an
   232  // IP multicast-capable network interface.
   233  //
   234  // The provided network must be "ip", "ip4" or "ip6".
   235  func MulticastSource(network string, ifi *net.Interface) (net.IP, error) {
   236  	switch network {
   237  	case "ip", "ip4", "ip6":
   238  	default:
   239  		return nil, errNoAvailableAddress
   240  	}
   241  	if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
   242  		return nil, errNoAvailableAddress
   243  	}
   244  	ip, ok := hasRoutableIP(network, ifi)
   245  	if !ok {
   246  		return nil, errNoAvailableAddress
   247  	}
   248  	return ip, nil
   249  }
   250  
   251  // LoopbackInterface returns an available logical network interface
   252  // for loopback test.
   253  func LoopbackInterface() (*net.Interface, error) {
   254  	ift, err := net.Interfaces()
   255  	if err != nil {
   256  		return nil, errNoAvailableInterface
   257  	}
   258  	for _, ifi := range ift {
   259  		if ifi.Flags&net.FlagLoopback != 0 && ifi.Flags&net.FlagUp != 0 {
   260  			return &ifi, nil
   261  		}
   262  	}
   263  	return nil, errNoAvailableInterface
   264  }
   265  
   266  // RoutedInterface returns a network interface that can route IP
   267  // traffic and satisfies flags.
   268  //
   269  // The provided network must be "ip", "ip4" or "ip6".
   270  func RoutedInterface(network string, flags net.Flags) (*net.Interface, error) {
   271  	switch network {
   272  	case "ip", "ip4", "ip6":
   273  	default:
   274  		return nil, errNoAvailableInterface
   275  	}
   276  	ift, err := net.Interfaces()
   277  	if err != nil {
   278  		return nil, errNoAvailableInterface
   279  	}
   280  	for _, ifi := range ift {
   281  		if ifi.Flags&flags != flags {
   282  			continue
   283  		}
   284  		if _, ok := hasRoutableIP(network, &ifi); !ok {
   285  			continue
   286  		}
   287  		return &ifi, nil
   288  	}
   289  	return nil, errNoAvailableInterface
   290  }
   291  
   292  func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) {
   293  	ifat, err := ifi.Addrs()
   294  	if err != nil {
   295  		return nil, false
   296  	}
   297  	for _, ifa := range ifat {
   298  		switch ifa := ifa.(type) {
   299  		case *net.IPAddr:
   300  			if ip, ok := routableIP(network, ifa.IP); ok {
   301  				return ip, true
   302  			}
   303  		case *net.IPNet:
   304  			if ip, ok := routableIP(network, ifa.IP); ok {
   305  				return ip, true
   306  			}
   307  		}
   308  	}
   309  	return nil, false
   310  }
   311  
   312  func routableIP(network string, ip net.IP) (net.IP, bool) {
   313  	if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
   314  		return nil, false
   315  	}
   316  	switch network {
   317  	case "ip4":
   318  		if ip := ip.To4(); ip != nil {
   319  			return ip, true
   320  		}
   321  	case "ip6":
   322  		if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
   323  			return nil, false
   324  		}
   325  		if ip := ip.To16(); ip != nil && ip.To4() == nil {
   326  			return ip, true
   327  		}
   328  	default:
   329  		if ip := ip.To4(); ip != nil {
   330  			return ip, true
   331  		}
   332  		if ip := ip.To16(); ip != nil {
   333  			return ip, true
   334  		}
   335  	}
   336  	return nil, false
   337  }