1
0
mirror of https://github.com/golang/go synced 2024-11-23 12:40:11 -07:00

net: parse IPv6 address with zone using DefaultResolver.Lookup{Host,IPAddr}

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>
This commit is contained in:
Michael Fraenkel 2017-11-26 08:22:22 -05:00 committed by Brad Fitzpatrick
parent bafe466a95
commit d144dd785f
7 changed files with 81 additions and 49 deletions

View 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

View 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"))
}
}

View 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 ""

View 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 {

View 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

View 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 {

View 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
}{