2015-04-13 23:45:00 +09:00
|
|
|
// Copyright 2015 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.
|
|
|
|
|
|
2018-03-04 12:18:32 +01:00
|
|
|
// +build !js
|
|
|
|
|
|
2015-04-13 23:45:00 +09:00
|
|
|
package net
|
|
|
|
|
|
|
|
|
|
import (
|
2016-04-14 17:47:25 -07:00
|
|
|
"context"
|
2015-04-13 23:45:00 +09:00
|
|
|
"fmt"
|
2017-02-10 14:59:38 -08:00
|
|
|
"internal/poll"
|
2015-04-16 23:10:56 +09:00
|
|
|
"io"
|
2015-04-18 16:53:55 +09:00
|
|
|
"io/ioutil"
|
2015-04-13 23:45:00 +09:00
|
|
|
"net/internal/socktest"
|
|
|
|
|
"os"
|
|
|
|
|
"runtime"
|
2017-04-07 15:53:19 -07:00
|
|
|
"strings"
|
2015-04-13 23:45:00 +09:00
|
|
|
"testing"
|
2015-04-17 14:35:54 +09:00
|
|
|
"time"
|
2015-04-13 23:45:00 +09:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (e *OpError) isValid() error {
|
|
|
|
|
if e.Op == "" {
|
|
|
|
|
return fmt.Errorf("OpError.Op is empty: %v", e)
|
|
|
|
|
}
|
|
|
|
|
if e.Net == "" {
|
|
|
|
|
return fmt.Errorf("OpError.Net is empty: %v", e)
|
|
|
|
|
}
|
2015-04-21 22:53:47 +09:00
|
|
|
for _, addr := range []Addr{e.Source, e.Addr} {
|
2015-05-30 07:33:16 +09:00
|
|
|
switch addr := addr.(type) {
|
|
|
|
|
case nil:
|
|
|
|
|
case *TCPAddr:
|
|
|
|
|
if addr == nil {
|
|
|
|
|
return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
|
2015-04-21 22:53:47 +09:00
|
|
|
}
|
2015-05-30 07:33:16 +09:00
|
|
|
case *UDPAddr:
|
|
|
|
|
if addr == nil {
|
|
|
|
|
return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
|
|
|
|
|
}
|
|
|
|
|
case *IPAddr:
|
|
|
|
|
if addr == nil {
|
|
|
|
|
return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
|
|
|
|
|
}
|
|
|
|
|
case *IPNet:
|
|
|
|
|
if addr == nil {
|
|
|
|
|
return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
|
|
|
|
|
}
|
|
|
|
|
case *UnixAddr:
|
|
|
|
|
if addr == nil {
|
|
|
|
|
return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
|
|
|
|
|
}
|
|
|
|
|
case *pipeAddr:
|
|
|
|
|
if addr == nil {
|
|
|
|
|
return fmt.Errorf("OpError.Source or Addr is non-nil interface: %#v, %v", addr, e)
|
|
|
|
|
}
|
|
|
|
|
case fileAddr:
|
|
|
|
|
if addr == "" {
|
|
|
|
|
return fmt.Errorf("OpError.Source or Addr is empty: %#v, %v", addr, e)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("OpError.Source or Addr is unknown type: %T, %v", addr, e)
|
2015-04-18 16:53:55 +09:00
|
|
|
}
|
2015-04-13 23:45:00 +09:00
|
|
|
}
|
|
|
|
|
if e.Err == nil {
|
|
|
|
|
return fmt.Errorf("OpError.Err is empty: %v", e)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseDialError parses nestedErr and reports whether it is a valid
|
|
|
|
|
// error value from Dial, Listen functions.
|
|
|
|
|
// It returns nil when nestedErr is valid.
|
|
|
|
|
func parseDialError(nestedErr error) error {
|
|
|
|
|
if nestedErr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *OpError:
|
|
|
|
|
if err := err.isValid(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto second
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
second:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
switch err := nestedErr.(type) {
|
2017-02-10 14:59:38 -08:00
|
|
|
case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *poll.TimeoutError, UnknownNetworkError:
|
2015-04-13 23:45:00 +09:00
|
|
|
return nil
|
|
|
|
|
case *os.SyscallError:
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
2016-03-02 20:08:18 +09:00
|
|
|
case *os.PathError: // for Plan 9
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
2015-04-13 23:45:00 +09:00
|
|
|
}
|
|
|
|
|
switch nestedErr {
|
2017-04-07 15:53:19 -07:00
|
|
|
case errCanceled, poll.ErrNetClosing, errMissingAddress, errNoSuitableAddress,
|
2016-11-11 14:05:51 -08:00
|
|
|
context.DeadlineExceeded, context.Canceled:
|
2015-04-13 23:45:00 +09:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
third:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dialErrorTests = []struct {
|
|
|
|
|
network, address string
|
|
|
|
|
}{
|
|
|
|
|
{"foo", ""},
|
|
|
|
|
{"bar", "baz"},
|
|
|
|
|
{"datakit", "mh/astro/r70"},
|
|
|
|
|
{"tcp", ""},
|
|
|
|
|
{"tcp", "127.0.0.1:☺"},
|
|
|
|
|
{"tcp", "no-such-name:80"},
|
|
|
|
|
{"tcp", "mh/astro/r70:http"},
|
|
|
|
|
|
2015-07-21 12:51:01 +09:00
|
|
|
{"tcp", JoinHostPort("127.0.0.1", "-1")},
|
|
|
|
|
{"tcp", JoinHostPort("127.0.0.1", "123456789")},
|
|
|
|
|
{"udp", JoinHostPort("127.0.0.1", "-1")},
|
|
|
|
|
{"udp", JoinHostPort("127.0.0.1", "123456789")},
|
2015-04-13 23:45:00 +09:00
|
|
|
{"ip:icmp", "127.0.0.1"},
|
|
|
|
|
|
|
|
|
|
{"unix", "/path/to/somewhere"},
|
|
|
|
|
{"unixgram", "/path/to/somewhere"},
|
|
|
|
|
{"unixpacket", "/path/to/somewhere"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestDialError(t *testing.T) {
|
|
|
|
|
switch runtime.GOOS {
|
|
|
|
|
case "plan9":
|
|
|
|
|
t.Skipf("%s does not have full support of socktest", runtime.GOOS)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
origTestHookLookupIP := testHookLookupIP
|
|
|
|
|
defer func() { testHookLookupIP = origTestHookLookupIP }()
|
2016-04-14 17:47:25 -07:00
|
|
|
testHookLookupIP = func(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
|
2015-04-13 23:45:00 +09:00
|
|
|
return nil, &DNSError{Err: "dial error test", Name: "name", Server: "server", IsTimeout: true}
|
|
|
|
|
}
|
|
|
|
|
sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
|
|
|
|
|
return nil, errOpNotSupported
|
|
|
|
|
})
|
|
|
|
|
defer sw.Set(socktest.FilterConnect, nil)
|
|
|
|
|
|
|
|
|
|
d := Dialer{Timeout: someTimeout}
|
|
|
|
|
for i, tt := range dialErrorTests {
|
|
|
|
|
c, err := d.Dial(tt.network, tt.address)
|
|
|
|
|
if err == nil {
|
2015-07-21 12:51:01 +09:00
|
|
|
t.Errorf("#%d: should fail; %s:%s->%s", i, c.LocalAddr().Network(), c.LocalAddr(), c.RemoteAddr())
|
2015-04-13 23:45:00 +09:00
|
|
|
c.Close()
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-07-21 12:51:01 +09:00
|
|
|
if tt.network == "tcp" || tt.network == "udp" {
|
|
|
|
|
nerr := err
|
|
|
|
|
if op, ok := nerr.(*OpError); ok {
|
|
|
|
|
nerr = op.Err
|
|
|
|
|
}
|
|
|
|
|
if sys, ok := nerr.(*os.SyscallError); ok {
|
|
|
|
|
nerr = sys.Err
|
|
|
|
|
}
|
|
|
|
|
if nerr == errOpNotSupported {
|
|
|
|
|
t.Errorf("#%d: should fail without %v; %s:%s->", i, nerr, tt.network, tt.address)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-04-17 14:55:07 +09:00
|
|
|
if c != nil {
|
|
|
|
|
t.Errorf("Dial returned non-nil interface %T(%v) with err != nil", c, c)
|
|
|
|
|
}
|
2015-04-13 23:45:00 +09:00
|
|
|
if err = parseDialError(err); err != nil {
|
|
|
|
|
t.Errorf("#%d: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-30 07:33:16 +09:00
|
|
|
func TestProtocolDialError(t *testing.T) {
|
|
|
|
|
switch runtime.GOOS {
|
2015-06-14 09:01:23 +09:00
|
|
|
case "nacl", "solaris":
|
2015-05-30 07:33:16 +09:00
|
|
|
t.Skipf("not supported on %s", runtime.GOOS)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, network := range []string{"tcp", "udp", "ip:4294967296", "unix", "unixpacket", "unixgram"} {
|
|
|
|
|
var err error
|
|
|
|
|
switch network {
|
|
|
|
|
case "tcp":
|
|
|
|
|
_, err = DialTCP(network, nil, &TCPAddr{Port: 1 << 16})
|
|
|
|
|
case "udp":
|
|
|
|
|
_, err = DialUDP(network, nil, &UDPAddr{Port: 1 << 16})
|
|
|
|
|
case "ip:4294967296":
|
|
|
|
|
_, err = DialIP(network, nil, nil)
|
|
|
|
|
case "unix", "unixpacket", "unixgram":
|
|
|
|
|
_, err = DialUnix(network, nil, &UnixAddr{Name: "//"})
|
|
|
|
|
}
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Errorf("%s: should fail", network)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if err = parseDialError(err); err != nil {
|
|
|
|
|
t.Errorf("%s: %v", network, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-07 17:19:29 +09:00
|
|
|
func TestDialAddrError(t *testing.T) {
|
|
|
|
|
switch runtime.GOOS {
|
|
|
|
|
case "nacl", "plan9":
|
|
|
|
|
t.Skipf("not supported on %s", runtime.GOOS)
|
|
|
|
|
}
|
2017-04-14 17:43:42 +09:00
|
|
|
if !supportsIPv4() || !supportsIPv6() {
|
2016-04-14 14:53:12 +09:00
|
|
|
t.Skip("both IPv4 and IPv6 are required")
|
|
|
|
|
}
|
2016-04-07 17:19:29 +09:00
|
|
|
|
|
|
|
|
for _, tt := range []struct {
|
|
|
|
|
network string
|
|
|
|
|
lit string
|
|
|
|
|
addr *TCPAddr
|
|
|
|
|
}{
|
|
|
|
|
{"tcp4", "::1", nil},
|
|
|
|
|
{"tcp4", "", &TCPAddr{IP: IPv6loopback}},
|
|
|
|
|
// We don't test the {"tcp6", "byte sequence", nil}
|
|
|
|
|
// case for now because there is no easy way to
|
|
|
|
|
// control name resolution.
|
|
|
|
|
{"tcp6", "", &TCPAddr{IP: IP{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}}},
|
|
|
|
|
} {
|
|
|
|
|
var err error
|
|
|
|
|
var c Conn
|
net: apply tcp4/tcp6 restrictions to literals in ResolveTCPAddr
The restrictions were already being applied to the IP addresses
received from the host resolver. Apply the same restrictions to
literal IP addresses not passed to the host resolver.
For example, ResolveTCPAddr("tcp4", "[2001:db8::1]:http") used
to succeed and now does not (that's not an IPv4 address).
Perhaps a bit surprisingly,
ResolveTCPAddr("tcp4", "[::ffff:127.0.0.1]:http") succeeds,
behaving identically to ResolveTCPAddr("tcp4", "127.0.0.1:http"), and
ResolveTCPAddr("tcp6", "[::ffff:127.0.0.1]:http") fails,
behaving identically to ResolveTCPAddr("tcp6", "127.0.0.1:http").
Even so, it seems right to match (by reusing) the existing filtering
as applied to addresses resolved by the host C library.
If anyone can make a strong argument for changing the filtering
of IPv4-inside-IPv6 addresses, the fix can be applied to all
the code paths in a separate CL.
Fixes #14037.
Change-Id: I690dfdcbe93d730e11e00ea387fa7484cd524341
Reviewed-on: https://go-review.googlesource.com/32100
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-10-26 20:44:26 -04:00
|
|
|
var op string
|
2016-04-07 17:19:29 +09:00
|
|
|
if tt.lit != "" {
|
|
|
|
|
c, err = Dial(tt.network, JoinHostPort(tt.lit, "0"))
|
net: apply tcp4/tcp6 restrictions to literals in ResolveTCPAddr
The restrictions were already being applied to the IP addresses
received from the host resolver. Apply the same restrictions to
literal IP addresses not passed to the host resolver.
For example, ResolveTCPAddr("tcp4", "[2001:db8::1]:http") used
to succeed and now does not (that's not an IPv4 address).
Perhaps a bit surprisingly,
ResolveTCPAddr("tcp4", "[::ffff:127.0.0.1]:http") succeeds,
behaving identically to ResolveTCPAddr("tcp4", "127.0.0.1:http"), and
ResolveTCPAddr("tcp6", "[::ffff:127.0.0.1]:http") fails,
behaving identically to ResolveTCPAddr("tcp6", "127.0.0.1:http").
Even so, it seems right to match (by reusing) the existing filtering
as applied to addresses resolved by the host C library.
If anyone can make a strong argument for changing the filtering
of IPv4-inside-IPv6 addresses, the fix can be applied to all
the code paths in a separate CL.
Fixes #14037.
Change-Id: I690dfdcbe93d730e11e00ea387fa7484cd524341
Reviewed-on: https://go-review.googlesource.com/32100
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-10-26 20:44:26 -04:00
|
|
|
op = fmt.Sprintf("Dial(%q, %q)", tt.network, JoinHostPort(tt.lit, "0"))
|
2016-04-07 17:19:29 +09:00
|
|
|
} else {
|
|
|
|
|
c, err = DialTCP(tt.network, nil, tt.addr)
|
net: apply tcp4/tcp6 restrictions to literals in ResolveTCPAddr
The restrictions were already being applied to the IP addresses
received from the host resolver. Apply the same restrictions to
literal IP addresses not passed to the host resolver.
For example, ResolveTCPAddr("tcp4", "[2001:db8::1]:http") used
to succeed and now does not (that's not an IPv4 address).
Perhaps a bit surprisingly,
ResolveTCPAddr("tcp4", "[::ffff:127.0.0.1]:http") succeeds,
behaving identically to ResolveTCPAddr("tcp4", "127.0.0.1:http"), and
ResolveTCPAddr("tcp6", "[::ffff:127.0.0.1]:http") fails,
behaving identically to ResolveTCPAddr("tcp6", "127.0.0.1:http").
Even so, it seems right to match (by reusing) the existing filtering
as applied to addresses resolved by the host C library.
If anyone can make a strong argument for changing the filtering
of IPv4-inside-IPv6 addresses, the fix can be applied to all
the code paths in a separate CL.
Fixes #14037.
Change-Id: I690dfdcbe93d730e11e00ea387fa7484cd524341
Reviewed-on: https://go-review.googlesource.com/32100
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-10-26 20:44:26 -04:00
|
|
|
op = fmt.Sprintf("DialTCP(%q, %q)", tt.network, tt.addr)
|
2016-04-07 17:19:29 +09:00
|
|
|
}
|
|
|
|
|
if err == nil {
|
|
|
|
|
c.Close()
|
net: apply tcp4/tcp6 restrictions to literals in ResolveTCPAddr
The restrictions were already being applied to the IP addresses
received from the host resolver. Apply the same restrictions to
literal IP addresses not passed to the host resolver.
For example, ResolveTCPAddr("tcp4", "[2001:db8::1]:http") used
to succeed and now does not (that's not an IPv4 address).
Perhaps a bit surprisingly,
ResolveTCPAddr("tcp4", "[::ffff:127.0.0.1]:http") succeeds,
behaving identically to ResolveTCPAddr("tcp4", "127.0.0.1:http"), and
ResolveTCPAddr("tcp6", "[::ffff:127.0.0.1]:http") fails,
behaving identically to ResolveTCPAddr("tcp6", "127.0.0.1:http").
Even so, it seems right to match (by reusing) the existing filtering
as applied to addresses resolved by the host C library.
If anyone can make a strong argument for changing the filtering
of IPv4-inside-IPv6 addresses, the fix can be applied to all
the code paths in a separate CL.
Fixes #14037.
Change-Id: I690dfdcbe93d730e11e00ea387fa7484cd524341
Reviewed-on: https://go-review.googlesource.com/32100
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-10-26 20:44:26 -04:00
|
|
|
t.Errorf("%s succeeded, want error", op)
|
2016-04-07 17:19:29 +09:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if perr := parseDialError(err); perr != nil {
|
net: apply tcp4/tcp6 restrictions to literals in ResolveTCPAddr
The restrictions were already being applied to the IP addresses
received from the host resolver. Apply the same restrictions to
literal IP addresses not passed to the host resolver.
For example, ResolveTCPAddr("tcp4", "[2001:db8::1]:http") used
to succeed and now does not (that's not an IPv4 address).
Perhaps a bit surprisingly,
ResolveTCPAddr("tcp4", "[::ffff:127.0.0.1]:http") succeeds,
behaving identically to ResolveTCPAddr("tcp4", "127.0.0.1:http"), and
ResolveTCPAddr("tcp6", "[::ffff:127.0.0.1]:http") fails,
behaving identically to ResolveTCPAddr("tcp6", "127.0.0.1:http").
Even so, it seems right to match (by reusing) the existing filtering
as applied to addresses resolved by the host C library.
If anyone can make a strong argument for changing the filtering
of IPv4-inside-IPv6 addresses, the fix can be applied to all
the code paths in a separate CL.
Fixes #14037.
Change-Id: I690dfdcbe93d730e11e00ea387fa7484cd524341
Reviewed-on: https://go-review.googlesource.com/32100
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-10-26 20:44:26 -04:00
|
|
|
t.Errorf("%s: %v", op, perr)
|
2016-04-07 17:19:29 +09:00
|
|
|
continue
|
|
|
|
|
}
|
net: apply tcp4/tcp6 restrictions to literals in ResolveTCPAddr
The restrictions were already being applied to the IP addresses
received from the host resolver. Apply the same restrictions to
literal IP addresses not passed to the host resolver.
For example, ResolveTCPAddr("tcp4", "[2001:db8::1]:http") used
to succeed and now does not (that's not an IPv4 address).
Perhaps a bit surprisingly,
ResolveTCPAddr("tcp4", "[::ffff:127.0.0.1]:http") succeeds,
behaving identically to ResolveTCPAddr("tcp4", "127.0.0.1:http"), and
ResolveTCPAddr("tcp6", "[::ffff:127.0.0.1]:http") fails,
behaving identically to ResolveTCPAddr("tcp6", "127.0.0.1:http").
Even so, it seems right to match (by reusing) the existing filtering
as applied to addresses resolved by the host C library.
If anyone can make a strong argument for changing the filtering
of IPv4-inside-IPv6 addresses, the fix can be applied to all
the code paths in a separate CL.
Fixes #14037.
Change-Id: I690dfdcbe93d730e11e00ea387fa7484cd524341
Reviewed-on: https://go-review.googlesource.com/32100
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-10-26 20:44:26 -04:00
|
|
|
operr := err.(*OpError).Err
|
|
|
|
|
aerr, ok := operr.(*AddrError)
|
2016-04-07 17:19:29 +09:00
|
|
|
if !ok {
|
2016-11-13 13:10:42 -08:00
|
|
|
t.Errorf("%s: %v is %T, want *AddrError", op, err, operr)
|
2016-04-07 17:19:29 +09:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
want := tt.lit
|
|
|
|
|
if tt.lit == "" {
|
|
|
|
|
want = tt.addr.IP.String()
|
|
|
|
|
}
|
|
|
|
|
if aerr.Addr != want {
|
net: apply tcp4/tcp6 restrictions to literals in ResolveTCPAddr
The restrictions were already being applied to the IP addresses
received from the host resolver. Apply the same restrictions to
literal IP addresses not passed to the host resolver.
For example, ResolveTCPAddr("tcp4", "[2001:db8::1]:http") used
to succeed and now does not (that's not an IPv4 address).
Perhaps a bit surprisingly,
ResolveTCPAddr("tcp4", "[::ffff:127.0.0.1]:http") succeeds,
behaving identically to ResolveTCPAddr("tcp4", "127.0.0.1:http"), and
ResolveTCPAddr("tcp6", "[::ffff:127.0.0.1]:http") fails,
behaving identically to ResolveTCPAddr("tcp6", "127.0.0.1:http").
Even so, it seems right to match (by reusing) the existing filtering
as applied to addresses resolved by the host C library.
If anyone can make a strong argument for changing the filtering
of IPv4-inside-IPv6 addresses, the fix can be applied to all
the code paths in a separate CL.
Fixes #14037.
Change-Id: I690dfdcbe93d730e11e00ea387fa7484cd524341
Reviewed-on: https://go-review.googlesource.com/32100
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-10-26 20:44:26 -04:00
|
|
|
t.Errorf("%s: %v, error Addr=%q, want %q", op, err, aerr.Addr, want)
|
2016-04-07 17:19:29 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-13 23:45:00 +09:00
|
|
|
var listenErrorTests = []struct {
|
|
|
|
|
network, address string
|
|
|
|
|
}{
|
|
|
|
|
{"foo", ""},
|
|
|
|
|
{"bar", "baz"},
|
|
|
|
|
{"datakit", "mh/astro/r70"},
|
|
|
|
|
{"tcp", "127.0.0.1:☺"},
|
|
|
|
|
{"tcp", "no-such-name:80"},
|
|
|
|
|
{"tcp", "mh/astro/r70:http"},
|
2015-04-17 14:55:07 +09:00
|
|
|
|
2015-07-21 12:51:01 +09:00
|
|
|
{"tcp", JoinHostPort("127.0.0.1", "-1")},
|
|
|
|
|
{"tcp", JoinHostPort("127.0.0.1", "123456789")},
|
2015-04-17 14:55:07 +09:00
|
|
|
|
|
|
|
|
{"unix", "/path/to/somewhere"},
|
|
|
|
|
{"unixpacket", "/path/to/somewhere"},
|
2015-04-13 23:45:00 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestListenError(t *testing.T) {
|
|
|
|
|
switch runtime.GOOS {
|
|
|
|
|
case "plan9":
|
|
|
|
|
t.Skipf("%s does not have full support of socktest", runtime.GOOS)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
origTestHookLookupIP := testHookLookupIP
|
|
|
|
|
defer func() { testHookLookupIP = origTestHookLookupIP }()
|
2016-04-14 17:47:25 -07:00
|
|
|
testHookLookupIP = func(_ context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
|
2015-04-13 23:45:00 +09:00
|
|
|
return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
|
|
|
|
|
}
|
|
|
|
|
sw.Set(socktest.FilterListen, func(so *socktest.Status) (socktest.AfterFilter, error) {
|
|
|
|
|
return nil, errOpNotSupported
|
|
|
|
|
})
|
|
|
|
|
defer sw.Set(socktest.FilterListen, nil)
|
|
|
|
|
|
|
|
|
|
for i, tt := range listenErrorTests {
|
|
|
|
|
ln, err := Listen(tt.network, tt.address)
|
|
|
|
|
if err == nil {
|
2015-07-21 12:51:01 +09:00
|
|
|
t.Errorf("#%d: should fail; %s:%s->", i, ln.Addr().Network(), ln.Addr())
|
2015-04-13 23:45:00 +09:00
|
|
|
ln.Close()
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-07-21 12:51:01 +09:00
|
|
|
if tt.network == "tcp" {
|
|
|
|
|
nerr := err
|
|
|
|
|
if op, ok := nerr.(*OpError); ok {
|
|
|
|
|
nerr = op.Err
|
|
|
|
|
}
|
|
|
|
|
if sys, ok := nerr.(*os.SyscallError); ok {
|
|
|
|
|
nerr = sys.Err
|
|
|
|
|
}
|
|
|
|
|
if nerr == errOpNotSupported {
|
|
|
|
|
t.Errorf("#%d: should fail without %v; %s:%s->", i, nerr, tt.network, tt.address)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-04-17 14:55:07 +09:00
|
|
|
if ln != nil {
|
|
|
|
|
t.Errorf("Listen returned non-nil interface %T(%v) with err != nil", ln, ln)
|
|
|
|
|
}
|
|
|
|
|
if err = parseDialError(err); err != nil {
|
|
|
|
|
t.Errorf("#%d: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var listenPacketErrorTests = []struct {
|
|
|
|
|
network, address string
|
|
|
|
|
}{
|
|
|
|
|
{"foo", ""},
|
|
|
|
|
{"bar", "baz"},
|
|
|
|
|
{"datakit", "mh/astro/r70"},
|
|
|
|
|
{"udp", "127.0.0.1:☺"},
|
|
|
|
|
{"udp", "no-such-name:80"},
|
|
|
|
|
{"udp", "mh/astro/r70:http"},
|
2015-07-21 12:51:01 +09:00
|
|
|
|
|
|
|
|
{"udp", JoinHostPort("127.0.0.1", "-1")},
|
|
|
|
|
{"udp", JoinHostPort("127.0.0.1", "123456789")},
|
2015-04-17 14:55:07 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestListenPacketError(t *testing.T) {
|
|
|
|
|
switch runtime.GOOS {
|
|
|
|
|
case "plan9":
|
|
|
|
|
t.Skipf("%s does not have full support of socktest", runtime.GOOS)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
origTestHookLookupIP := testHookLookupIP
|
|
|
|
|
defer func() { testHookLookupIP = origTestHookLookupIP }()
|
2016-04-14 17:47:25 -07:00
|
|
|
testHookLookupIP = func(_ context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
|
2015-04-17 14:55:07 +09:00
|
|
|
return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, tt := range listenPacketErrorTests {
|
|
|
|
|
c, err := ListenPacket(tt.network, tt.address)
|
|
|
|
|
if err == nil {
|
2015-07-21 12:51:01 +09:00
|
|
|
t.Errorf("#%d: should fail; %s:%s->", i, c.LocalAddr().Network(), c.LocalAddr())
|
2015-04-17 14:55:07 +09:00
|
|
|
c.Close()
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if c != nil {
|
|
|
|
|
t.Errorf("ListenPacket returned non-nil interface %T(%v) with err != nil", c, c)
|
|
|
|
|
}
|
2015-04-13 23:45:00 +09:00
|
|
|
if err = parseDialError(err); err != nil {
|
|
|
|
|
t.Errorf("#%d: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-04-16 23:10:56 +09:00
|
|
|
|
2015-05-30 07:33:16 +09:00
|
|
|
func TestProtocolListenError(t *testing.T) {
|
|
|
|
|
switch runtime.GOOS {
|
|
|
|
|
case "nacl", "plan9":
|
|
|
|
|
t.Skipf("not supported on %s", runtime.GOOS)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, network := range []string{"tcp", "udp", "ip:4294967296", "unix", "unixpacket", "unixgram"} {
|
|
|
|
|
var err error
|
|
|
|
|
switch network {
|
|
|
|
|
case "tcp":
|
|
|
|
|
_, err = ListenTCP(network, &TCPAddr{Port: 1 << 16})
|
|
|
|
|
case "udp":
|
|
|
|
|
_, err = ListenUDP(network, &UDPAddr{Port: 1 << 16})
|
|
|
|
|
case "ip:4294967296":
|
|
|
|
|
_, err = ListenIP(network, nil)
|
|
|
|
|
case "unix", "unixpacket":
|
|
|
|
|
_, err = ListenUnix(network, &UnixAddr{Name: "//"})
|
|
|
|
|
case "unixgram":
|
|
|
|
|
_, err = ListenUnixgram(network, &UnixAddr{Name: "//"})
|
|
|
|
|
}
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Errorf("%s: should fail", network)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if err = parseDialError(err); err != nil {
|
|
|
|
|
t.Errorf("%s: %v", network, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 23:10:56 +09:00
|
|
|
// parseReadError parses nestedErr and reports whether it is a valid
|
|
|
|
|
// error value from Read functions.
|
|
|
|
|
// It returns nil when nestedErr is valid.
|
|
|
|
|
func parseReadError(nestedErr error) error {
|
|
|
|
|
if nestedErr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *OpError:
|
|
|
|
|
if err := err.isValid(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto second
|
|
|
|
|
}
|
|
|
|
|
if nestedErr == io.EOF {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
second:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *os.SyscallError:
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
|
|
|
|
}
|
|
|
|
|
switch nestedErr {
|
2017-04-07 15:53:19 -07:00
|
|
|
case poll.ErrNetClosing, poll.ErrTimeout:
|
2015-04-16 23:10:56 +09:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
third:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
|
|
|
|
|
}
|
2015-04-16 11:26:44 +09:00
|
|
|
|
|
|
|
|
// parseWriteError parses nestedErr and reports whether it is a valid
|
|
|
|
|
// error value from Write functions.
|
|
|
|
|
// It returns nil when nestedErr is valid.
|
|
|
|
|
func parseWriteError(nestedErr error) error {
|
|
|
|
|
if nestedErr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *OpError:
|
|
|
|
|
if err := err.isValid(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto second
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
second:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
switch err := nestedErr.(type) {
|
2017-02-10 14:59:38 -08:00
|
|
|
case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *poll.TimeoutError, UnknownNetworkError:
|
2015-04-19 23:42:11 +09:00
|
|
|
return nil
|
2015-04-16 11:26:44 +09:00
|
|
|
case *os.SyscallError:
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
|
|
|
|
}
|
|
|
|
|
switch nestedErr {
|
2017-04-07 15:53:19 -07:00
|
|
|
case errCanceled, poll.ErrNetClosing, errMissingAddress, poll.ErrTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF:
|
2015-04-16 11:26:44 +09:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
third:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
|
|
|
|
|
}
|
2015-04-17 12:24:42 +09:00
|
|
|
|
|
|
|
|
// parseCloseError parses nestedErr and reports whether it is a valid
|
|
|
|
|
// error value from Close functions.
|
|
|
|
|
// It returns nil when nestedErr is valid.
|
2017-04-07 15:53:19 -07:00
|
|
|
func parseCloseError(nestedErr error, isShutdown bool) error {
|
2015-04-17 12:24:42 +09:00
|
|
|
if nestedErr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-07 15:53:19 -07:00
|
|
|
// Because historically we have not exported the error that we
|
|
|
|
|
// return for an operation on a closed network connection,
|
|
|
|
|
// there are programs that test for the exact error string.
|
|
|
|
|
// Verify that string here so that we don't break those
|
|
|
|
|
// programs unexpectedly. See issues #4373 and #19252.
|
|
|
|
|
want := "use of closed network connection"
|
|
|
|
|
if !isShutdown && !strings.Contains(nestedErr.Error(), want) {
|
|
|
|
|
return fmt.Errorf("error string %q does not contain expected string %q", nestedErr, want)
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-17 12:24:42 +09:00
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *OpError:
|
|
|
|
|
if err := err.isValid(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto second
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
second:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *os.SyscallError:
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
|
|
|
|
case *os.PathError: // for Plan 9
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
|
|
|
|
}
|
|
|
|
|
switch nestedErr {
|
2017-04-07 15:53:19 -07:00
|
|
|
case poll.ErrNetClosing:
|
2015-04-17 12:24:42 +09:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
third:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2016-10-25 00:12:57 +02:00
|
|
|
switch nestedErr {
|
|
|
|
|
case os.ErrClosed: // for Plan 9
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2015-04-17 12:24:42 +09:00
|
|
|
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCloseError(t *testing.T) {
|
|
|
|
|
ln, err := newLocalListener("tcp")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer ln.Close()
|
|
|
|
|
c, err := Dial(ln.Addr().Network(), ln.Addr().String())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer c.Close()
|
|
|
|
|
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
err = c.(*TCPConn).CloseRead()
|
2017-04-07 15:53:19 -07:00
|
|
|
if perr := parseCloseError(err, true); perr != nil {
|
2015-04-17 12:24:42 +09:00
|
|
|
t.Errorf("#%d: %v", i, perr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
err = c.(*TCPConn).CloseWrite()
|
2017-04-07 15:53:19 -07:00
|
|
|
if perr := parseCloseError(err, true); perr != nil {
|
2015-04-17 12:24:42 +09:00
|
|
|
t.Errorf("#%d: %v", i, perr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
err = c.Close()
|
2017-04-07 15:53:19 -07:00
|
|
|
if perr := parseCloseError(err, false); perr != nil {
|
2015-04-17 12:24:42 +09:00
|
|
|
t.Errorf("#%d: %v", i, perr)
|
|
|
|
|
}
|
|
|
|
|
err = ln.Close()
|
2017-04-07 15:53:19 -07:00
|
|
|
if perr := parseCloseError(err, false); perr != nil {
|
2015-04-17 12:24:42 +09:00
|
|
|
t.Errorf("#%d: %v", i, perr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pc, err := ListenPacket("udp", "127.0.0.1:0")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer pc.Close()
|
|
|
|
|
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
err = pc.Close()
|
2017-04-07 15:53:19 -07:00
|
|
|
if perr := parseCloseError(err, false); perr != nil {
|
2015-04-17 12:24:42 +09:00
|
|
|
t.Errorf("#%d: %v", i, perr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-04-17 14:35:54 +09:00
|
|
|
|
|
|
|
|
// parseAcceptError parses nestedErr and reports whether it is a valid
|
|
|
|
|
// error value from Accept functions.
|
|
|
|
|
// It returns nil when nestedErr is valid.
|
|
|
|
|
func parseAcceptError(nestedErr error) error {
|
|
|
|
|
if nestedErr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *OpError:
|
|
|
|
|
if err := err.isValid(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto second
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
second:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *os.SyscallError:
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
2016-03-02 20:08:18 +09:00
|
|
|
case *os.PathError: // for Plan 9
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
2015-04-17 14:35:54 +09:00
|
|
|
}
|
|
|
|
|
switch nestedErr {
|
2017-04-07 15:53:19 -07:00
|
|
|
case poll.ErrNetClosing, poll.ErrTimeout:
|
2015-04-17 14:35:54 +09:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
third:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAcceptError(t *testing.T) {
|
|
|
|
|
handler := func(ls *localServer, ln Listener) {
|
|
|
|
|
for {
|
|
|
|
|
ln.(*TCPListener).SetDeadline(time.Now().Add(5 * time.Millisecond))
|
|
|
|
|
c, err := ln.Accept()
|
|
|
|
|
if perr := parseAcceptError(err); perr != nil {
|
|
|
|
|
t.Error(perr)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
if c != nil {
|
|
|
|
|
t.Errorf("Accept returned non-nil interface %T(%v) with err != nil", c, c)
|
|
|
|
|
}
|
2015-04-28 21:17:46 +09:00
|
|
|
if nerr, ok := err.(Error); !ok || (!nerr.Timeout() && !nerr.Temporary()) {
|
2015-04-17 14:35:54 +09:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
c.Close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ls, err := newLocalServer("tcp")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if err := ls.buildup(handler); err != nil {
|
|
|
|
|
ls.teardown()
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
|
ls.teardown()
|
|
|
|
|
}
|
2015-04-18 16:53:55 +09:00
|
|
|
|
|
|
|
|
// parseCommonError parses nestedErr and reports whether it is a valid
|
|
|
|
|
// error value from miscellaneous functions.
|
|
|
|
|
// It returns nil when nestedErr is valid.
|
|
|
|
|
func parseCommonError(nestedErr error) error {
|
|
|
|
|
if nestedErr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *OpError:
|
|
|
|
|
if err := err.isValid(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto second
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
second:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
switch err := nestedErr.(type) {
|
|
|
|
|
case *os.SyscallError:
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
|
|
|
|
case *os.LinkError:
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
|
|
|
|
case *os.PathError:
|
|
|
|
|
nestedErr = err.Err
|
|
|
|
|
goto third
|
|
|
|
|
}
|
2015-04-28 21:17:46 +09:00
|
|
|
switch nestedErr {
|
2017-04-07 15:53:19 -07:00
|
|
|
case poll.ErrNetClosing:
|
2015-04-28 21:17:46 +09:00
|
|
|
return nil
|
|
|
|
|
}
|
2015-04-18 16:53:55 +09:00
|
|
|
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
|
|
|
|
|
|
|
|
|
third:
|
|
|
|
|
if isPlatformError(nestedErr) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestFileError(t *testing.T) {
|
|
|
|
|
switch runtime.GOOS {
|
|
|
|
|
case "windows":
|
2015-05-14 12:26:27 +09:00
|
|
|
t.Skipf("not supported on %s", runtime.GOOS)
|
2015-04-18 16:53:55 +09:00
|
|
|
}
|
|
|
|
|
|
2015-05-06 10:41:01 +09:00
|
|
|
f, err := ioutil.TempFile("", "go-nettest")
|
2015-04-18 16:53:55 +09:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2015-05-06 10:41:01 +09:00
|
|
|
defer os.Remove(f.Name())
|
2015-04-18 16:53:55 +09:00
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
|
|
c, err := FileConn(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if c != nil {
|
|
|
|
|
t.Errorf("FileConn returned non-nil interface %T(%v) with err != nil", c, c)
|
|
|
|
|
}
|
|
|
|
|
if perr := parseCommonError(err); perr != nil {
|
|
|
|
|
t.Error(perr)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
c.Close()
|
|
|
|
|
t.Error("should fail")
|
|
|
|
|
}
|
|
|
|
|
ln, err := FileListener(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if ln != nil {
|
|
|
|
|
t.Errorf("FileListener returned non-nil interface %T(%v) with err != nil", ln, ln)
|
|
|
|
|
}
|
|
|
|
|
if perr := parseCommonError(err); perr != nil {
|
|
|
|
|
t.Error(perr)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ln.Close()
|
|
|
|
|
t.Error("should fail")
|
|
|
|
|
}
|
|
|
|
|
pc, err := FilePacketConn(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if pc != nil {
|
|
|
|
|
t.Errorf("FilePacketConn returned non-nil interface %T(%v) with err != nil", pc, pc)
|
|
|
|
|
}
|
|
|
|
|
if perr := parseCommonError(err); perr != nil {
|
|
|
|
|
t.Error(perr)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
pc.Close()
|
|
|
|
|
t.Error("should fail")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ln, err = newLocalListener("tcp")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
f, err := ln.(*TCPListener).File()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if perr := parseCommonError(err); perr != nil {
|
|
|
|
|
t.Error(perr)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
f.Close()
|
|
|
|
|
}
|
|
|
|
|
ln.Close()
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-05-13 05:02:00 +09:00
|
|
|
|
|
|
|
|
func parseLookupPortError(nestedErr error) error {
|
|
|
|
|
if nestedErr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch nestedErr.(type) {
|
|
|
|
|
case *AddrError, *DNSError:
|
|
|
|
|
return nil
|
|
|
|
|
case *os.PathError: // for Plan 9
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
|
|
|
|
|
}
|