diff options
Diffstat (limited to 'libgo/go/net/ipsock.go')
-rw-r--r-- | libgo/go/net/ipsock.go | 201 |
1 files changed, 84 insertions, 117 deletions
diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index dda85780308..6e75c33d534 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.go @@ -26,113 +26,82 @@ var ( supportsIPv4map bool ) -func init() { - sysInit() - supportsIPv4 = probeIPv4Stack() - supportsIPv6, supportsIPv4map = probeIPv6Stack() -} +// An addrList represents a list of network endpoint addresses. +type addrList []Addr -// A netaddr represents a network endpoint address or a list of -// network endpoint addresses. -type netaddr interface { - // toAddr returns the address represented in Addr interface. - // It returns a nil interface when the address is nil. - toAddr() Addr +// isIPv4 returns true if the Addr contains an IPv4 address. +func isIPv4(addr Addr) bool { + 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. -type addrList []netaddr +// first returns the first address which satisfies strategy, or if +// none do, then the first address of any kind. +func (addrs addrList) first(strategy func(Addr) bool) Addr { + for _, addr := range addrs { + if strategy(addr) { + return addr + } + } + return addrs[0] +} -func (al addrList) toAddr() Addr { - switch len(al) { - case 0: - 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() +// 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") -// firstFavoriteAddr returns an address or a list of addresses that -// implement the netaddr interface. Known filters are nil, ipv4only -// and ipv6only. It returns any address when filter is nil. The result -// contains at least one address when error is nil. -func firstFavoriteAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) { - if filter != nil { - return firstSupportedAddr(filter, ips, inetaddr) - } - var ( - ipv4, ipv6, swap bool - list addrList - ) +// filterAddrList applies a filter to a list of IP addresses, +// yielding a list of Addr objects. Known filters are nil, ipv4only, +// and ipv6only. It returns every address when the filter is nil. +// The result contains at least one address when error is nil. +func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr) (addrList, error) { + var addrs addrList for _, ip := range ips { - // We'll take any IP address, but since the dialing - // code does not yet try multiple addresses - // 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 ip4 := ipv4only(ip); ip4 != nil && !ipv4 { - list = append(list, inetaddr(ip4)) - ipv4 = true - if ipv6 { - swap = true - } - } else if ip6 := ipv6only(ip); ip6 != nil && !ipv6 { - list = append(list, inetaddr(ip6)) - ipv6 = true - } - if ipv4 && ipv6 { - if swap { - list[0], list[1] = list[1], list[0] - } - break + if filter == nil || filter(ip) { + addrs = append(addrs, inetaddr(ip)) } } - switch len(list) { - case 0: + if len(addrs) == 0 { return nil, errNoSuitableAddress - case 1: - return list[0], nil - default: - return list, nil - } -} - -func firstSupportedAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) { - for _, ip := range ips { - if ip := filter(ip); ip != nil { - return inetaddr(ip), nil - } } - return nil, errNoSuitableAddress + return addrs, nil } -// ipv4only returns IPv4 addresses that we can use with the kernel's -// IPv4 addressing modes. If ip is an IPv4 address, ipv4only returns ip. -// Otherwise it returns nil. -func ipv4only(ip IP) IP { - if supportsIPv4 && ip.To4() != nil { - return ip - } - return nil +// ipv4only reports whether the kernel supports IPv4 addressing mode +// and addr is an IPv4 address. +func ipv4only(addr IPAddr) bool { + return supportsIPv4 && addr.IP.To4() != nil } -// ipv6only returns IPv6 addresses that we can use with the kernel's -// IPv6 addressing modes. It returns IPv4-mapped IPv6 addresses as -// nils and returns other IPv6 address types as IPv6 addresses. -func ipv6only(ip IP) IP { - if supportsIPv6 && len(ip) == IPv6len && ip.To4() == nil { - return ip - } - return nil +// ipv6only reports whether the kernel supports IPv6 addressing mode +// and addr is an IPv6 address except IPv4-mapped IPv6 address. +func ipv6only(addr IPAddr) bool { + return supportsIPv6 && len(addr.IP) == IPv6len && addr.IP.To4() == nil } // SplitHostPort splits a network address of the form "host:port", @@ -153,7 +122,7 @@ func SplitHostPort(hostport string) (host, port string, err error) { // Expect the first ']' just before the last ':'. end := byteIndex(hostport, ']') if end < 0 { - err = &AddrError{"missing ']' in address", hostport} + err = &AddrError{Err: "missing ']' in address", Addr: hostport} return } switch end + 1 { @@ -182,11 +151,11 @@ func SplitHostPort(hostport string) (host, port string, err error) { } } if byteIndex(hostport[j:], '[') >= 0 { - err = &AddrError{"unexpected '[' in address", hostport} + err = &AddrError{Err: "unexpected '[' in address", Addr: hostport} return } if byteIndex(hostport[k:], ']') >= 0 { - err = &AddrError{"unexpected ']' in address", hostport} + err = &AddrError{Err: "unexpected ']' in address", Addr: hostport} return } @@ -194,15 +163,15 @@ func SplitHostPort(hostport string) (host, port string, err error) { return missingPort: - err = &AddrError{"missing port in address", hostport} + err = &AddrError{Err: "missing port in address", Addr: hostport} return tooManyColons: - err = &AddrError{"too many colons in address", hostport} + err = &AddrError{Err: "too many colons in address", Addr: hostport} return missingBrackets: - err = &AddrError{"missing brackets in address", hostport} + err = &AddrError{Err: "missing brackets in address", Addr: hostport} return } @@ -228,17 +197,15 @@ func JoinHostPort(host, port string) string { return host + ":" + port } -// resolveInternetAddr resolves addr that is either a literal IP -// address or a DNS name and returns an internet protocol family -// address. It returns a list that contains a pair of different -// address family addresses when addr is a DNS name and the name has -// multiple address family records. The result contains at least one -// address when error is nil. -func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) { +// internetAddrList resolves addr, which may be a literal IP +// address or a DNS name, and returns a list of internet protocol +// family addresses. The result contains at least one address when +// error is nil. +func internetAddrList(net, addr string, deadline time.Time) (addrList, error) { var ( - err error - host, port, zone string - portnum int + err error + host, port string + portnum int ) switch net { case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": @@ -257,43 +224,43 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) default: return nil, UnknownNetworkError(net) } - inetaddr := func(ip IP) netaddr { + inetaddr := func(ip IPAddr) Addr { switch net { case "tcp", "tcp4", "tcp6": - return &TCPAddr{IP: ip, Port: portnum, Zone: zone} + return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone} case "udp", "udp4", "udp6": - return &UDPAddr{IP: ip, Port: portnum, Zone: zone} + return &UDPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone} case "ip", "ip4", "ip6": - return &IPAddr{IP: ip, Zone: zone} + return &IPAddr{IP: ip.IP, Zone: ip.Zone} default: panic("unexpected network: " + net) } } if host == "" { - return inetaddr(nil), nil + return addrList{inetaddr(IPAddr{})}, nil } // Try as a literal IP address. var ip IP if ip = parseIPv4(host); ip != nil { - return inetaddr(ip), nil + return addrList{inetaddr(IPAddr{IP: ip})}, nil } + var zone string if ip, zone = parseIPv6(host, true); ip != nil { - return inetaddr(ip), nil + return addrList{inetaddr(IPAddr{IP: ip, Zone: zone})}, nil } // Try as a DNS name. - host, zone = splitHostZone(host) ips, err := lookupIPDeadline(host, deadline) if err != nil { return nil, err } - var filter func(IP) IP + var filter func(IPAddr) bool if net != "" && net[len(net)-1] == '4' { filter = ipv4only } - if net != "" && net[len(net)-1] == '6' || zone != "" { + if net != "" && net[len(net)-1] == '6' { filter = ipv6only } - return firstFavoriteAddr(filter, ips, inetaddr) + return filterAddrList(filter, ips, inetaddr) } func zoneToString(zone int) string { @@ -303,7 +270,7 @@ func zoneToString(zone int) string { if ifi, err := InterfaceByIndex(zone); err == nil { return ifi.Name } - return itod(uint(zone)) + return uitoa(uint(zone)) } func zoneToInt(zone string) int { |