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
// 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"))
}
}
var zone string
ip = parseIPv4(addr)
if ip == nil {
- ip, zone = parseIPv6(addr, true)
+ ip, zone = parseIPv6Zone(addr)
}
if ip == nil {
return ""
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
}
}
// 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]
// 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:]
// 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-- {
}
} 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.
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.
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 {
}
// 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
// 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)
// 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 {
}
}
+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
}{