Skip to content

Commit 8f923a4

Browse files
zx2c4bradfitz
authored andcommitted
net/netip: add missing encoding.BinaryUnmarshaler to AddrPort and Prefix
The Addr type got an encoding.BinaryUnmarshaler implementation, but not AddrPort and Prefix. This commit adds the missing implementation of that interface to these types. It also adds two round trip tests that follow the template of the existing one for Addr. Updates #49298. Change-Id: Iac633aed8aac579960815bb64d06ff3181214841 Reviewed-on: https://go-review.googlesource.com/c/go/+/360875 Trust: Jason A. Donenfeld <[email protected]> Run-TryBot: Jason A. Donenfeld <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent d3a80c7 commit 8f923a4

File tree

3 files changed

+150
-3
lines changed

3 files changed

+150
-3
lines changed

src/net/netip/leaf_alts.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,14 @@ func bePutUint32(b []byte, v uint32) {
4141
b[2] = byte(v >> 8)
4242
b[3] = byte(v)
4343
}
44+
45+
func leUint16(b []byte) uint16 {
46+
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
47+
return uint16(b[0]) | uint16(b[1])<<8
48+
}
49+
50+
func lePutUint16(b []byte, v uint16) {
51+
_ = b[1] // early bounds check to guarantee safety of writes below
52+
b[0] = byte(v)
53+
b[1] = byte(v >> 8)
54+
}

src/net/netip/netip.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,34 @@ func (p *AddrPort) UnmarshalText(text []byte) error {
11701170
return err
11711171
}
11721172

1173+
// MarshalBinary implements the encoding.BinaryMarshaler interface.
1174+
// It returns Addr.MarshalBinary with an additional two bytes appended
1175+
// containing the port in little-endian.
1176+
func (p AddrPort) MarshalBinary() ([]byte, error) {
1177+
b, err := p.Addr().MarshalBinary()
1178+
if err != nil {
1179+
return nil, err
1180+
}
1181+
b = append(b, 0, 0)
1182+
lePutUint16(b[len(b)-2:], p.Port())
1183+
return b, nil
1184+
}
1185+
1186+
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
1187+
// It expects data in the form generated by MarshalBinary.
1188+
func (p *AddrPort) UnmarshalBinary(b []byte) error {
1189+
if len(b) < 2 {
1190+
return errors.New("unexpected slice size")
1191+
}
1192+
var addr Addr
1193+
err := addr.UnmarshalBinary(b[:len(b)-2])
1194+
if err != nil {
1195+
return err
1196+
}
1197+
*p = AddrPortFrom(addr, leUint16(b[len(b)-2:]))
1198+
return nil
1199+
}
1200+
11731201
// Prefix is an IP address prefix (CIDR) representing an IP network.
11741202
//
11751203
// The first Bits() of Addr() are specified. The remaining bits match any address.
@@ -1401,6 +1429,32 @@ func (p *Prefix) UnmarshalText(text []byte) error {
14011429
return err
14021430
}
14031431

1432+
// MarshalBinary implements the encoding.BinaryMarshaler interface.
1433+
// It returns Addr.MarshalBinary with an additional byte appended
1434+
// containing the prefix bits.
1435+
func (p Prefix) MarshalBinary() ([]byte, error) {
1436+
b, err := p.Addr().MarshalBinary()
1437+
if err != nil {
1438+
return nil, err
1439+
}
1440+
return append(b, uint8(p.Bits())), nil
1441+
}
1442+
1443+
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
1444+
// It expects data in the form generated by MarshalBinary.
1445+
func (p *Prefix) UnmarshalBinary(b []byte) error {
1446+
if len(b) < 1 {
1447+
return errors.New("unexpected slice size")
1448+
}
1449+
var addr Addr
1450+
err := addr.UnmarshalBinary(b[:len(b)-1])
1451+
if err != nil {
1452+
return err
1453+
}
1454+
*p = PrefixFrom(addr, int(b[len(b)-1]))
1455+
return nil
1456+
}
1457+
14041458
// String returns the CIDR notation of p: "<ip>/<bits>".
14051459
func (p Prefix) String() string {
14061460
if !p.IsValid() {

src/net/netip/netip_test.go

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type uint128 = Uint128
2525
var (
2626
mustPrefix = MustParsePrefix
2727
mustIP = MustParseAddr
28+
mustIPPort = MustParseAddrPort
2829
)
2930

3031
func TestParseAddr(t *testing.T) {
@@ -332,10 +333,91 @@ func TestAddrMarshalUnmarshalBinary(t *testing.T) {
332333
}
333334

334335
// Cannot unmarshal from unexpected IP length.
335-
for _, l := range []int{3, 5} {
336+
for _, n := range []int{3, 5} {
336337
var ip2 Addr
337-
if err := ip2.UnmarshalBinary(bytes.Repeat([]byte{1}, l)); err == nil {
338-
t.Fatalf("unmarshaled from unexpected IP length %d", l)
338+
if err := ip2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
339+
t.Fatalf("unmarshaled from unexpected IP length %d", n)
340+
}
341+
}
342+
}
343+
344+
func TestAddrPortMarshalUnmarshalBinary(t *testing.T) {
345+
tests := []struct {
346+
ipport string
347+
wantSize int
348+
}{
349+
{"1.2.3.4:51820", 4 + 2},
350+
{"[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", 16 + 2},
351+
{"[::ffff:c000:0280]:65535", 16 + 2},
352+
{"[::ffff:c000:0280%eth0]:1", 20 + 2},
353+
}
354+
for _, tc := range tests {
355+
var ipport AddrPort
356+
if len(tc.ipport) > 0 {
357+
ipport = mustIPPort(tc.ipport)
358+
}
359+
b, err := ipport.MarshalBinary()
360+
if err != nil {
361+
t.Fatal(err)
362+
}
363+
if len(b) != tc.wantSize {
364+
t.Fatalf("%q encoded to size %d; want %d", tc.ipport, len(b), tc.wantSize)
365+
}
366+
var ipport2 AddrPort
367+
if err := ipport2.UnmarshalBinary(b); err != nil {
368+
t.Fatal(err)
369+
}
370+
if ipport != ipport2 {
371+
t.Fatalf("got %v; want %v", ipport2, ipport)
372+
}
373+
}
374+
375+
// Cannot unmarshal from unexpected lengths.
376+
for _, n := range []int{3, 7} {
377+
var ipport2 AddrPort
378+
if err := ipport2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
379+
t.Fatalf("unmarshaled from unexpected length %d", n)
380+
}
381+
}
382+
}
383+
384+
func TestPrefixMarshalUnmarshalBinary(t *testing.T) {
385+
type testCase struct {
386+
prefix Prefix
387+
wantSize int
388+
}
389+
tests := []testCase{
390+
{mustPrefix("1.2.3.4/24"), 4 + 1},
391+
{mustPrefix("fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118"), 16 + 1},
392+
{mustPrefix("::ffff:c000:0280/96"), 16 + 1},
393+
{mustPrefix("::ffff:c000:0280%eth0/37"), 16 + 1}, // Zone should be stripped
394+
}
395+
tests = append(tests,
396+
testCase{PrefixFrom(tests[0].prefix.Addr(), 33), tests[0].wantSize},
397+
testCase{PrefixFrom(tests[1].prefix.Addr(), 129), tests[1].wantSize})
398+
for _, tc := range tests {
399+
prefix := tc.prefix
400+
b, err := prefix.MarshalBinary()
401+
if err != nil {
402+
t.Fatal(err)
403+
}
404+
if len(b) != tc.wantSize {
405+
t.Fatalf("%q encoded to size %d; want %d", tc.prefix, len(b), tc.wantSize)
406+
}
407+
var prefix2 Prefix
408+
if err := prefix2.UnmarshalBinary(b); err != nil {
409+
t.Fatal(err)
410+
}
411+
if prefix != prefix2 {
412+
t.Fatalf("got %v; want %v", prefix2, prefix)
413+
}
414+
}
415+
416+
// Cannot unmarshal from unexpected lengths.
417+
for _, n := range []int{3, 6} {
418+
var prefix2 Prefix
419+
if err := prefix2.UnmarshalBinary(bytes.Repeat([]byte{1}, n)); err == nil {
420+
t.Fatalf("unmarshaled from unexpected length %d", n)
339421
}
340422
}
341423
}

0 commit comments

Comments
 (0)