Skip to content

Commit d144dd7

Browse files
fraenkelbradfitz
authored andcommitted
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent bafe466 commit d144dd7

File tree

7 files changed

+81
-49
lines changed

7 files changed

+81
-49
lines changed

src/net/cgo_unix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error,
249249
var zone string
250250
ip := parseIPv4(addr)
251251
if ip == nil {
252-
ip, zone = parseIPv6(addr, true)
252+
ip, zone = parseIPv6Zone(addr)
253253
}
254254
if ip == nil {
255255
return nil, &DNSError{Err: "invalid address", Name: addr}, true

src/net/dnsconfig_unix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func dnsReadConfig(filename string) *dnsConfig {
7373
// to look it up.
7474
if parseIPv4(f[1]) != nil {
7575
conf.servers = append(conf.servers, JoinHostPort(f[1], "53"))
76-
} else if ip, _ := parseIPv6(f[1], true); ip != nil {
76+
} else if ip, _ := parseIPv6Zone(f[1]); ip != nil {
7777
conf.servers = append(conf.servers, JoinHostPort(f[1], "53"))
7878
}
7979
}

src/net/hosts.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func parseLiteralIP(addr string) string {
1616
var zone string
1717
ip = parseIPv4(addr)
1818
if ip == nil {
19-
ip, zone = parseIPv6(addr, true)
19+
ip, zone = parseIPv6Zone(addr)
2020
}
2121
if ip == nil {
2222
return ""

src/net/ip.go

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -562,25 +562,26 @@ func parseIPv4(s string) IP {
562562
return IPv4(p[0], p[1], p[2], p[3])
563563
}
564564

565-
// parseIPv6 parses s as a literal IPv6 address described in RFC 4291
566-
// and RFC 5952. It can also parse a literal scoped IPv6 address with
567-
// zone identifier which is described in RFC 4007 when zoneAllowed is
568-
// true.
569-
func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
565+
// parseIPv6Zone parses s as a literal IPv6 address and its associated zone
566+
// identifier which is described in RFC 4007.
567+
func parseIPv6Zone(s string) (IP, string) {
568+
s, zone := splitHostZone(s)
569+
return parseIPv6(s), zone
570+
}
571+
572+
// parseIPv6Zone parses s as a literal IPv6 address described in RFC 4291
573+
// and RFC 5952.
574+
func parseIPv6(s string) (ip IP) {
570575
ip = make(IP, IPv6len)
571576
ellipsis := -1 // position of ellipsis in ip
572577

573-
if zoneAllowed {
574-
s, zone = splitHostZone(s)
575-
}
576-
577578
// Might have leading ellipsis
578579
if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
579580
ellipsis = 0
580581
s = s[2:]
581582
// Might be only ellipsis
582583
if len(s) == 0 {
583-
return ip, zone
584+
return ip
584585
}
585586
}
586587

@@ -590,22 +591,22 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
590591
// Hex number.
591592
n, c, ok := xtoi(s)
592593
if !ok || n > 0xFFFF {
593-
return nil, zone
594+
return nil
594595
}
595596

596597
// If followed by dot, might be in trailing IPv4.
597598
if c < len(s) && s[c] == '.' {
598599
if ellipsis < 0 && i != IPv6len-IPv4len {
599600
// Not the right place.
600-
return nil, zone
601+
return nil
601602
}
602603
if i+IPv4len > IPv6len {
603604
// Not enough room.
604-
return nil, zone
605+
return nil
605606
}
606607
ip4 := parseIPv4(s)
607608
if ip4 == nil {
608-
return nil, zone
609+
return nil
609610
}
610611
ip[i] = ip4[12]
611612
ip[i+1] = ip4[13]
@@ -629,14 +630,14 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
629630

630631
// Otherwise must be followed by colon and more.
631632
if s[0] != ':' || len(s) == 1 {
632-
return nil, zone
633+
return nil
633634
}
634635
s = s[1:]
635636

636637
// Look for ellipsis.
637638
if s[0] == ':' {
638639
if ellipsis >= 0 { // already have one
639-
return nil, zone
640+
return nil
640641
}
641642
ellipsis = i
642643
s = s[1:]
@@ -648,13 +649,13 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
648649

649650
// Must have used entire string.
650651
if len(s) != 0 {
651-
return nil, zone
652+
return nil
652653
}
653654

654655
// If didn't parse enough, expand ellipsis.
655656
if i < IPv6len {
656657
if ellipsis < 0 {
657-
return nil, zone
658+
return nil
658659
}
659660
n := IPv6len - i
660661
for j := i - 1; j >= ellipsis; j-- {
@@ -665,9 +666,9 @@ func parseIPv6(s string, zoneAllowed bool) (ip IP, zone string) {
665666
}
666667
} else if ellipsis >= 0 {
667668
// Ellipsis must represent at least one 0 group.
668-
return nil, zone
669+
return nil
669670
}
670-
return ip, zone
671+
return ip
671672
}
672673

673674
// ParseIP parses s as an IP address, returning the result.
@@ -681,13 +682,26 @@ func ParseIP(s string) IP {
681682
case '.':
682683
return parseIPv4(s)
683684
case ':':
684-
ip, _ := parseIPv6(s, false)
685-
return ip
685+
return parseIPv6(s)
686686
}
687687
}
688688
return nil
689689
}
690690

691+
// parseIPZone parses s as an IP address, return it and its associated zone
692+
// identifier (IPv6 only).
693+
func parseIPZone(s string) (IP, string) {
694+
for i := 0; i < len(s); i++ {
695+
switch s[i] {
696+
case '.':
697+
return parseIPv4(s), ""
698+
case ':':
699+
return parseIPv6Zone(s)
700+
}
701+
}
702+
return nil, ""
703+
}
704+
691705
// ParseCIDR parses s as a CIDR notation IP address and prefix length,
692706
// like "192.0.2.0/24" or "2001:db8::/32", as defined in
693707
// RFC 4632 and RFC 4291.
@@ -706,7 +720,7 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
706720
ip := parseIPv4(addr)
707721
if ip == nil {
708722
iplen = IPv6len
709-
ip, _ = parseIPv6(addr, false)
723+
ip = parseIPv6(addr)
710724
}
711725
n, i, ok := dtoi(mask)
712726
if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {

src/net/ipsock.go

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -276,24 +276,16 @@ func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addr
276276
}
277277

278278
// Try as a literal IP address, then as a DNS name.
279-
var ips []IPAddr
280-
if ip := parseIPv4(host); ip != nil {
281-
ips = []IPAddr{{IP: ip}}
282-
} else if ip, zone := parseIPv6(host, true); ip != nil {
283-
ips = []IPAddr{{IP: ip, Zone: zone}}
284-
// Issue 18806: if the machine has halfway configured
285-
// IPv6 such that it can bind on "::" (IPv6unspecified)
286-
// but not connect back to that same address, fall
287-
// back to dialing 0.0.0.0.
288-
if ip.Equal(IPv6unspecified) {
289-
ips = append(ips, IPAddr{IP: IPv4zero})
290-
}
291-
} else {
292-
// Try as a DNS name.
293-
ips, err = r.LookupIPAddr(ctx, host)
294-
if err != nil {
295-
return nil, err
296-
}
279+
ips, err := r.LookupIPAddr(ctx, host)
280+
if err != nil {
281+
return nil, err
282+
}
283+
// Issue 18806: if the machine has halfway configured
284+
// IPv6 such that it can bind on "::" (IPv6unspecified)
285+
// but not connect back to that same address, fall
286+
// back to dialing 0.0.0.0.
287+
if len(ips) == 1 && ips[0].IP.Equal(IPv6unspecified) {
288+
ips = append(ips, IPAddr{IP: IPv4zero})
297289
}
298290

299291
var filter func(IPAddr) bool

src/net/lookup.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,11 @@ func LookupHost(host string) (addrs []string, err error) {
162162
// It returns a slice of that host's addresses.
163163
func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
164164
// Make sure that no matter what we do later, host=="" is rejected.
165-
// ParseIP, for example, does accept empty strings.
165+
// parseIP, for example, does accept empty strings.
166166
if host == "" {
167167
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
168168
}
169-
if ip := ParseIP(host); ip != nil {
169+
if ip, _ := parseIPZone(host); ip != nil {
170170
return []string{host}, nil
171171
}
172172
return r.lookupHost(ctx, host)
@@ -190,12 +190,12 @@ func LookupIP(host string) ([]IP, error) {
190190
// It returns a slice of that host's IPv4 and IPv6 addresses.
191191
func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
192192
// Make sure that no matter what we do later, host=="" is rejected.
193-
// ParseIP, for example, does accept empty strings.
193+
// parseIP, for example, does accept empty strings.
194194
if host == "" {
195195
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
196196
}
197-
if ip := ParseIP(host); ip != nil {
198-
return []IPAddr{{IP: ip}}, nil
197+
if ip, zone := parseIPZone(host); ip != nil {
198+
return []IPAddr{{IP: ip, Zone: zone}}, nil
199199
}
200200
trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
201201
if trace != nil && trace.DNSStart != nil {

src/net/lookup_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,32 @@ func TestLookupIPv6LinkLocalAddr(t *testing.T) {
306306
}
307307
}
308308

309+
func TestLookupIPv6LinkLocalAddrWithZone(t *testing.T) {
310+
if !supportsIPv6() || !*testIPv6 {
311+
t.Skip("IPv6 is required")
312+
}
313+
314+
ipaddrs, err := DefaultResolver.LookupIPAddr(context.Background(), "fe80::1%lo0")
315+
if err != nil {
316+
t.Error(err)
317+
}
318+
for _, addr := range ipaddrs {
319+
if e, a := "lo0", addr.Zone; e != a {
320+
t.Errorf("wrong zone: want %q, got %q", e, a)
321+
}
322+
}
323+
324+
addrs, err := DefaultResolver.LookupHost(context.Background(), "fe80::1%lo0")
325+
if err != nil {
326+
t.Error(err)
327+
}
328+
for _, addr := range addrs {
329+
if e, a := "fe80::1%lo0", addr; e != a {
330+
t.Errorf("wrong host: want %q got %q", e, a)
331+
}
332+
}
333+
}
334+
309335
var lookupCNAMETests = []struct {
310336
name, cname string
311337
}{

0 commit comments

Comments
 (0)