]> Cypherpunks repositories - gostls13.git/commitdiff
net: LookupHost and Resolve{TCP,UDP,IP}Addr should use zone from getaddrinfo
authorAndrew Pilloud <andrewpilloud@igneoussystems.com>
Fri, 13 Feb 2015 04:24:47 +0000 (20:24 -0800)
committerMikio Hara <mikioh.mikioh@gmail.com>
Sun, 8 Mar 2015 09:58:11 +0000 (09:58 +0000)
The unix and windows getaddrinfo calls return a zone with IPv6
addresses. IPv6 link-local addresses returned are only valid on the
given zone. When the zone is dropped, connections to the address
will fail. This patch replaces IP with IPAddr in several internal
resolver functions, and plumbs through the zone.

Change-Id: Ifea891654f586f15b76988464f82e04a42ccff6d
Reviewed-on: https://go-review.googlesource.com/5851
Reviewed-by: Mikio Hara <mikioh.mikioh@gmail.com>
src/net/cgo_stub.go
src/net/cgo_unix.go
src/net/dnsclient_unix.go
src/net/ipsock.go
src/net/ipsock_test.go
src/net/lookup.go
src/net/lookup_plan9.go
src/net/lookup_stub.go
src/net/lookup_unix.go
src/net/lookup_windows.go

index f533c14212fb9836dec8eeb35348ae625b415060..d2d40da74f14fc9201f01d9704111e1afb37b225 100644 (file)
@@ -16,7 +16,7 @@ func cgoLookupPort(network, service string) (port int, err error, completed bool
        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
 }
 
index 1f366ee5c6d1596dee3d623b2b8d0969ea8e4ff6..eba57773479321a8b5532ae4104d9b830cfa7765 100644 (file)
@@ -81,7 +81,7 @@ func cgoLookupPort(net, service string) (port int, err error, completed bool) {
        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()
 
@@ -135,16 +135,18 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
                        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
 }
index 7511083f7955e6304230038893a8785853b90aaf..30c7ada5baa334eebec9d2271be69960d5c6ee82 100644 (file)
@@ -361,13 +361,15 @@ func goLookupHost(name string) (addrs []string, err error) {
 // 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 {
@@ -396,9 +398,15 @@ func goLookupIP(name string) (addrs []IP, err error) {
                }
                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 {
index 858c6ef12c03b2733ceb39f38797485209c24853..98d2dbffb7efc1544a6d26fa8eecc719761aab77 100644 (file)
@@ -64,7 +64,7 @@ var errNoSuitableAddress = errors.New("no suitable address found")
 // 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)
        }
@@ -79,14 +79,14 @@ func firstFavoriteAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr)
                // 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 {
@@ -106,33 +106,25 @@ func firstFavoriteAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr)
        }
 }
 
-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",
@@ -236,9 +228,9 @@ func JoinHostPort(host, port string) string {
 // 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":
@@ -257,40 +249,40 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error)
        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)
index 9ecaaec69f671b5e35f97b156014a3c60b3d3505..7567dad5230ef5ee8e95d4a2f73716a565ef99f7 100644 (file)
@@ -9,20 +9,20 @@ import (
        "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{
@@ -33,9 +33,9 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                nil,
-               []IP{
-                       IPv6loopback,
-                       IPv4(127, 0, 0, 1),
+               []IPAddr{
+                       IPAddr{IP: IPv6loopback},
+                       IPAddr{IP: IPv4(127, 0, 0, 1)},
                },
                testInetaddr,
                addrList{
@@ -46,9 +46,9 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                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},
@@ -56,9 +56,9 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                nil,
-               []IP{
-                       IPv6loopback,
-                       ParseIP("fe80::1"),
+               []IPAddr{
+                       IPAddr{IP: IPv6loopback},
+                       IPAddr{IP: ParseIP("fe80::1"), Zone: "eth0"},
                },
                testInetaddr,
                &TCPAddr{IP: IPv6loopback, Port: 5682},
@@ -66,11 +66,11 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                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{
@@ -81,11 +81,11 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                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{
@@ -96,11 +96,11 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                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{
@@ -111,11 +111,11 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                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{
@@ -127,9 +127,9 @@ var firstFavoriteAddrTests = []struct {
 
        {
                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},
@@ -137,9 +137,9 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                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},
@@ -148,9 +148,9 @@ var firstFavoriteAddrTests = []struct {
 
        {
                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},
@@ -158,9 +158,9 @@ var firstFavoriteAddrTests = []struct {
        },
        {
                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},
@@ -170,10 +170,10 @@ var firstFavoriteAddrTests = []struct {
        {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) {
index aeffe6c9b725312f8bab3669a129bfb563ae71c8..65abc81309f1cb46cf1ffceacaa687ad5afb005a 100644 (file)
@@ -27,8 +27,16 @@ func LookupHost(host string) (addrs []string, err error) {
 
 // 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
@@ -36,7 +44,7 @@ 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)
        })
@@ -45,13 +53,13 @@ func lookupIPMerge(host string) (addrs []IP, err error) {
 
 // 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
        }
@@ -59,7 +67,7 @@ func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IP, error) {
 }
 
 // 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)
        }
index b80ac10e0d9d0ef155c123e11fc26a031f200873..73abbad93be460ad2aaedfe05419f17a86ae9df9 100644 (file)
@@ -147,14 +147,16 @@ loop:
        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
index 502aafb2702312b79129a634f03f93ed0f682a94..5636198f88186ca5d783640dde349c74868c0c77 100644 (file)
@@ -16,7 +16,7 @@ func lookupHost(host string) (addrs []string, err error) {
        return nil, syscall.ENOPROTOOPT
 }
 
-func lookupIP(host string) (ips []IP, err error) {
+func lookupIP(host string) (addrs []IPAddr, err error) {
        return nil, syscall.ENOPROTOOPT
 }
 
index a54578456d7c6ac49df2a0ae56460c90afaa3e0c..473adf87f6dedd3e02d7dc0be6fbde10dfd7cb2b 100644 (file)
@@ -60,7 +60,7 @@ func lookupHost(host string) (addrs []string, err error) {
        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)
index 6a925b0a7adad630e569ce237905c12400730747..6a8d9181ba053c1eead21764919bb118c4838d91 100644 (file)
@@ -62,7 +62,7 @@ func lookupHost(name string) (addrs []string, err error) {
        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 {
@@ -71,9 +71,9 @@ func gethostbyname(name string) (addrs []IP, err error) {
        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.
@@ -82,11 +82,11 @@ func gethostbyname(name string) (addrs []IP, err error) {
        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)
@@ -99,10 +99,10 @@ func oldLookupIP(name string) (addrs []IP, err error) {
                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{
@@ -116,16 +116,17 @@ func newLookupIP(name string) (addrs []IP, err error) {
                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)
                }