mirror of
https://github.com/golang/go
synced 2024-11-19 23:44:43 -07:00
net: make multi-IP resolution more flexible.
Remove the "netaddr" type, which ambiguously represented either one address, or a list of addresses. Instead, use "addrList" wherever multiple addresses are supported. The "first" method returns the first address matching some condition (e.g. "is it IPv4?"), primarily to support legacy code that can't handle multiple addresses. The "partition" method splits an addrList into two categories, as defined by some strategy function. This is useful for implementing Happy Eyeballs, and similar two-channel algorithms. Finally, internetAddrList (formerly resolveInternetAddr) no longer mangles the ordering defined by getaddrinfo. In the future, this may be used by a sequential Dial implementation. Updates #8453, #8455. Change-Id: I7375f4c34481580ab40e31d33002a4073a0474f3 Reviewed-on: https://go-review.googlesource.com/8360 Reviewed-by: Mikio Hara <mikioh.mikioh@gmail.com> Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
4b21be4670
commit
a5dec3859a
@ -95,7 +95,7 @@ func parseNetwork(net string) (afnet string, proto int, err error) {
|
|||||||
return "", 0, UnknownNetworkError(net)
|
return "", 0, UnknownNetworkError(net)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
|
func resolveAddrList(op, net, addr string, deadline time.Time) (addrList, error) {
|
||||||
afnet, _, err := parseNetwork(net)
|
afnet, _, err := parseNetwork(net)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -105,9 +105,13 @@ func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
|
|||||||
}
|
}
|
||||||
switch afnet {
|
switch afnet {
|
||||||
case "unix", "unixgram", "unixpacket":
|
case "unix", "unixgram", "unixpacket":
|
||||||
return ResolveUnixAddr(afnet, addr)
|
addr, err := ResolveUnixAddr(afnet, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return addrList{addr}, nil
|
||||||
}
|
}
|
||||||
return resolveInternetAddr(afnet, addr, deadline)
|
return internetAddrList(afnet, addr, deadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial connects to the address on the named network.
|
// Dial connects to the address on the named network.
|
||||||
@ -155,21 +159,25 @@ func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
|
|||||||
// See func Dial for a description of the network and address
|
// See func Dial for a description of the network and address
|
||||||
// parameters.
|
// parameters.
|
||||||
func (d *Dialer) Dial(network, address string) (Conn, error) {
|
func (d *Dialer) Dial(network, address string) (Conn, error) {
|
||||||
ra, err := resolveAddr("dial", network, address, d.deadline())
|
addrs, err := resolveAddrList("dial", network, address, d.deadline())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &OpError{Op: "dial", Net: network, Addr: nil, Err: err}
|
return nil, &OpError{Op: "dial", Net: network, Addr: nil, Err: err}
|
||||||
}
|
}
|
||||||
var dialer func(deadline time.Time) (Conn, error)
|
var dialer func(deadline time.Time) (Conn, error)
|
||||||
if ras, ok := ra.(addrList); ok && d.DualStack && network == "tcp" {
|
if d.DualStack && network == "tcp" {
|
||||||
dialer = func(deadline time.Time) (Conn, error) {
|
primaries, fallbacks := addrs.partition(isIPv4)
|
||||||
return dialMulti(network, address, d.LocalAddr, ras, deadline)
|
if len(fallbacks) > 0 {
|
||||||
}
|
dialer = func(deadline time.Time) (Conn, error) {
|
||||||
} else {
|
return dialMulti(network, address, d.LocalAddr, addrList{primaries[0], fallbacks[0]}, deadline)
|
||||||
dialer = func(deadline time.Time) (Conn, error) {
|
}
|
||||||
return dialSingle(network, address, d.LocalAddr, ra.toAddr(), deadline)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c, err := dial(network, ra.toAddr(), dialer, d.deadline())
|
if dialer == nil {
|
||||||
|
dialer = func(deadline time.Time) (Conn, error) {
|
||||||
|
return dialSingle(network, address, d.LocalAddr, addrs.first(isIPv4), deadline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c, err := dial(network, addrs.first(isIPv4), dialer, d.deadline())
|
||||||
if d.KeepAlive > 0 && err == nil {
|
if d.KeepAlive > 0 && err == nil {
|
||||||
if tc, ok := c.(*TCPConn); ok {
|
if tc, ok := c.(*TCPConn); ok {
|
||||||
tc.SetKeepAlive(true)
|
tc.SetKeepAlive(true)
|
||||||
@ -206,7 +214,7 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
|
|||||||
// unnecessary resource starvation.
|
// unnecessary resource starvation.
|
||||||
c.Close()
|
c.Close()
|
||||||
}
|
}
|
||||||
}(ra.toAddr())
|
}(ra)
|
||||||
}
|
}
|
||||||
defer close(sig)
|
defer close(sig)
|
||||||
lastErr := errTimeout
|
lastErr := errTimeout
|
||||||
@ -256,12 +264,12 @@ func dialSingle(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err
|
|||||||
// "tcp6", "unix" or "unixpacket".
|
// "tcp6", "unix" or "unixpacket".
|
||||||
// See Dial for the syntax of laddr.
|
// See Dial for the syntax of laddr.
|
||||||
func Listen(net, laddr string) (Listener, error) {
|
func Listen(net, laddr string) (Listener, error) {
|
||||||
la, err := resolveAddr("listen", net, laddr, noDeadline)
|
addrs, err := resolveAddrList("listen", net, laddr, noDeadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
|
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
|
||||||
}
|
}
|
||||||
var l Listener
|
var l Listener
|
||||||
switch la := la.toAddr().(type) {
|
switch la := addrs.first(isIPv4).(type) {
|
||||||
case *TCPAddr:
|
case *TCPAddr:
|
||||||
l, err = ListenTCP(net, la)
|
l, err = ListenTCP(net, la)
|
||||||
case *UnixAddr:
|
case *UnixAddr:
|
||||||
@ -280,12 +288,12 @@ func Listen(net, laddr string) (Listener, error) {
|
|||||||
// "udp6", "ip", "ip4", "ip6" or "unixgram".
|
// "udp6", "ip", "ip4", "ip6" or "unixgram".
|
||||||
// See Dial for the syntax of laddr.
|
// See Dial for the syntax of laddr.
|
||||||
func ListenPacket(net, laddr string) (PacketConn, error) {
|
func ListenPacket(net, laddr string) (PacketConn, error) {
|
||||||
la, err := resolveAddr("listen", net, laddr, noDeadline)
|
addrs, err := resolveAddrList("listen", net, laddr, noDeadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
|
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
|
||||||
}
|
}
|
||||||
var l PacketConn
|
var l PacketConn
|
||||||
switch la := la.toAddr().(type) {
|
switch la := addrs.first(isIPv4).(type) {
|
||||||
case *UDPAddr:
|
case *UDPAddr:
|
||||||
l, err = ListenUDP(net, la)
|
l, err = ListenUDP(net, la)
|
||||||
case *IPAddr:
|
case *IPAddr:
|
||||||
|
@ -238,8 +238,8 @@ func parseProcNetIGMP(path string, ifi *Interface) []Addr {
|
|||||||
b[i/2], _ = xtoi2(f[0][i:i+2], 0)
|
b[i/2], _ = xtoi2(f[0][i:i+2], 0)
|
||||||
}
|
}
|
||||||
i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
|
i := *(*uint32)(unsafe.Pointer(&b[:4][0]))
|
||||||
ifma := IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
|
ifma := &IPAddr{IP: IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))}
|
||||||
ifmat = append(ifmat, ifma.toAddr())
|
ifmat = append(ifmat, ifma)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,8 +263,8 @@ func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
|
|||||||
for i := 0; i+1 < len(f[2]); i += 2 {
|
for i := 0; i+1 < len(f[2]); i += 2 {
|
||||||
b[i/2], _ = xtoi2(f[2][i:i+2], 0)
|
b[i/2], _ = xtoi2(f[2][i:i+2], 0)
|
||||||
}
|
}
|
||||||
ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}}
|
ifma := &IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}}
|
||||||
ifmat = append(ifmat, ifma.toAddr())
|
ifmat = append(ifmat, ifma)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ifmat
|
return ifmat
|
||||||
|
@ -217,11 +217,11 @@ func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
|
|||||||
case *syscall.SockaddrInet4:
|
case *syscall.SockaddrInet4:
|
||||||
ifa := &IPAddr{IP: make(IP, IPv4len)}
|
ifa := &IPAddr{IP: make(IP, IPv4len)}
|
||||||
copy(ifa.IP, sav.Addr[:])
|
copy(ifa.IP, sav.Addr[:])
|
||||||
ifat = append(ifat, ifa.toAddr())
|
ifat = append(ifat, ifa)
|
||||||
case *syscall.SockaddrInet6:
|
case *syscall.SockaddrInet6:
|
||||||
ifa := &IPAddr{IP: make(IP, IPv6len)}
|
ifa := &IPAddr{IP: make(IP, IPv6len)}
|
||||||
copy(ifa.IP, sav.Addr[:])
|
copy(ifa.IP, sav.Addr[:])
|
||||||
ifat = append(ifat, ifa.toAddr())
|
ifat = append(ifat, ifa)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,6 @@ func (a *IPAddr) isWildcard() bool {
|
|||||||
return a.IP.IsUnspecified()
|
return a.IP.IsUnspecified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *IPAddr) toAddr() Addr {
|
|
||||||
if a == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolveIPAddr parses addr as an IP address of the form "host" or
|
// ResolveIPAddr parses addr as an IP address of the form "host" or
|
||||||
// "ipv6-host%zone" and resolves the domain name on the network net,
|
// "ipv6-host%zone" and resolves the domain name on the network net,
|
||||||
// which must be "ip", "ip4" or "ip6".
|
// which must be "ip", "ip4" or "ip6".
|
||||||
@ -53,9 +46,9 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, UnknownNetworkError(net)
|
return nil, UnknownNetworkError(net)
|
||||||
}
|
}
|
||||||
a, err := resolveInternetAddr(afnet, addr, noDeadline)
|
addrs, err := internetAddrList(afnet, addr, noDeadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return a.toAddr().(*IPAddr), nil
|
return addrs.first(isIPv4).(*IPAddr), nil
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,10 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
|
|||||||
return 0, nil, syscall.EINVAL
|
return 0, nil, syscall.EINVAL
|
||||||
}
|
}
|
||||||
n, addr, err := c.ReadFromIP(b)
|
n, addr, err := c.ReadFromIP(b)
|
||||||
return n, addr.toAddr(), err
|
if addr == nil {
|
||||||
|
return n, nil, err
|
||||||
|
}
|
||||||
|
return n, addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadMsgIP reads a packet from c, copying the payload into b and the
|
// ReadMsgIP reads a packet from c, copying the payload into b and the
|
||||||
|
@ -26,87 +26,70 @@ var (
|
|||||||
supportsIPv4map bool
|
supportsIPv4map bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// A netaddr represents a network endpoint address or a list of
|
// An addrList represents a list of network endpoint addresses.
|
||||||
// network endpoint addresses.
|
type addrList []Addr
|
||||||
type netaddr interface {
|
|
||||||
// toAddr returns the address represented in Addr interface.
|
// isIPv4 returns true if the Addr contains an IPv4 address.
|
||||||
// It returns a nil interface when the address is nil.
|
func isIPv4(addr Addr) bool {
|
||||||
toAddr() Addr
|
switch addr := addr.(type) {
|
||||||
|
case *TCPAddr:
|
||||||
|
return addr.IP.To4() != nil
|
||||||
|
case *UDPAddr:
|
||||||
|
return addr.IP.To4() != nil
|
||||||
|
case *IPAddr:
|
||||||
|
return addr.IP.To4() != nil
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// An addrList represents a list of network endpoint addresses.
|
// first returns the first address which satisfies strategy, or if
|
||||||
type addrList []netaddr
|
// none do, then the first address of any kind.
|
||||||
|
func (addrs addrList) first(strategy func(Addr) bool) Addr {
|
||||||
func (al addrList) toAddr() Addr {
|
for _, addr := range addrs {
|
||||||
switch len(al) {
|
if strategy(addr) {
|
||||||
case 0:
|
return addr
|
||||||
return nil
|
}
|
||||||
case 1:
|
|
||||||
return al[0].toAddr()
|
|
||||||
default:
|
|
||||||
// For now, we'll roughly pick first one without
|
|
||||||
// considering dealing with any preferences such as
|
|
||||||
// DNS TTL, transport path quality, network routing
|
|
||||||
// information.
|
|
||||||
return al[0].toAddr()
|
|
||||||
}
|
}
|
||||||
|
return addrs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// partition divides an address list into two categories, using a
|
||||||
|
// strategy function to assign a boolean label to each address.
|
||||||
|
// The first address, and any with a matching label, are returned as
|
||||||
|
// primaries, while addresses with the opposite label are returned
|
||||||
|
// as fallbacks. For non-empty inputs, primaries is guaranteed to be
|
||||||
|
// non-empty.
|
||||||
|
func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks addrList) {
|
||||||
|
var primaryLabel bool
|
||||||
|
for i, addr := range addrs {
|
||||||
|
label := strategy(addr)
|
||||||
|
if i == 0 || label == primaryLabel {
|
||||||
|
primaryLabel = label
|
||||||
|
primaries = append(primaries, addr)
|
||||||
|
} else {
|
||||||
|
fallbacks = append(fallbacks, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var errNoSuitableAddress = errors.New("no suitable address found")
|
var errNoSuitableAddress = errors.New("no suitable address found")
|
||||||
|
|
||||||
// firstFavoriteAddr returns an address or a list of addresses that
|
// filterAddrList applies a filter to a list of IP addresses,
|
||||||
// implement the netaddr interface. Known filters are nil, ipv4only
|
// yielding a list of Addr objects. Known filters are nil, ipv4only,
|
||||||
// and ipv6only. It returns any address when filter is nil. The result
|
// and ipv6only. It returns every address when the filter is nil.
|
||||||
// contains at least one address when error is nil.
|
// The result contains at least one address when error is nil.
|
||||||
func firstFavoriteAddr(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) netaddr) (netaddr, error) {
|
func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr) (addrList, error) {
|
||||||
if filter != nil {
|
var addrs addrList
|
||||||
return firstSupportedAddr(filter, ips, inetaddr)
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
ipv4, ipv6, swap bool
|
|
||||||
list addrList
|
|
||||||
)
|
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
// We'll take any IP address, but since the dialing
|
if filter == nil || filter(ip) {
|
||||||
// code does not yet try multiple addresses
|
addrs = append(addrs, inetaddr(ip))
|
||||||
// effectively, prefer to use an IPv4 address if
|
|
||||||
// possible. This is especially relevant if localhost
|
|
||||||
// resolves to [ipv6-localhost, ipv4-localhost]. Too
|
|
||||||
// much code assumes localhost == ipv4-localhost.
|
|
||||||
if ipv4only(ip) && !ipv4 {
|
|
||||||
list = append(list, inetaddr(ip))
|
|
||||||
ipv4 = true
|
|
||||||
if ipv6 {
|
|
||||||
swap = true
|
|
||||||
}
|
|
||||||
} else if ipv6only(ip) && !ipv6 {
|
|
||||||
list = append(list, inetaddr(ip))
|
|
||||||
ipv6 = true
|
|
||||||
}
|
|
||||||
if ipv4 && ipv6 {
|
|
||||||
if swap {
|
|
||||||
list[0], list[1] = list[1], list[0]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch len(list) {
|
if len(addrs) == 0 {
|
||||||
case 0:
|
|
||||||
return nil, errNoSuitableAddress
|
return nil, errNoSuitableAddress
|
||||||
case 1:
|
|
||||||
return list[0], nil
|
|
||||||
default:
|
|
||||||
return list, nil
|
|
||||||
}
|
}
|
||||||
}
|
return addrs, nil
|
||||||
|
|
||||||
func firstSupportedAddr(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) netaddr) (netaddr, error) {
|
|
||||||
for _, ip := range ips {
|
|
||||||
if filter(ip) {
|
|
||||||
return inetaddr(ip), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, errNoSuitableAddress
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ipv4only reports whether the kernel supports IPv4 addressing mode
|
// ipv4only reports whether the kernel supports IPv4 addressing mode
|
||||||
@ -214,13 +197,11 @@ func JoinHostPort(host, port string) string {
|
|||||||
return host + ":" + port
|
return host + ":" + port
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveInternetAddr resolves addr that is either a literal IP
|
// internetAddrList resolves addr, which may be a literal IP
|
||||||
// address or a DNS name and returns an internet protocol family
|
// address or a DNS name, and returns a list of internet protocol
|
||||||
// address. It returns a list that contains a pair of different
|
// family addresses. The result contains at least one address when
|
||||||
// address family addresses when addr is a DNS name and the name has
|
// error is nil.
|
||||||
// multiple address family records. The result contains at least one
|
func internetAddrList(net, addr string, deadline time.Time) (addrList, error) {
|
||||||
// address when error is nil.
|
|
||||||
func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) {
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
host, port string
|
host, port string
|
||||||
@ -243,7 +224,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
|
|||||||
default:
|
default:
|
||||||
return nil, UnknownNetworkError(net)
|
return nil, UnknownNetworkError(net)
|
||||||
}
|
}
|
||||||
inetaddr := func(ip IPAddr) netaddr {
|
inetaddr := func(ip IPAddr) Addr {
|
||||||
switch net {
|
switch net {
|
||||||
case "tcp", "tcp4", "tcp6":
|
case "tcp", "tcp4", "tcp6":
|
||||||
return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
|
return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
|
||||||
@ -256,16 +237,16 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
return inetaddr(IPAddr{}), nil
|
return addrList{inetaddr(IPAddr{})}, nil
|
||||||
}
|
}
|
||||||
// Try as a literal IP address.
|
// Try as a literal IP address.
|
||||||
var ip IP
|
var ip IP
|
||||||
if ip = parseIPv4(host); ip != nil {
|
if ip = parseIPv4(host); ip != nil {
|
||||||
return inetaddr(IPAddr{IP: ip}), nil
|
return addrList{inetaddr(IPAddr{IP: ip})}, nil
|
||||||
}
|
}
|
||||||
var zone string
|
var zone string
|
||||||
if ip, zone = parseIPv6(host, true); ip != nil {
|
if ip, zone = parseIPv6(host, true); ip != nil {
|
||||||
return inetaddr(IPAddr{IP: ip, Zone: zone}), nil
|
return addrList{inetaddr(IPAddr{IP: ip, Zone: zone})}, nil
|
||||||
}
|
}
|
||||||
// Try as a DNS name.
|
// Try as a DNS name.
|
||||||
ips, err := lookupIPDeadline(host, deadline)
|
ips, err := lookupIPDeadline(host, deadline)
|
||||||
@ -279,7 +260,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
|
|||||||
if net != "" && net[len(net)-1] == '6' {
|
if net != "" && net[len(net)-1] == '6' {
|
||||||
filter = ipv6only
|
filter = ipv6only
|
||||||
}
|
}
|
||||||
return firstFavoriteAddr(filter, ips, inetaddr)
|
return filterAddrList(filter, ips, inetaddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func zoneToString(zone int) string {
|
func zoneToString(zone int) string {
|
||||||
|
@ -9,14 +9,16 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testInetaddr = func(ip IPAddr) netaddr { return &TCPAddr{IP: ip.IP, Port: 5682, Zone: ip.Zone} }
|
var testInetaddr = func(ip IPAddr) Addr { return &TCPAddr{IP: ip.IP, Port: 5682, Zone: ip.Zone} }
|
||||||
|
|
||||||
var firstFavoriteAddrTests = []struct {
|
var addrListTests = []struct {
|
||||||
filter func(IPAddr) bool
|
filter func(IPAddr) bool
|
||||||
ips []IPAddr
|
ips []IPAddr
|
||||||
inetaddr func(IPAddr) netaddr
|
inetaddr func(IPAddr) Addr
|
||||||
addr netaddr
|
first Addr
|
||||||
err error
|
primaries addrList
|
||||||
|
fallbacks addrList
|
||||||
|
err error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
nil,
|
nil,
|
||||||
@ -25,10 +27,9 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
{IP: IPv6loopback},
|
{IP: IPv6loopback},
|
||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
addrList{
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
|
||||||
},
|
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -38,10 +39,9 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
{IP: IPv4(127, 0, 0, 1)},
|
{IP: IPv4(127, 0, 0, 1)},
|
||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
addrList{
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
|
||||||
},
|
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -52,6 +52,11 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
|
addrList{
|
||||||
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
|
&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -62,6 +67,11 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
||||||
|
addrList{
|
||||||
|
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
||||||
|
&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -73,9 +83,14 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
{IP: ParseIP("fe80::1"), Zone: "eth0"},
|
{IP: ParseIP("fe80::1"), Zone: "eth0"},
|
||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
addrList{
|
addrList{
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
|
&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
|
||||||
|
},
|
||||||
|
addrList{
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
||||||
|
&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
@ -88,9 +103,14 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
{IP: IPv4(192, 168, 0, 1)},
|
{IP: IPv4(192, 168, 0, 1)},
|
||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
|
addrList{
|
||||||
|
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
||||||
|
&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
|
||||||
|
},
|
||||||
addrList{
|
addrList{
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
@ -103,9 +123,14 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
{IP: ParseIP("fe80::1"), Zone: "eth0"},
|
{IP: ParseIP("fe80::1"), Zone: "eth0"},
|
||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
addrList{
|
addrList{
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
|
&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
|
||||||
|
},
|
||||||
|
addrList{
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
||||||
|
&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
@ -118,9 +143,14 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
{IP: IPv4(192, 168, 0, 1)},
|
{IP: IPv4(192, 168, 0, 1)},
|
||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
|
addrList{
|
||||||
|
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
||||||
|
&TCPAddr{IP: ParseIP("fe80::1"), Port: 5682, Zone: "eth0"},
|
||||||
|
},
|
||||||
addrList{
|
addrList{
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
&TCPAddr{IP: IPv4(192, 168, 0, 1), Port: 5682},
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
@ -133,6 +163,8 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
|
addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
|
||||||
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -143,6 +175,8 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
|
||||||
|
addrList{&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}},
|
||||||
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -154,6 +188,8 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
||||||
|
addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
|
||||||
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -164,30 +200,83 @@ var firstFavoriteAddrTests = []struct {
|
|||||||
},
|
},
|
||||||
testInetaddr,
|
testInetaddr,
|
||||||
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
&TCPAddr{IP: IPv6loopback, Port: 5682},
|
||||||
|
addrList{&TCPAddr{IP: IPv6loopback, Port: 5682}},
|
||||||
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
{nil, nil, testInetaddr, nil, errNoSuitableAddress},
|
{nil, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
|
||||||
|
|
||||||
{ipv4only, nil, testInetaddr, nil, errNoSuitableAddress},
|
{ipv4only, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
|
||||||
{ipv4only, []IPAddr{{IP: IPv6loopback}}, testInetaddr, nil, errNoSuitableAddress},
|
{ipv4only, []IPAddr{{IP: IPv6loopback}}, testInetaddr, nil, nil, nil, errNoSuitableAddress},
|
||||||
|
|
||||||
{ipv6only, nil, testInetaddr, nil, errNoSuitableAddress},
|
{ipv6only, nil, testInetaddr, nil, nil, nil, errNoSuitableAddress},
|
||||||
{ipv6only, []IPAddr{{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, errNoSuitableAddress},
|
{ipv6only, []IPAddr{{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, nil, nil, errNoSuitableAddress},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFirstFavoriteAddr(t *testing.T) {
|
func TestAddrList(t *testing.T) {
|
||||||
if !supportsIPv4 || !supportsIPv6 {
|
if !supportsIPv4 || !supportsIPv6 {
|
||||||
t.Skip("ipv4 or ipv6 is not supported")
|
t.Skip("ipv4 or ipv6 is not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range firstFavoriteAddrTests {
|
for i, tt := range addrListTests {
|
||||||
addr, err := firstFavoriteAddr(tt.filter, tt.ips, tt.inetaddr)
|
addrs, err := filterAddrList(tt.filter, tt.ips, tt.inetaddr)
|
||||||
if err != tt.err {
|
if err != tt.err {
|
||||||
t.Errorf("#%v: got %v; expected %v", i, err, tt.err)
|
t.Errorf("#%v: got %v; want %v", i, err, tt.err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(addr, tt.addr) {
|
if tt.err != nil {
|
||||||
t.Errorf("#%v: got %v; expected %v", i, addr, tt.addr)
|
if len(addrs) != 0 {
|
||||||
|
t.Errorf("#%v: got %v; want 0", len(addrs))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
first := addrs.first(isIPv4)
|
||||||
|
if !reflect.DeepEqual(first, tt.first) {
|
||||||
|
t.Errorf("#%v: got %v; want %v", i, first, tt.first)
|
||||||
|
}
|
||||||
|
primaries, fallbacks := addrs.partition(isIPv4)
|
||||||
|
if !reflect.DeepEqual(primaries, tt.primaries) {
|
||||||
|
t.Errorf("#%v: got %v; want %v", i, primaries, tt.primaries)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(fallbacks, tt.fallbacks) {
|
||||||
|
t.Errorf("#%v: got %v; want %v", i, fallbacks, tt.fallbacks)
|
||||||
|
}
|
||||||
|
expectedLen := len(primaries) + len(fallbacks)
|
||||||
|
if len(addrs) != expectedLen {
|
||||||
|
t.Errorf("#%v: got %v; want %v", i, len(addrs), expectedLen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddrListPartition(t *testing.T) {
|
||||||
|
addrs := addrList{
|
||||||
|
&IPAddr{IP: ParseIP("fe80::"), Zone: "eth0"},
|
||||||
|
&IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
|
||||||
|
&IPAddr{IP: ParseIP("fe80::2"), Zone: "eth0"},
|
||||||
|
}
|
||||||
|
cases := []struct {
|
||||||
|
lastByte byte
|
||||||
|
primaries addrList
|
||||||
|
fallbacks addrList
|
||||||
|
}{
|
||||||
|
{0, addrList{addrs[0]}, addrList{addrs[1], addrs[2]}},
|
||||||
|
{1, addrList{addrs[0], addrs[2]}, addrList{addrs[1]}},
|
||||||
|
{2, addrList{addrs[0], addrs[1]}, addrList{addrs[2]}},
|
||||||
|
{3, addrList{addrs[0], addrs[1], addrs[2]}, nil},
|
||||||
|
}
|
||||||
|
for i, tt := range cases {
|
||||||
|
// Inverting the function's output should not affect the outcome.
|
||||||
|
for _, invert := range []bool{false, true} {
|
||||||
|
primaries, fallbacks := addrs.partition(func(a Addr) bool {
|
||||||
|
ip := a.(*IPAddr).IP
|
||||||
|
return (ip[len(ip)-1] == tt.lastByte) != invert
|
||||||
|
})
|
||||||
|
if !reflect.DeepEqual(primaries, tt.primaries) {
|
||||||
|
t.Errorf("#%v: got %v; want %v", i, primaries, tt.primaries)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(fallbacks, tt.fallbacks) {
|
||||||
|
t.Errorf("#%v: got %v; want %v", i, fallbacks, tt.fallbacks)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@ import (
|
|||||||
type sockaddr interface {
|
type sockaddr interface {
|
||||||
Addr
|
Addr
|
||||||
|
|
||||||
netaddr
|
|
||||||
|
|
||||||
// family returns the platform-dependent address family
|
// family returns the platform-dependent address family
|
||||||
// identifier.
|
// identifier.
|
||||||
family() int
|
family() int
|
||||||
|
@ -32,13 +32,6 @@ func (a *TCPAddr) isWildcard() bool {
|
|||||||
return a.IP.IsUnspecified()
|
return a.IP.IsUnspecified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TCPAddr) toAddr() Addr {
|
|
||||||
if a == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolveTCPAddr parses addr as a TCP address of the form "host:port"
|
// ResolveTCPAddr parses addr as a TCP address of the form "host:port"
|
||||||
// or "[ipv6-host%zone]:port" and resolves a pair of domain name and
|
// or "[ipv6-host%zone]:port" and resolves a pair of domain name and
|
||||||
// port name on the network net, which must be "tcp", "tcp4" or
|
// port name on the network net, which must be "tcp", "tcp4" or
|
||||||
@ -53,9 +46,9 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, UnknownNetworkError(net)
|
return nil, UnknownNetworkError(net)
|
||||||
}
|
}
|
||||||
a, err := resolveInternetAddr(net, addr, noDeadline)
|
addrs, err := internetAddrList(net, addr, noDeadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return a.toAddr().(*TCPAddr), nil
|
return addrs.first(isIPv4).(*TCPAddr), nil
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,6 @@ func (a *UDPAddr) isWildcard() bool {
|
|||||||
return a.IP.IsUnspecified()
|
return a.IP.IsUnspecified()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *UDPAddr) toAddr() Addr {
|
|
||||||
if a == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolveUDPAddr parses addr as a UDP address of the form "host:port"
|
// ResolveUDPAddr parses addr as a UDP address of the form "host:port"
|
||||||
// or "[ipv6-host%zone]:port" and resolves a pair of domain name and
|
// or "[ipv6-host%zone]:port" and resolves a pair of domain name and
|
||||||
// port name on the network net, which must be "udp", "udp4" or
|
// port name on the network net, which must be "udp", "udp4" or
|
||||||
@ -53,9 +46,9 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, UnknownNetworkError(net)
|
return nil, UnknownNetworkError(net)
|
||||||
}
|
}
|
||||||
a, err := resolveInternetAddr(net, addr, noDeadline)
|
addrs, err := internetAddrList(net, addr, noDeadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return a.toAddr().(*UDPAddr), nil
|
return addrs.first(isIPv4).(*UDPAddr), nil
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,10 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
|
|||||||
return 0, nil, syscall.EINVAL
|
return 0, nil, syscall.EINVAL
|
||||||
}
|
}
|
||||||
n, addr, err := c.ReadFromUDP(b)
|
n, addr, err := c.ReadFromUDP(b)
|
||||||
return n, addr.toAddr(), err
|
if addr == nil {
|
||||||
|
return n, nil, err
|
||||||
|
}
|
||||||
|
return n, addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadMsgUDP reads a packet from c, copying the payload into b and
|
// ReadMsgUDP reads a packet from c, copying the payload into b and
|
||||||
|
@ -23,13 +23,6 @@ func (a *UnixAddr) String() string {
|
|||||||
return a.Name
|
return a.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *UnixAddr) toAddr() Addr {
|
|
||||||
if a == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolveUnixAddr parses addr as a Unix domain socket address.
|
// ResolveUnixAddr parses addr as a Unix domain socket address.
|
||||||
// The string net gives the network name, "unix", "unixgram" or
|
// The string net gives the network name, "unix", "unixgram" or
|
||||||
// "unixpacket".
|
// "unixpacket".
|
||||||
|
@ -133,7 +133,10 @@ func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) {
|
|||||||
return 0, nil, syscall.EINVAL
|
return 0, nil, syscall.EINVAL
|
||||||
}
|
}
|
||||||
n, addr, err := c.ReadFromUnix(b)
|
n, addr, err := c.ReadFromUnix(b)
|
||||||
return n, addr.toAddr(), err
|
if addr == nil {
|
||||||
|
return n, nil, err
|
||||||
|
}
|
||||||
|
return n, addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadMsgUnix reads a packet from c, copying the payload into b and
|
// ReadMsgUnix reads a packet from c, copying the payload into b and
|
||||||
|
Loading…
Reference in New Issue
Block a user