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 }