return 0, nil, false
}
-func cgoLookupIP(name string) (addrs []IP, err error, completed bool) {
+func cgoLookupIP(name string) (addrs []IPAddr, err error, completed bool) {
return nil, nil, false
}
return 0, &AddrError{"unknown port", net + "/" + service}, true
}
-func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, completed bool) {
+func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, completed bool) {
acquireThread()
defer releaseThread()
continue
case C.AF_INET:
sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
- addrs = append(addrs, copyIP(sa.Addr[:]))
+ addr := IPAddr{IP: copyIP(sa.Addr[:])}
+ addrs = append(addrs, addr)
case C.AF_INET6:
sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
- addrs = append(addrs, copyIP(sa.Addr[:]))
+ addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneToString(int(sa.Scope_id))}
+ addrs = append(addrs, addr)
}
}
return addrs, cname, nil, true
}
-func cgoLookupIP(name string) (addrs []IP, err error, completed bool) {
+func cgoLookupIP(name string) (addrs []IPAddr, err error, completed bool) {
addrs, _, err, completed = cgoLookupIPCNAME(name)
return
}
// Normally we let cgo use the C library resolver instead of
// depending on our lookup code, so that Go and C get the same
// answers.
-func goLookupIP(name string) (addrs []IP, err error) {
+func goLookupIP(name string) (addrs []IPAddr, err error) {
// Use entries from /etc/hosts if possible.
haddrs := lookupStaticHost(name)
if len(haddrs) > 0 {
for _, haddr := range haddrs {
+ haddr, zone := splitHostZone(haddr)
if ip := ParseIP(haddr); ip != nil {
- addrs = append(addrs, ip)
+ addr := IPAddr{IP: ip, Zone: zone}
+ addrs = append(addrs, addr)
}
}
if len(addrs) > 0 {
}
switch racer.qtype {
case dnsTypeA:
- addrs = append(addrs, convertRR_A(racer.rrs)...)
+ for _, ip := range convertRR_A(racer.rrs) {
+ addr := IPAddr{IP: ip}
+ addrs = append(addrs, addr)
+ }
case dnsTypeAAAA:
- addrs = append(addrs, convertRR_AAAA(racer.rrs)...)
+ for _, ip := range convertRR_AAAA(racer.rrs) {
+ addr := IPAddr{IP: ip}
+ addrs = append(addrs, addr)
+ }
}
}
if len(addrs) == 0 && lastErr != nil {
// 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) {
+func firstFavoriteAddr(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) netaddr) (netaddr, error) {
if filter != nil {
return firstSupportedAddr(filter, ips, inetaddr)
}
// 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))
+ if ipv4only(ip) && !ipv4 {
+ list = append(list, inetaddr(ip))
ipv4 = true
if ipv6 {
swap = true
}
- } else if ip6 := ipv6only(ip); ip6 != nil && !ipv6 {
- list = append(list, inetaddr(ip6))
+ } else if ipv6only(ip) && !ipv6 {
+ list = append(list, inetaddr(ip))
ipv6 = true
}
if ipv4 && ipv6 {
}
}
-func firstSupportedAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) {
+func firstSupportedAddr(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) netaddr) (netaddr, error) {
for _, ip := range ips {
- if ip := filter(ip); ip != nil {
+ if filter(ip) {
return inetaddr(ip), nil
}
}
return nil, errNoSuitableAddress
}
-// 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",
// address when error is nil.
func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, 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":
default:
return nil, UnknownNetworkError(net)
}
- inetaddr := func(ip IP) netaddr {
+ inetaddr := func(ip IPAddr) netaddr {
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 inetaddr(IPAddr{}), nil
}
// Try as a literal IP address.
var ip IP
if ip = parseIPv4(host); ip != nil {
- return inetaddr(ip), nil
+ return inetaddr(IPAddr{IP: ip}), nil
}
+ var zone string
if ip, zone = parseIPv6(host, true); ip != nil {
- return inetaddr(ip), nil
+ return 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)
"testing"
)
-var testInetaddr = func(ip IP) netaddr { return &TCPAddr{IP: ip, Port: 5682} }
+var testInetaddr = func(ip IPAddr) netaddr { return &TCPAddr{IP: ip.IP, Port: 5682, Zone: ip.Zone} }
var firstFavoriteAddrTests = []struct {
- filter func(IP) IP
- ips []IP
- inetaddr func(IP) netaddr
+ filter func(IPAddr) bool
+ ips []IPAddr
+ inetaddr func(IPAddr) netaddr
addr netaddr
err error
}{
{
nil,
- []IP{
- IPv4(127, 0, 0, 1),
- IPv6loopback,
+ []IPAddr{
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
+ IPAddr{IP: IPv6loopback},
},
testInetaddr,
addrList{
},
{
nil,
- []IP{
- IPv6loopback,
- IPv4(127, 0, 0, 1),
+ []IPAddr{
+ IPAddr{IP: IPv6loopback},
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
},
testInetaddr,
addrList{
},
{
nil,
- []IP{
- IPv4(127, 0, 0, 1),
- IPv4(192, 168, 0, 1),
+ []IPAddr{
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
+ IPAddr{IP: IPv4(192, 168, 0, 1)},
},
testInetaddr,
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
},
{
nil,
- []IP{
- IPv6loopback,
- ParseIP("fe80::1"),
+ []IPAddr{
+ IPAddr{IP: IPv6loopback},
+ IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
},
testInetaddr,
&TCPAddr{IP: IPv6loopback, Port: 5682},
},
{
nil,
- []IP{
- IPv4(127, 0, 0, 1),
- IPv4(192, 168, 0, 1),
- IPv6loopback,
- ParseIP("fe80::1"),
+ []IPAddr{
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
+ IPAddr{IP: IPv4(192, 168, 0, 1)},
+ IPAddr{IP: IPv6loopback},
+ IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
},
testInetaddr,
addrList{
},
{
nil,
- []IP{
- IPv6loopback,
- ParseIP("fe80::1"),
- IPv4(127, 0, 0, 1),
- IPv4(192, 168, 0, 1),
+ []IPAddr{
+ IPAddr{IP: IPv6loopback},
+ IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
+ IPAddr{IP: IPv4(192, 168, 0, 1)},
},
testInetaddr,
addrList{
},
{
nil,
- []IP{
- IPv4(127, 0, 0, 1),
- IPv6loopback,
- IPv4(192, 168, 0, 1),
- ParseIP("fe80::1"),
+ []IPAddr{
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
+ IPAddr{IP: IPv6loopback},
+ IPAddr{IP: IPv4(192, 168, 0, 1)},
+ IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
},
testInetaddr,
addrList{
},
{
nil,
- []IP{
- IPv6loopback,
- IPv4(127, 0, 0, 1),
- ParseIP("fe80::1"),
- IPv4(192, 168, 0, 1),
+ []IPAddr{
+ IPAddr{IP: IPv6loopback},
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
+ IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
+ IPAddr{IP: IPv4(192, 168, 0, 1)},
},
testInetaddr,
addrList{
{
ipv4only,
- []IP{
- IPv4(127, 0, 0, 1),
- IPv6loopback,
+ []IPAddr{
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
+ IPAddr{IP: IPv6loopback},
},
testInetaddr,
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
},
{
ipv4only,
- []IP{
- IPv6loopback,
- IPv4(127, 0, 0, 1),
+ []IPAddr{
+ IPAddr{IP: IPv6loopback},
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
},
testInetaddr,
&TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682},
{
ipv6only,
- []IP{
- IPv4(127, 0, 0, 1),
- IPv6loopback,
+ []IPAddr{
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
+ IPAddr{IP: IPv6loopback},
},
testInetaddr,
&TCPAddr{IP: IPv6loopback, Port: 5682},
},
{
ipv6only,
- []IP{
- IPv6loopback,
- IPv4(127, 0, 0, 1),
+ []IPAddr{
+ IPAddr{IP: IPv6loopback},
+ IPAddr{IP: IPv4(127, 0, 0, 1)},
},
testInetaddr,
&TCPAddr{IP: IPv6loopback, Port: 5682},
{nil, nil, testInetaddr, nil, errNoSuitableAddress},
{ipv4only, nil, testInetaddr, nil, errNoSuitableAddress},
- {ipv4only, []IP{IPv6loopback}, testInetaddr, nil, errNoSuitableAddress},
+ {ipv4only, []IPAddr{IPAddr{IP: IPv6loopback}}, testInetaddr, nil, errNoSuitableAddress},
{ipv6only, nil, testInetaddr, nil, errNoSuitableAddress},
- {ipv6only, []IP{IPv4(127, 0, 0, 1)}, testInetaddr, nil, errNoSuitableAddress},
+ {ipv6only, []IPAddr{IPAddr{IP: IPv4(127, 0, 0, 1)}}, testInetaddr, nil, errNoSuitableAddress},
}
func TestFirstFavoriteAddr(t *testing.T) {
// LookupIP looks up host using the local resolver.
// It returns an array of that host's IPv4 and IPv6 addresses.
-func LookupIP(host string) (addrs []IP, err error) {
- return lookupIPMerge(host)
+func LookupIP(host string) (ips []IP, err error) {
+ addrs, err := lookupIPMerge(host)
+ if err != nil {
+ return
+ }
+ ips = make([]IP, len(addrs))
+ for i, addr := range addrs {
+ ips[i] = addr.IP
+ }
+ return
}
var lookupGroup singleflight
// lookupIPMerge wraps lookupIP, but makes sure that for any given
// host, only one lookup is in-flight at a time. The returned memory
// is always owned by the caller.
-func lookupIPMerge(host string) (addrs []IP, err error) {
+func lookupIPMerge(host string) (addrs []IPAddr, err error) {
addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
return lookupIP(host)
})
// lookupIPReturn turns the return values from singleflight.Do into
// the return values from LookupIP.
-func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IP, error) {
+func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
if err != nil {
return nil, err
}
- addrs := addrsi.([]IP)
+ addrs := addrsi.([]IPAddr)
if shared {
- clone := make([]IP, len(addrs))
+ clone := make([]IPAddr, len(addrs))
copy(clone, addrs)
addrs = clone
}
}
// lookupIPDeadline looks up a hostname with a deadline.
-func lookupIPDeadline(host string, deadline time.Time) (addrs []IP, err error) {
+func lookupIPDeadline(host string, deadline time.Time) (addrs []IPAddr, err error) {
if deadline.IsZero() {
return lookupIPMerge(host)
}
return
}
-func lookupIP(host string) (ips []IP, err error) {
- addrs, err := LookupHost(host)
+func lookupIP(host string) (addrs []IPAddr, err error) {
+ lits, err := LookupHost(host)
if err != nil {
return
}
- for _, addr := range addrs {
- if ip := ParseIP(addr); ip != nil {
- ips = append(ips, ip)
+ for _, lit := range lits {
+ host, zone := splitHostZone(lit)
+ if ip := ParseIP(host); ip != nil {
+ addr := IPAddr{IP: ip, Zone: zone}
+ addrs = append(addrs, addr)
}
}
return
return nil, syscall.ENOPROTOOPT
}
-func lookupIP(host string) (ips []IP, err error) {
+func lookupIP(host string) (addrs []IPAddr, err error) {
return nil, syscall.ENOPROTOOPT
}
return
}
-func lookupIP(host string) (addrs []IP, err error) {
+func lookupIP(host string) (addrs []IPAddr, err error) {
addrs, err, ok := cgoLookupIP(host)
if !ok {
addrs, err = goLookupIP(host)
return
}
-func gethostbyname(name string) (addrs []IP, err error) {
+func gethostbyname(name string) (addrs []IPAddr, err error) {
// caller already acquired thread
h, err := syscall.GetHostByName(name)
if err != nil {
switch h.AddrType {
case syscall.AF_INET:
i := 0
- addrs = make([]IP, 100) // plenty of room to grow
+ addrs = make([]IPAddr, 100) // plenty of room to grow
for p := (*[100](*[4]byte))(unsafe.Pointer(h.AddrList)); i < cap(addrs) && p[i] != nil; i++ {
- addrs[i] = IPv4(p[i][0], p[i][1], p[i][2], p[i][3])
+ addrs[i] = IPAddr{IP: IPv4(p[i][0], p[i][1], p[i][2], p[i][3])}
}
addrs = addrs[0:i]
default: // TODO(vcc): Implement non IPv4 address lookups.
return addrs, nil
}
-func oldLookupIP(name string) (addrs []IP, err error) {
+func oldLookupIP(name string) (addrs []IPAddr, err error) {
// GetHostByName return value is stored in thread local storage.
// Start new os thread before the call to prevent races.
type result struct {
- addrs []IP
+ addrs []IPAddr
err error
}
ch := make(chan result)
ch <- result{addrs: addrs, err: err}
}()
r := <-ch
- return r.addrs, r.err
+ return addrs, r.err
}
-func newLookupIP(name string) (addrs []IP, err error) {
+func newLookupIP(name string) (addrs []IPAddr, err error) {
acquireThread()
defer releaseThread()
hints := syscall.AddrinfoW{
return nil, os.NewSyscallError("GetAddrInfoW", e)
}
defer syscall.FreeAddrInfoW(result)
- addrs = make([]IP, 0, 5)
+ addrs = make([]IPAddr, 0, 5)
for ; result != nil; result = result.Next {
addr := unsafe.Pointer(result.Addr)
switch result.Family {
case syscall.AF_INET:
a := (*syscall.RawSockaddrInet4)(addr).Addr
- addrs = append(addrs, IPv4(a[0], a[1], a[2], a[3]))
+ addrs = append(addrs, IPAddr{IP: IPv4(a[0], a[1], a[2], a[3])})
case syscall.AF_INET6:
a := (*syscall.RawSockaddrInet6)(addr).Addr
- addrs = append(addrs, IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]})
+ zone := zoneToString(int((*syscall.RawSockaddrInet6)(addr).Scope_id))
+ addrs = append(addrs, IPAddr{IP: IP{a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]}, Zone: zone})
default:
return nil, os.NewSyscallError("LookupIP", syscall.EWINDOWS)
}