]> Cypherpunks repositories - gostls13.git/commitdiff
net: parse IPv6 address with zone using DefaultResolver.Lookup{Host,IPAddr}
authorMichael Fraenkel <michael.fraenkel@gmail.com>
Sun, 26 Nov 2017 13:22:22 +0000 (08:22 -0500)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 27 Jun 2018 21:37:24 +0000 (21:37 +0000)
Allow a zone to be included with the ip address that is parsed when
using DefaultResolver's LookupHost or LookupIPAddr

Fixes #20790
Fixes #20767

Change-Id: I4e0baf9ade6a095af10a1b85ca6216788ba680ae
Reviewed-on: https://go-review.googlesource.com/79935
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/cgo_unix.go
src/net/dnsconfig_unix.go
src/net/hosts.go
src/net/ip.go
src/net/ipsock.go
src/net/lookup.go
src/net/lookup_test.go

index 1baa01f0360515b1094cb7c25ba9a5bdd6ff5295..3db867a080e1fafdcd17005ece17a6b525189bb5 100644 (file)
@@ -249,7 +249,7 @@ func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error,
        var zone string
        ip := parseIPv4(addr)
        if ip == nil {
-               ip, zone = parseIPv6(addr, true)
+               ip, zone = parseIPv6Zone(addr)
        }
        if ip == nil {
                return nil, &DNSError{Err: "invalid address", Name: addr}, true
index 53fa14687f8d37d74ad2fcd92510316761899bca..707fd6f6fe8419f6ecbb7a26633f1bdeb9b7a964 100644 (file)
@@ -73,7 +73,7 @@ func dnsReadConfig(filename string) *dnsConfig {
                                // to look it up.
                                if parseIPv4(f[1]) != nil {
                                        conf.servers = append(conf.servers, JoinHostPort(f[1], "53"))
-                               } else if ip, _ := parseIPv6(f[1], true); ip != nil {
+                               } else if ip, _ := parseIPv6Zone(f[1]); ip != nil {
                                        conf.servers = append(conf.servers, JoinHostPort(f[1], "53"))
                                }
                        }
index 9c101c6ef5b307d5f7b2005066d077aeb4faad36..ebc0353a7fb6d774bf8dcfe41f6ea0943e0349c8 100644 (file)
@@ -16,7 +16,7 @@ func parseLiteralIP(addr string) string {
        var zone string
        ip = parseIPv4(addr)
        if ip == nil {
-               ip, zone = parseIPv6(addr, true)
+               ip, zone = parseIPv6Zone(addr)
        }
        if ip == nil {
                return ""
index d5b405179448be30ab29503ea44c8f2a9d37a1bf..da8dca588e5c80209a080f7ef750ec1050d7940b 100644 (file)
@@ -562,25 +562,26 @@ func parseIPv4(s string) IP {
        return IPv4(p[0], p[1], p[2], p[3])
 }
 
-// parseIPv6 parses s as a literal IPv6 address described in RFC 4291
-// and RFC 5952.  It can also parse a literal scoped IPv6 address with
-// zone identifier which is described in RFC 4007 when zoneAllowed is
-// true.
-func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
+// parseIPv6Zone parses s as a literal IPv6 address and its associated zone
+// identifier which is described in RFC 4007.
+func parseIPv6Zone(s string) (IP, string) {
+       s, zone := splitHostZone(s)
+       return parseIPv6(s), zone
+}
+
+// parseIPv6Zone parses s as a literal IPv6 address described in RFC 4291
+// and RFC 5952.
+func parseIPv6(s string) (ip IP) {
        ip = make(IP, IPv6len)
        ellipsis := -1 // position of ellipsis in ip
 
-       if zoneAllowed {
-               s, zone = splitHostZone(s)
-       }
-
        // Might have leading ellipsis
        if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
                ellipsis = 0
                s = s[2:]
                // Might be only ellipsis
                if len(s) == 0 {
-                       return ip, zone
+                       return ip
                }
        }
 
@@ -590,22 +591,22 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
                // Hex number.
                n, c, ok := xtoi(s)
                if !ok || n > 0xFFFF {
-                       return nil, zone
+                       return nil
                }
 
                // If followed by dot, might be in trailing IPv4.
                if c < len(s) && s[c] == '.' {
                        if ellipsis < 0 && i != IPv6len-IPv4len {
                                // Not the right place.
-                               return nil, zone
+                               return nil
                        }
                        if i+IPv4len > IPv6len {
                                // Not enough room.
-                               return nil, zone
+                               return nil
                        }
                        ip4 := parseIPv4(s)
                        if ip4 == nil {
-                               return nil, zone
+                               return nil
                        }
                        ip[i] = ip4[12]
                        ip[i+1] = ip4[13]
@@ -629,14 +630,14 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
 
                // Otherwise must be followed by colon and more.
                if s[0] != ':' || len(s) == 1 {
-                       return nil, zone
+                       return nil
                }
                s = s[1:]
 
                // Look for ellipsis.
                if s[0] == ':' {
                        if ellipsis >= 0 { // already have one
-                               return nil, zone
+                               return nil
                        }
                        ellipsis = i
                        s = s[1:]
@@ -648,13 +649,13 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
 
        // Must have used entire string.
        if len(s) != 0 {
-               return nil, zone
+               return nil
        }
 
        // If didn't parse enough, expand ellipsis.
        if i < IPv6len {
                if ellipsis < 0 {
-                       return nil, zone
+                       return nil
                }
                n := IPv6len - i
                for j := i - 1; j >= ellipsis; j-- {
@@ -665,9 +666,9 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
                }
        } else if ellipsis >= 0 {
                // Ellipsis must represent at least one 0 group.
-               return nil, zone
+               return nil
        }
-       return ip, zone
+       return ip
 }
 
 // ParseIP parses s as an IP address, returning the result.
@@ -681,13 +682,26 @@ func ParseIP(s string) IP {
                case '.':
                        return parseIPv4(s)
                case ':':
-                       ip, _ := parseIPv6(s, false)
-                       return ip
+                       return parseIPv6(s)
                }
        }
        return nil
 }
 
+// parseIPZone parses s as an IP address, return it and its associated zone
+// identifier (IPv6 only).
+func parseIPZone(s string) (IP, string) {
+       for i := 0; i < len(s); i++ {
+               switch s[i] {
+               case '.':
+                       return parseIPv4(s), ""
+               case ':':
+                       return parseIPv6Zone(s)
+               }
+       }
+       return nil, ""
+}
+
 // ParseCIDR parses s as a CIDR notation IP address and prefix length,
 // like "192.0.2.0/24" or "2001:db8::/32", as defined in
 // RFC 4632 and RFC 4291.
@@ -706,7 +720,7 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
        ip := parseIPv4(addr)
        if ip == nil {
                iplen = IPv6len
-               ip, _ = parseIPv6(addr, false)
+               ip = parseIPv6(addr)
        }
        n, i, ok := dtoi(mask)
        if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
index 947bdf34897e76e62ed65a2f0a4ad8d1212b1cbb..f4ff82bd750916748d0aae2889d7a24e2f013e7f 100644 (file)
@@ -276,24 +276,16 @@ func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addr
        }
 
        // Try as a literal IP address, then as a DNS name.
-       var ips []IPAddr
-       if ip := parseIPv4(host); ip != nil {
-               ips = []IPAddr{{IP: ip}}
-       } else if ip, zone := parseIPv6(host, true); ip != nil {
-               ips = []IPAddr{{IP: ip, Zone: zone}}
-               // Issue 18806: if the machine has halfway configured
-               // IPv6 such that it can bind on "::" (IPv6unspecified)
-               // but not connect back to that same address, fall
-               // back to dialing 0.0.0.0.
-               if ip.Equal(IPv6unspecified) {
-                       ips = append(ips, IPAddr{IP: IPv4zero})
-               }
-       } else {
-               // Try as a DNS name.
-               ips, err = r.LookupIPAddr(ctx, host)
-               if err != nil {
-                       return nil, err
-               }
+       ips, err := r.LookupIPAddr(ctx, host)
+       if err != nil {
+               return nil, err
+       }
+       // Issue 18806: if the machine has halfway configured
+       // IPv6 such that it can bind on "::" (IPv6unspecified)
+       // but not connect back to that same address, fall
+       // back to dialing 0.0.0.0.
+       if len(ips) == 1 && ips[0].IP.Equal(IPv6unspecified) {
+               ips = append(ips, IPAddr{IP: IPv4zero})
        }
 
        var filter func(IPAddr) bool
index 1a9b4a9f085b4526ea9c7940c9a7802a40949178..e0f21fa9a8d3fd580e6da1d37e16dd75c31b5a97 100644 (file)
@@ -162,11 +162,11 @@ func LookupHost(host string) (addrs []string, err error) {
 // It returns a slice of that host's addresses.
 func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
        // Make sure that no matter what we do later, host=="" is rejected.
-       // ParseIP, for example, does accept empty strings.
+       // parseIP, for example, does accept empty strings.
        if host == "" {
                return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
        }
-       if ip := ParseIP(host); ip != nil {
+       if ip, _ := parseIPZone(host); ip != nil {
                return []string{host}, nil
        }
        return r.lookupHost(ctx, host)
@@ -190,12 +190,12 @@ func LookupIP(host string) ([]IP, error) {
 // It returns a slice of that host's IPv4 and IPv6 addresses.
 func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
        // Make sure that no matter what we do later, host=="" is rejected.
-       // ParseIP, for example, does accept empty strings.
+       // parseIP, for example, does accept empty strings.
        if host == "" {
                return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
        }
-       if ip := ParseIP(host); ip != nil {
-               return []IPAddr{{IP: ip}}, nil
+       if ip, zone := parseIPZone(host); ip != nil {
+               return []IPAddr{{IP: ip, Zone: zone}}, nil
        }
        trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
        if trace != nil && trace.DNSStart != nil {
index 010f71df2f8c65f360c36a99fb627c9e196655be..5c66dfa2603ad902c3b0a608277dd817a76d974f 100644 (file)
@@ -306,6 +306,32 @@ func TestLookupIPv6LinkLocalAddr(t *testing.T) {
        }
 }
 
+func TestLookupIPv6LinkLocalAddrWithZone(t *testing.T) {
+       if !supportsIPv6() || !*testIPv6 {
+               t.Skip("IPv6 is required")
+       }
+
+       ipaddrs, err := DefaultResolver.LookupIPAddr(context.Background(), "fe80::1%lo0")
+       if err != nil {
+               t.Error(err)
+       }
+       for _, addr := range ipaddrs {
+               if e, a := "lo0", addr.Zone; e != a {
+                       t.Errorf("wrong zone: want %q, got %q", e, a)
+               }
+       }
+
+       addrs, err := DefaultResolver.LookupHost(context.Background(), "fe80::1%lo0")
+       if err != nil {
+               t.Error(err)
+       }
+       for _, addr := range addrs {
+               if e, a := "fe80::1%lo0", addr; e != a {
+                       t.Errorf("wrong host: want %q got %q", e, a)
+               }
+       }
+}
+
 var lookupCNAMETests = []struct {
        name, cname string
 }{