diff --git a/pkg/app/app.go b/pkg/app/app.go index bf600502f..e2be01015 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -243,7 +243,10 @@ func (app *App) closeConn(data []byte) error { delete(app.conns, loop) app.mu.Unlock() - return conn.Close() + if conn != nil { + return conn.Close() + } + return nil } func (app *App) confirmLoop(data []byte) error { diff --git a/vendor/github.com/skycoin/dmsg/client.go b/vendor/github.com/skycoin/dmsg/client.go index 0ec707809..bd07446ed 100644 --- a/vendor/github.com/skycoin/dmsg/client.go +++ b/vendor/github.com/skycoin/dmsg/client.go @@ -256,14 +256,18 @@ func (c *ClientConn) DialTransport(ctx context.Context, clientPK cipher.PubKey) } func (c *ClientConn) close() (closed bool) { + if c == nil { + return false + } c.once.Do(func() { closed = true c.log.WithField("remoteServer", c.remoteSrv).Infoln("ClosingConnection") close(c.done) c.mx.Lock() for _, tp := range c.tps { + // Nil check is required here to keep 8192 running goroutines limit in tests with -race flag. if tp != nil { - go tp.Close() //nolint:errcheck + go tp.Close() // nolint:errcheck } } _ = c.Conn.Close() //nolint:errcheck @@ -539,6 +543,10 @@ func (c *Client) Type() string { // Close closes the dms_client and associated connections. // TODO(evaninjin): proper error handling. func (c *Client) Close() error { + if c == nil { + return nil + } + c.once.Do(func() { close(c.done) for { diff --git a/vendor/github.com/skycoin/dmsg/transport.go b/vendor/github.com/skycoin/dmsg/transport.go index 990966d0f..cc2724428 100644 --- a/vendor/github.com/skycoin/dmsg/transport.go +++ b/vendor/github.com/skycoin/dmsg/transport.go @@ -85,6 +85,10 @@ func (tp *Transport) serve() (started bool) { // 4. But as, under the mutexes protecting `inCh`/`bufCh`, checking `done` comes first, // and we know that `done` is closed before `inCh`/`bufCh`, we can guarantee that it avoids writing to closed chan. func (tp *Transport) close() (closed bool) { + if tp == nil { + return false + } + tp.doneOnce.Do(func() { closed = true diff --git a/vendor/golang.org/x/net/nettest/conntest.go b/vendor/golang.org/x/net/nettest/conntest.go new file mode 100644 index 000000000..39cc6a631 --- /dev/null +++ b/vendor/golang.org/x/net/nettest/conntest.go @@ -0,0 +1,464 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package nettest + +import ( + "bytes" + "encoding/binary" + "io" + "io/ioutil" + "math/rand" + "net" + "runtime" + "sync" + "testing" + "time" +) + +// MakePipe creates a connection between two endpoints and returns the pair +// as c1 and c2, such that anything written to c1 is read by c2 and vice-versa. +// The stop function closes all resources, including c1, c2, and the underlying +// net.Listener (if there is one), and should not be nil. +type MakePipe func() (c1, c2 net.Conn, stop func(), err error) + +// TestConn tests that a net.Conn implementation properly satisfies the interface. +// The tests should not produce any false positives, but may experience +// false negatives. Thus, some issues may only be detected when the test is +// run multiple times. For maximal effectiveness, run the tests under the +// race detector. +func TestConn(t *testing.T, mp MakePipe) { + t.Run("BasicIO", func(t *testing.T) { timeoutWrapper(t, mp, testBasicIO) }) + t.Run("PingPong", func(t *testing.T) { timeoutWrapper(t, mp, testPingPong) }) + t.Run("RacyRead", func(t *testing.T) { timeoutWrapper(t, mp, testRacyRead) }) + t.Run("RacyWrite", func(t *testing.T) { timeoutWrapper(t, mp, testRacyWrite) }) + t.Run("ReadTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testReadTimeout) }) + t.Run("WriteTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testWriteTimeout) }) + t.Run("PastTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testPastTimeout) }) + t.Run("PresentTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testPresentTimeout) }) + t.Run("FutureTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testFutureTimeout) }) + t.Run("CloseTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testCloseTimeout) }) + t.Run("ConcurrentMethods", func(t *testing.T) { timeoutWrapper(t, mp, testConcurrentMethods) }) +} + +type connTester func(t *testing.T, c1, c2 net.Conn) + +func timeoutWrapper(t *testing.T, mp MakePipe, f connTester) { + t.Helper() + c1, c2, stop, err := mp() + if err != nil { + t.Fatalf("unable to make pipe: %v", err) + } + var once sync.Once + defer once.Do(func() { stop() }) + timer := time.AfterFunc(time.Minute, func() { + once.Do(func() { + t.Error("test timed out; terminating pipe") + stop() + }) + }) + defer timer.Stop() + f(t, c1, c2) +} + +// testBasicIO tests that the data sent on c1 is properly received on c2. +func testBasicIO(t *testing.T, c1, c2 net.Conn) { + want := make([]byte, 1<<20) + rand.New(rand.NewSource(0)).Read(want) + + dataCh := make(chan []byte) + go func() { + rd := bytes.NewReader(want) + if err := chunkedCopy(c1, rd); err != nil { + t.Errorf("unexpected c1.Write error: %v", err) + } + if err := c1.Close(); err != nil { + t.Errorf("unexpected c1.Close error: %v", err) + } + }() + + go func() { + wr := new(bytes.Buffer) + if err := chunkedCopy(wr, c2); err != nil { + t.Errorf("unexpected c2.Read error: %v", err) + } + if err := c2.Close(); err != nil { + t.Errorf("unexpected c2.Close error: %v", err) + } + dataCh <- wr.Bytes() + }() + + if got := <-dataCh; !bytes.Equal(got, want) { + t.Error("transmitted data differs") + } +} + +// testPingPong tests that the two endpoints can synchronously send data to +// each other in a typical request-response pattern. +func testPingPong(t *testing.T, c1, c2 net.Conn) { + var wg sync.WaitGroup + defer wg.Wait() + + pingPonger := func(c net.Conn) { + defer wg.Done() + buf := make([]byte, 8) + var prev uint64 + for { + if _, err := io.ReadFull(c, buf); err != nil { + if err == io.EOF { + break + } + t.Errorf("unexpected Read error: %v", err) + } + + v := binary.LittleEndian.Uint64(buf) + binary.LittleEndian.PutUint64(buf, v+1) + if prev != 0 && prev+2 != v { + t.Errorf("mismatching value: got %d, want %d", v, prev+2) + } + prev = v + if v == 1000 { + break + } + + if _, err := c.Write(buf); err != nil { + t.Errorf("unexpected Write error: %v", err) + break + } + } + if err := c.Close(); err != nil { + t.Errorf("unexpected Close error: %v", err) + } + } + + wg.Add(2) + go pingPonger(c1) + go pingPonger(c2) + + // Start off the chain reaction. + if _, err := c1.Write(make([]byte, 8)); err != nil { + t.Errorf("unexpected c1.Write error: %v", err) + } +} + +// testRacyRead tests that it is safe to mutate the input Read buffer +// immediately after cancelation has occurred. +func testRacyRead(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, rand.New(rand.NewSource(0))) + + var wg sync.WaitGroup + defer wg.Wait() + + c1.SetReadDeadline(time.Now().Add(time.Millisecond)) + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + b1 := make([]byte, 1024) + b2 := make([]byte, 1024) + for j := 0; j < 100; j++ { + _, err := c1.Read(b1) + copy(b1, b2) // Mutate b1 to trigger potential race + if err != nil { + checkForTimeoutError(t, err) + c1.SetReadDeadline(time.Now().Add(time.Millisecond)) + } + } + }() + } +} + +// testRacyWrite tests that it is safe to mutate the input Write buffer +// immediately after cancelation has occurred. +func testRacyWrite(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(ioutil.Discard, c2) + + var wg sync.WaitGroup + defer wg.Wait() + + c1.SetWriteDeadline(time.Now().Add(time.Millisecond)) + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + b1 := make([]byte, 1024) + b2 := make([]byte, 1024) + for j := 0; j < 100; j++ { + _, err := c1.Write(b1) + copy(b1, b2) // Mutate b1 to trigger potential race + if err != nil { + checkForTimeoutError(t, err) + c1.SetWriteDeadline(time.Now().Add(time.Millisecond)) + } + } + }() + } +} + +// testReadTimeout tests that Read timeouts do not affect Write. +func testReadTimeout(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(ioutil.Discard, c2) + + c1.SetReadDeadline(aLongTimeAgo) + _, err := c1.Read(make([]byte, 1024)) + checkForTimeoutError(t, err) + if _, err := c1.Write(make([]byte, 1024)); err != nil { + t.Errorf("unexpected Write error: %v", err) + } +} + +// testWriteTimeout tests that Write timeouts do not affect Read. +func testWriteTimeout(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, rand.New(rand.NewSource(0))) + + c1.SetWriteDeadline(aLongTimeAgo) + _, err := c1.Write(make([]byte, 1024)) + checkForTimeoutError(t, err) + if _, err := c1.Read(make([]byte, 1024)); err != nil { + t.Errorf("unexpected Read error: %v", err) + } +} + +// testPastTimeout tests that a deadline set in the past immediately times out +// Read and Write requests. +func testPastTimeout(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, c2) + + testRoundtrip(t, c1) + + c1.SetDeadline(aLongTimeAgo) + n, err := c1.Write(make([]byte, 1024)) + if n != 0 { + t.Errorf("unexpected Write count: got %d, want 0", n) + } + checkForTimeoutError(t, err) + n, err = c1.Read(make([]byte, 1024)) + if n != 0 { + t.Errorf("unexpected Read count: got %d, want 0", n) + } + checkForTimeoutError(t, err) + + testRoundtrip(t, c1) +} + +// testPresentTimeout tests that a past deadline set while there are pending +// Read and Write operations immediately times out those operations. +func testPresentTimeout(t *testing.T, c1, c2 net.Conn) { + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(3) + + deadlineSet := make(chan bool, 1) + go func() { + defer wg.Done() + time.Sleep(100 * time.Millisecond) + deadlineSet <- true + c1.SetReadDeadline(aLongTimeAgo) + c1.SetWriteDeadline(aLongTimeAgo) + }() + go func() { + defer wg.Done() + n, err := c1.Read(make([]byte, 1024)) + if n != 0 { + t.Errorf("unexpected Read count: got %d, want 0", n) + } + checkForTimeoutError(t, err) + if len(deadlineSet) == 0 { + t.Error("Read timed out before deadline is set") + } + }() + go func() { + defer wg.Done() + var err error + for err == nil { + _, err = c1.Write(make([]byte, 1024)) + } + checkForTimeoutError(t, err) + if len(deadlineSet) == 0 { + t.Error("Write timed out before deadline is set") + } + }() +} + +// testFutureTimeout tests that a future deadline will eventually time out +// Read and Write operations. +func testFutureTimeout(t *testing.T, c1, c2 net.Conn) { + var wg sync.WaitGroup + wg.Add(2) + + c1.SetDeadline(time.Now().Add(100 * time.Millisecond)) + go func() { + defer wg.Done() + _, err := c1.Read(make([]byte, 1024)) + checkForTimeoutError(t, err) + }() + go func() { + defer wg.Done() + var err error + for err == nil { + _, err = c1.Write(make([]byte, 1024)) + } + checkForTimeoutError(t, err) + }() + wg.Wait() + + go chunkedCopy(c2, c2) + resyncConn(t, c1) + testRoundtrip(t, c1) +} + +// testCloseTimeout tests that calling Close immediately times out pending +// Read and Write operations. +func testCloseTimeout(t *testing.T, c1, c2 net.Conn) { + go chunkedCopy(c2, c2) + + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(3) + + // Test for cancelation upon connection closure. + c1.SetDeadline(neverTimeout) + go func() { + defer wg.Done() + time.Sleep(100 * time.Millisecond) + c1.Close() + }() + go func() { + defer wg.Done() + var err error + buf := make([]byte, 1024) + for err == nil { + _, err = c1.Read(buf) + } + }() + go func() { + defer wg.Done() + var err error + buf := make([]byte, 1024) + for err == nil { + _, err = c1.Write(buf) + } + }() +} + +// testConcurrentMethods tests that the methods of net.Conn can safely +// be called concurrently. +func testConcurrentMethods(t *testing.T, c1, c2 net.Conn) { + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; see https://golang.org/issue/20489") + } + go chunkedCopy(c2, c2) + + // The results of the calls may be nonsensical, but this should + // not trigger a race detector warning. + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(7) + go func() { + defer wg.Done() + c1.Read(make([]byte, 1024)) + }() + go func() { + defer wg.Done() + c1.Write(make([]byte, 1024)) + }() + go func() { + defer wg.Done() + c1.SetDeadline(time.Now().Add(10 * time.Millisecond)) + }() + go func() { + defer wg.Done() + c1.SetReadDeadline(aLongTimeAgo) + }() + go func() { + defer wg.Done() + c1.SetWriteDeadline(aLongTimeAgo) + }() + go func() { + defer wg.Done() + c1.LocalAddr() + }() + go func() { + defer wg.Done() + c1.RemoteAddr() + }() + } + wg.Wait() // At worst, the deadline is set 10ms into the future + + resyncConn(t, c1) + testRoundtrip(t, c1) +} + +// checkForTimeoutError checks that the error satisfies the Error interface +// and that Timeout returns true. +func checkForTimeoutError(t *testing.T, err error) { + t.Helper() + if nerr, ok := err.(net.Error); ok { + if !nerr.Timeout() { + t.Errorf("err.Timeout() = false, want true") + } + } else { + t.Errorf("got %T, want net.Error", err) + } +} + +// testRoundtrip writes something into c and reads it back. +// It assumes that everything written into c is echoed back to itself. +func testRoundtrip(t *testing.T, c net.Conn) { + t.Helper() + if err := c.SetDeadline(neverTimeout); err != nil { + t.Errorf("roundtrip SetDeadline error: %v", err) + } + + const s = "Hello, world!" + buf := []byte(s) + if _, err := c.Write(buf); err != nil { + t.Errorf("roundtrip Write error: %v", err) + } + if _, err := io.ReadFull(c, buf); err != nil { + t.Errorf("roundtrip Read error: %v", err) + } + if string(buf) != s { + t.Errorf("roundtrip data mismatch: got %q, want %q", buf, s) + } +} + +// resyncConn resynchronizes the connection into a sane state. +// It assumes that everything written into c is echoed back to itself. +// It assumes that 0xff is not currently on the wire or in the read buffer. +func resyncConn(t *testing.T, c net.Conn) { + t.Helper() + c.SetDeadline(neverTimeout) + errCh := make(chan error) + go func() { + _, err := c.Write([]byte{0xff}) + errCh <- err + }() + buf := make([]byte, 1024) + for { + n, err := c.Read(buf) + if n > 0 && bytes.IndexByte(buf[:n], 0xff) == n-1 { + break + } + if err != nil { + t.Errorf("unexpected Read error: %v", err) + break + } + } + if err := <-errCh; err != nil { + t.Errorf("unexpected Write error: %v", err) + } +} + +// chunkedCopy copies from r to w in fixed-width chunks to avoid +// causing a Write that exceeds the maximum packet size for packet-based +// connections like "unixpacket". +// We assume that the maximum packet size is at least 1024. +func chunkedCopy(w io.Writer, r io.Reader) error { + b := make([]byte, 1024) + _, err := io.CopyBuffer(struct{ io.Writer }{w}, struct{ io.Reader }{r}, b) + return err +} diff --git a/vendor/golang.org/x/net/nettest/nettest.go b/vendor/golang.org/x/net/nettest/nettest.go new file mode 100644 index 000000000..717bbb064 --- /dev/null +++ b/vendor/golang.org/x/net/nettest/nettest.go @@ -0,0 +1,345 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package nettest provides utilities for network testing. +package nettest + +import ( + "errors" + "fmt" + "io/ioutil" + "net" + "os" + "os/exec" + "runtime" + "strconv" + "strings" + "sync" + "time" +) + +var ( + stackOnce sync.Once + ipv4Enabled bool + ipv6Enabled bool + rawSocketSess bool + aixTechLvl int + + aLongTimeAgo = time.Unix(233431200, 0) + neverTimeout = time.Time{} + + errNoAvailableInterface = errors.New("no available interface") + errNoAvailableAddress = errors.New("no available address") +) + +func probeStack() { + if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil { + ln.Close() + ipv4Enabled = true + } + if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil { + ln.Close() + ipv6Enabled = true + } + rawSocketSess = supportsRawSocket() + if runtime.GOOS == "aix" { + out, err := exec.Command("oslevel", "-s").Output() + if err == nil { + aixTechLvl, _ = strconv.Atoi(string(out[5:7])) + } + } +} + +func aixTechLevel() int { + stackOnce.Do(probeStack) + return aixTechLvl +} + +// SupportsIPv4 reports whether the platform supports IPv4 networking +// functionality. +func SupportsIPv4() bool { + stackOnce.Do(probeStack) + return ipv4Enabled +} + +// SupportsIPv6 reports whether the platform supports IPv6 networking +// functionality. +func SupportsIPv6() bool { + stackOnce.Do(probeStack) + return ipv6Enabled +} + +// SupportsRawSocket reports whether the current session is available +// to use raw sockets. +func SupportsRawSocket() bool { + stackOnce.Do(probeStack) + return rawSocketSess +} + +// TestableNetwork reports whether network is testable on the current +// platform configuration. +// +// See func Dial of the standard library for the supported networks. +func TestableNetwork(network string) bool { + ss := strings.Split(network, ":") + switch ss[0] { + case "ip+nopriv": + // This is an internal network name for testing on the + // package net of the standard library. + switch runtime.GOOS { + case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows": + return false + case "darwin": + // iOS doesn't support it. + if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { + return false + } + } + case "ip", "ip4", "ip6": + switch runtime.GOOS { + case "fuchsia", "hurd", "js", "nacl", "plan9": + return false + default: + if os.Getuid() != 0 { + return false + } + } + case "unix", "unixgram": + switch runtime.GOOS { + case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows": + return false + case "aix": + // Unix network isn't properly working on AIX + // 7.2 with Technical Level < 2. + if aixTechLevel() < 2 { + return false + } + return true + case "darwin": + // iOS does not support unix, unixgram. + if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { + return false + } + } + case "unixpacket": + switch runtime.GOOS { + case "aix", "android", "fuchsia", "hurd", "darwin", "js", "nacl", "plan9", "windows": + return false + case "netbsd": + // It passes on amd64 at least. 386 fails + // (Issue 22927). arm is unknown. + if runtime.GOARCH == "386" { + return false + } + } + } + switch ss[0] { + case "tcp4", "udp4", "ip4": + return SupportsIPv4() + case "tcp6", "udp6", "ip6": + return SupportsIPv6() + } + return true +} + +// TestableAddress reports whether address of network is testable on +// the current platform configuration. +func TestableAddress(network, address string) bool { + switch ss := strings.Split(network, ":"); ss[0] { + case "unix", "unixgram", "unixpacket": + // Abstract unix domain sockets, a Linux-ism. + if address[0] == '@' && runtime.GOOS != "linux" { + return false + } + } + return true +} + +// NewLocalListener returns a listener which listens to a loopback IP +// address or local file system path. +// +// The provided network must be "tcp", "tcp4", "tcp6", "unix" or +// "unixpacket". +func NewLocalListener(network string) (net.Listener, error) { + switch network { + case "tcp": + if SupportsIPv4() { + if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil { + return ln, nil + } + } + if SupportsIPv6() { + return net.Listen("tcp6", "[::1]:0") + } + case "tcp4": + if SupportsIPv4() { + return net.Listen("tcp4", "127.0.0.1:0") + } + case "tcp6": + if SupportsIPv6() { + return net.Listen("tcp6", "[::1]:0") + } + case "unix", "unixpacket": + path, err := LocalPath() + if err != nil { + return nil, err + } + return net.Listen(network, path) + } + return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH) +} + +// NewLocalPacketListener returns a packet listener which listens to a +// loopback IP address or local file system path. +// +// The provided network must be "udp", "udp4", "udp6" or "unixgram". +func NewLocalPacketListener(network string) (net.PacketConn, error) { + switch network { + case "udp": + if SupportsIPv4() { + if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil { + return c, nil + } + } + if SupportsIPv6() { + return net.ListenPacket("udp6", "[::1]:0") + } + case "udp4": + if SupportsIPv4() { + return net.ListenPacket("udp4", "127.0.0.1:0") + } + case "udp6": + if SupportsIPv6() { + return net.ListenPacket("udp6", "[::1]:0") + } + case "unixgram": + path, err := LocalPath() + if err != nil { + return nil, err + } + return net.ListenPacket(network, path) + } + return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH) +} + +// LocalPath returns a local path that can be used for Unix-domain +// protocol testing. +func LocalPath() (string, error) { + f, err := ioutil.TempFile("", "go-nettest") + if err != nil { + return "", err + } + path := f.Name() + f.Close() + os.Remove(path) + return path, nil +} + +// MulticastSource returns a unicast IP address on ifi when ifi is an +// IP multicast-capable network interface. +// +// The provided network must be "ip", "ip4" or "ip6". +func MulticastSource(network string, ifi *net.Interface) (net.IP, error) { + switch network { + case "ip", "ip4", "ip6": + default: + return nil, errNoAvailableAddress + } + if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 { + return nil, errNoAvailableAddress + } + ip, ok := hasRoutableIP(network, ifi) + if !ok { + return nil, errNoAvailableAddress + } + return ip, nil +} + +// LoopbackInterface returns an available logical network interface +// for loopback test. +func LoopbackInterface() (*net.Interface, error) { + ift, err := net.Interfaces() + if err != nil { + return nil, errNoAvailableInterface + } + for _, ifi := range ift { + if ifi.Flags&net.FlagLoopback != 0 && ifi.Flags&net.FlagUp != 0 { + return &ifi, nil + } + } + return nil, errNoAvailableInterface +} + +// RoutedInterface returns a network interface that can route IP +// traffic and satisfies flags. +// +// The provided network must be "ip", "ip4" or "ip6". +func RoutedInterface(network string, flags net.Flags) (*net.Interface, error) { + switch network { + case "ip", "ip4", "ip6": + default: + return nil, errNoAvailableInterface + } + ift, err := net.Interfaces() + if err != nil { + return nil, errNoAvailableInterface + } + for _, ifi := range ift { + if ifi.Flags&flags != flags { + continue + } + if _, ok := hasRoutableIP(network, &ifi); !ok { + continue + } + return &ifi, nil + } + return nil, errNoAvailableInterface +} + +func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) { + ifat, err := ifi.Addrs() + if err != nil { + return nil, false + } + for _, ifa := range ifat { + switch ifa := ifa.(type) { + case *net.IPAddr: + if ip, ok := routableIP(network, ifa.IP); ok { + return ip, true + } + case *net.IPNet: + if ip, ok := routableIP(network, ifa.IP); ok { + return ip, true + } + } + } + return nil, false +} + +func routableIP(network string, ip net.IP) (net.IP, bool) { + if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() { + return nil, false + } + switch network { + case "ip4": + if ip := ip.To4(); ip != nil { + return ip, true + } + case "ip6": + if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation + return nil, false + } + if ip := ip.To16(); ip != nil && ip.To4() == nil { + return ip, true + } + default: + if ip := ip.To4(); ip != nil { + return ip, true + } + if ip := ip.To16(); ip != nil { + return ip, true + } + } + return nil, false +} diff --git a/vendor/golang.org/x/net/nettest/nettest_stub.go b/vendor/golang.org/x/net/nettest/nettest_stub.go new file mode 100644 index 000000000..2bb8c0576 --- /dev/null +++ b/vendor/golang.org/x/net/nettest/nettest_stub.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package nettest + +func supportsRawSocket() bool { + return false +} diff --git a/vendor/golang.org/x/net/nettest/nettest_unix.go b/vendor/golang.org/x/net/nettest/nettest_unix.go new file mode 100644 index 000000000..afff744e8 --- /dev/null +++ b/vendor/golang.org/x/net/nettest/nettest_unix.go @@ -0,0 +1,21 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + +package nettest + +import "syscall" + +func supportsRawSocket() bool { + for _, af := range []int{syscall.AF_INET, syscall.AF_INET6} { + s, err := syscall.Socket(af, syscall.SOCK_RAW, 0) + if err != nil { + continue + } + syscall.Close(s) + return true + } + return false +} diff --git a/vendor/golang.org/x/net/nettest/nettest_windows.go b/vendor/golang.org/x/net/nettest/nettest_windows.go new file mode 100644 index 000000000..4939964db --- /dev/null +++ b/vendor/golang.org/x/net/nettest/nettest_windows.go @@ -0,0 +1,26 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package nettest + +import "syscall" + +func supportsRawSocket() bool { + // From http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548.aspx: + // Note: To use a socket of type SOCK_RAW requires administrative privileges. + // Users running Winsock applications that use raw sockets must be a member of + // the Administrators group on the local computer, otherwise raw socket calls + // will fail with an error code of WSAEACCES. On Windows Vista and later, access + // for raw sockets is enforced at socket creation. In earlier versions of Windows, + // access for raw sockets is enforced during other socket operations. + for _, af := range []int{syscall.AF_INET, syscall.AF_INET6} { + s, err := syscall.Socket(af, syscall.SOCK_RAW, 0) + if err != nil { + continue + } + syscall.Closesocket(s) + return true + } + return false +} diff --git a/vendor/modules.txt b/vendor/modules.txt index c7c61f234..5d9179f82 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -95,6 +95,7 @@ golang.org/x/crypto/internal/subtle golang.org/x/crypto/poly1305 # golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/net/context +golang.org/x/net/nettest golang.org/x/net/proxy golang.org/x/net/internal/socks # golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb