Skip to content

Commit ae9ce82

Browse files
committed
net/netip: return an error from ParsePrefix with IPv6 zone input
net.ParseCIDR already rejects input in the form of 2001:db8::%a/32, but netip.ParsePrefix previously accepted the input and silently dropped the zone. Make the two consistent by always returning an error if an IPv6 zone is present in CIDR input for ParsePrefix. Fixes #51899. Change-Id: Iee7d8d4a5161e0b54a4ee1bd68b02c1a287ff399 Reviewed-on: https://go-review.googlesource.com/c/go/+/396299 Trust: Matt Layher <mdlayher@gmail.com> Run-TryBot: Matt Layher <mdlayher@gmail.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Trust: Damien Neil <dneil@google.com>
1 parent 9b90838 commit ae9ce82

File tree

3 files changed

+25
-37
lines changed

3 files changed

+25
-37
lines changed

src/net/netip/netip.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,8 @@ func (p Prefix) IsSingleIP() bool { return p.bits != 0 && int(p.bits) == p.ip.Bi
12901290
// ParsePrefix parses s as an IP address prefix.
12911291
// The string can be in the form "192.168.1.0/24" or "2001:db8::/32",
12921292
// the CIDR notation defined in RFC 4632 and RFC 4291.
1293+
// IPv6 zones are not permitted in prefixes, and an error will be returned if a
1294+
// zone is present.
12931295
//
12941296
// Note that masked address bits are not zeroed. Use Masked for that.
12951297
func ParsePrefix(s string) (Prefix, error) {
@@ -1301,6 +1303,11 @@ func ParsePrefix(s string) (Prefix, error) {
13011303
if err != nil {
13021304
return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): " + err.Error())
13031305
}
1306+
// IPv6 zones are not allowed: https://go.dev/issue/51899
1307+
if ip.Is6() && ip.z != z6noz {
1308+
return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote(s) + "): IPv6 zones cannot be present in a prefix")
1309+
}
1310+
13041311
bitsStr := s[i+1:]
13051312
bits, err := strconv.Atoi(bitsStr)
13061313
if err != nil {

src/net/netip/netip_pkg_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ func TestPrefixContains(t *testing.T) {
160160
{mustPrefix("::1/127"), mustIP("::2"), false},
161161
{mustPrefix("::1/128"), mustIP("::1"), true},
162162
{mustPrefix("::1/127"), mustIP("::2"), false},
163-
// zones support
164-
{mustPrefix("::1%a/128"), mustIP("::1"), true}, // prefix zones are stripped...
165-
{mustPrefix("::1%a/128"), mustIP("::1%a"), false}, // but ip zones are not
163+
// Zones ignored: https://go.dev/issue/51899
164+
{Prefix{mustIP("1.2.3.4").WithZone("a"), 32}, mustIP("1.2.3.4"), true},
165+
{Prefix{mustIP("::1").WithZone("a"), 128}, mustIP("::1"), true},
166166
// invalid IP
167167
{mustPrefix("::1/0"), Addr{}, false},
168168
{mustPrefix("1.2.3.4/0"), Addr{}, false},

src/net/netip/netip_test.go

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,8 @@ func TestPrefixMarshalTextString(t *testing.T) {
421421
{mustPrefix("1.2.3.4/24"), "1.2.3.4/24"},
422422
{mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"},
423423
{mustPrefix("::ffff:c000:0280/96"), "::ffff:192.0.2.128/96"},
424-
{mustPrefix("::ffff:c000:0280%eth0/37"), "::ffff:192.0.2.128/37"}, // Zone should be stripped
425424
{mustPrefix("::ffff:192.168.140.255/8"), "::ffff:192.168.140.255/8"},
425+
{PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), "::ffff:192.0.2.128/37"}, // Zone should be stripped
426426
}
427427
for i, tt := range tests {
428428
if got := tt.in.String(); got != tt.want {
@@ -448,7 +448,7 @@ func TestPrefixMarshalUnmarshalBinary(t *testing.T) {
448448
{mustPrefix("1.2.3.4/24"), 4 + 1},
449449
{mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1},
450450
{mustPrefix("::ffff:c000:0280/96"), 16 + 1},
451-
{mustPrefix("::ffff:c000:0280%eth0/37"), 16 + 1}, // Zone should be stripped
451+
{PrefixFrom(mustIP("::ffff:c000:0280").WithZone("eth0"), 37), 16 + 1}, // Zone should be stripped
452452
}
453453
tests = append(tests,
454454
testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize},
@@ -901,25 +901,25 @@ func TestPrefixMasking(t *testing.T) {
901901
{
902902
ip: mustIP(fmt.Sprintf("2001:db8::1%s", zone)),
903903
bits: 32,
904-
p: mustPrefix(fmt.Sprintf("2001:db8::%s/32", zone)),
904+
p: mustPrefix("2001:db8::/32"),
905905
ok: true,
906906
},
907907
{
908908
ip: mustIP(fmt.Sprintf("fe80::dead:beef:dead:beef%s", zone)),
909909
bits: 96,
910-
p: mustPrefix(fmt.Sprintf("fe80::dead:beef:0:0%s/96", zone)),
910+
p: mustPrefix("fe80::dead:beef:0:0/96"),
911911
ok: true,
912912
},
913913
{
914914
ip: mustIP(fmt.Sprintf("aaaa::%s", zone)),
915915
bits: 4,
916-
p: mustPrefix(fmt.Sprintf("a000::%s/4", zone)),
916+
p: mustPrefix("a000::/4"),
917917
ok: true,
918918
},
919919
{
920920
ip: mustIP(fmt.Sprintf("::%s", zone)),
921921
bits: 63,
922-
p: mustPrefix(fmt.Sprintf("::%s/63", zone)),
922+
p: mustPrefix("::/63"),
923923
ok: true,
924924
},
925925
}
@@ -1047,26 +1047,6 @@ func TestPrefixMarshalUnmarshal(t *testing.T) {
10471047
}
10481048
}
10491049

1050-
func TestPrefixMarshalUnmarshalZone(t *testing.T) {
1051-
orig := `"fe80::1cc0:3e8c:119f:c2e1%ens18/128"`
1052-
unzoned := `"fe80::1cc0:3e8c:119f:c2e1/128"`
1053-
1054-
var p Prefix
1055-
if err := json.Unmarshal([]byte(orig), &p); err != nil {
1056-
t.Fatalf("failed to unmarshal: %v", err)
1057-
}
1058-
1059-
pb, err := json.Marshal(p)
1060-
if err != nil {
1061-
t.Fatalf("failed to marshal: %v", err)
1062-
}
1063-
1064-
back := string(pb)
1065-
if back != unzoned {
1066-
t.Errorf("Marshal = %q; want %q", back, unzoned)
1067-
}
1068-
}
1069-
10701050
func TestPrefixUnmarshalTextNonZero(t *testing.T) {
10711051
ip := mustPrefix("fe80::/64")
10721052
if err := ip.UnmarshalText([]byte("xxx")); err == nil {
@@ -1222,14 +1202,6 @@ func TestPrefix(t *testing.T) {
12221202
contains: mustIPs("2001:db8::1"),
12231203
notContains: mustIPs("fe80::1"),
12241204
},
1225-
{
1226-
prefix: "::%0/00/80",
1227-
ip: mustIP("::"),
1228-
bits: 80,
1229-
str: "::/80",
1230-
contains: mustIPs("::"),
1231-
notContains: mustIPs("ff::%0/00", "ff::%1/23", "::%0/00", "::%1/23"),
1232-
},
12331205
}
12341206
for _, test := range tests {
12351207
t.Run(test.prefix, func(t *testing.T) {
@@ -1348,6 +1320,15 @@ func TestParsePrefixError(t *testing.T) {
13481320
prefix: "2001::/129",
13491321
errstr: "out of range",
13501322
},
1323+
// Zones are not allowed: https://go.dev/issue/51899
1324+
{
1325+
prefix: "1.1.1.0%a/24",
1326+
errstr: "unexpected character",
1327+
},
1328+
{
1329+
prefix: "2001:db8::%a/32",
1330+
errstr: "zones cannot be present",
1331+
},
13511332
}
13521333
for _, test := range tests {
13531334
t.Run(test.prefix, func(t *testing.T) {

0 commit comments

Comments
 (0)