Source file src/net/netip/fuzz_test.go

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package netip_test
     6  
     7  import (
     8  	"bytes"
     9  	"encoding"
    10  	"fmt"
    11  	"net"
    12  	. "net/netip"
    13  	"reflect"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  var corpus = []string{
    19  	// Basic zero IPv4 address.
    20  	"0.0.0.0",
    21  	// Basic non-zero IPv4 address.
    22  	"192.168.140.255",
    23  	// IPv4 address in windows-style "print all the digits" form.
    24  	"010.000.015.001",
    25  	// IPv4 address with a silly amount of leading zeros.
    26  	"000001.00000002.00000003.000000004",
    27  	// 4-in-6 with octet with leading zero
    28  	"::ffff:1.2.03.4",
    29  	// Basic zero IPv6 address.
    30  	"::",
    31  	// Localhost IPv6.
    32  	"::1",
    33  	// Fully expanded IPv6 address.
    34  	"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b",
    35  	// IPv6 with elided fields in the middle.
    36  	"fd7a:115c::626b:430b",
    37  	// IPv6 with elided fields at the end.
    38  	"fd7a:115c:a1e0:ab12:4843:cd96::",
    39  	// IPv6 with single elided field at the end.
    40  	"fd7a:115c:a1e0:ab12:4843:cd96:626b::",
    41  	"fd7a:115c:a1e0:ab12:4843:cd96:626b:0",
    42  	// IPv6 with single elided field in the middle.
    43  	"fd7a:115c:a1e0::4843:cd96:626b:430b",
    44  	"fd7a:115c:a1e0:0:4843:cd96:626b:430b",
    45  	// IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6)
    46  	"::ffff:192.168.140.255",
    47  	"::ffff:192.168.140.255",
    48  	// IPv6 with a zone specifier.
    49  	"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0",
    50  	// IPv6 with dotted decimal and zone specifier.
    51  	"1:2::ffff:192.168.140.255%eth1",
    52  	"1:2::ffff:c0a8:8cff%eth1",
    53  	// IPv6 with capital letters.
    54  	"FD9E:1A04:F01D::1",
    55  	"fd9e:1a04:f01d::1",
    56  	// Empty string.
    57  	"",
    58  	// Garbage non-IP.
    59  	"bad",
    60  	// Single number. Some parsers accept this as an IPv4 address in
    61  	// big-endian uint32 form, but we don't.
    62  	"1234",
    63  	// IPv4 with a zone specifier.
    64  	"1.2.3.4%eth0",
    65  	// IPv4 field must have at least one digit.
    66  	".1.2.3",
    67  	"1.2.3.",
    68  	"1..2.3",
    69  	// IPv4 address too long.
    70  	"1.2.3.4.5",
    71  	// IPv4 in dotted octal form.
    72  	"0300.0250.0214.0377",
    73  	// IPv4 in dotted hex form.
    74  	"0xc0.0xa8.0x8c.0xff",
    75  	// IPv4 in class B form.
    76  	"192.168.12345",
    77  	// IPv4 in class B form, with a small enough number to be
    78  	// parseable as a regular dotted decimal field.
    79  	"127.0.1",
    80  	// IPv4 in class A form.
    81  	"192.1234567",
    82  	// IPv4 in class A form, with a small enough number to be
    83  	// parseable as a regular dotted decimal field.
    84  	"127.1",
    85  	// IPv4 field has value >255.
    86  	"192.168.300.1",
    87  	// IPv4 with too many fields.
    88  	"192.168.0.1.5.6",
    89  	// IPv6 with not enough fields.
    90  	"1:2:3:4:5:6:7",
    91  	// IPv6 with too many fields.
    92  	"1:2:3:4:5:6:7:8:9",
    93  	// IPv6 with 8 fields and a :: expander.
    94  	"1:2:3:4::5:6:7:8",
    95  	// IPv6 with a field bigger than 2b.
    96  	"fe801::1",
    97  	// IPv6 with non-hex values in field.
    98  	"fe80:tail:scal:e::",
    99  	// IPv6 with a zone delimiter but no zone.
   100  	"fe80::1%",
   101  	// IPv6 with a zone specifier of zero.
   102  	"::ffff:0:0%0",
   103  	// IPv6 (without ellipsis) with too many fields for trailing embedded IPv4.
   104  	"ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
   105  	// IPv6 (with ellipsis) with too many fields for trailing embedded IPv4.
   106  	"ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255",
   107  	// IPv6 with invalid embedded IPv4.
   108  	"::ffff:192.168.140.bad",
   109  	// IPv6 with multiple ellipsis ::.
   110  	"fe80::1::1",
   111  	// IPv6 with invalid non hex/colon character.
   112  	"fe80:1?:1",
   113  	// IPv6 with truncated bytes after single colon.
   114  	"fe80:",
   115  	// AddrPort strings.
   116  	"1.2.3.4:51820",
   117  	"[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80",
   118  	"[::ffff:c000:0280]:65535",
   119  	"[::ffff:c000:0280%eth0]:1",
   120  	// Prefix strings.
   121  	"1.2.3.4/24",
   122  	"fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118",
   123  	"::ffff:c000:0280/96",
   124  	"::ffff:c000:0280%eth0/37",
   125  }
   126  
   127  func FuzzParse(f *testing.F) {
   128  	for _, seed := range corpus {
   129  		f.Add(seed)
   130  	}
   131  
   132  	f.Fuzz(func(t *testing.T, s string) {
   133  		ip, _ := ParseAddr(s)
   134  		checkStringParseRoundTrip(t, ip, ParseAddr)
   135  		checkEncoding(t, ip)
   136  
   137  		// Check that we match the net's IP parser, modulo zones.
   138  		if !strings.Contains(s, "%") {
   139  			stdip := net.ParseIP(s)
   140  			if !ip.IsValid() != (stdip == nil) {
   141  				t.Errorf("ParseAddr zero != net.ParseIP nil: ip=%q stdip=%q", ip, stdip)
   142  			}
   143  
   144  			if ip.IsValid() && !ip.Is4In6() {
   145  				buf, err := ip.MarshalText()
   146  				if err != nil {
   147  					t.Fatal(err)
   148  				}
   149  				buf2, err := stdip.MarshalText()
   150  				if err != nil {
   151  					t.Fatal(err)
   152  				}
   153  				if !bytes.Equal(buf, buf2) {
   154  					t.Errorf("Addr.MarshalText() != net.IP.MarshalText(): ip=%q stdip=%q", ip, stdip)
   155  				}
   156  				if ip.String() != stdip.String() {
   157  					t.Errorf("Addr.String() != net.IP.String(): ip=%q stdip=%q", ip, stdip)
   158  				}
   159  				if ip.IsGlobalUnicast() != stdip.IsGlobalUnicast() {
   160  					t.Errorf("Addr.IsGlobalUnicast() != net.IP.IsGlobalUnicast(): ip=%q stdip=%q", ip, stdip)
   161  				}
   162  				if ip.IsInterfaceLocalMulticast() != stdip.IsInterfaceLocalMulticast() {
   163  					t.Errorf("Addr.IsInterfaceLocalMulticast() != net.IP.IsInterfaceLocalMulticast(): ip=%q stdip=%q", ip, stdip)
   164  				}
   165  				if ip.IsLinkLocalMulticast() != stdip.IsLinkLocalMulticast() {
   166  					t.Errorf("Addr.IsLinkLocalMulticast() != net.IP.IsLinkLocalMulticast(): ip=%q stdip=%q", ip, stdip)
   167  				}
   168  				if ip.IsLinkLocalUnicast() != stdip.IsLinkLocalUnicast() {
   169  					t.Errorf("Addr.IsLinkLocalUnicast() != net.IP.IsLinkLocalUnicast(): ip=%q stdip=%q", ip, stdip)
   170  				}
   171  				if ip.IsLoopback() != stdip.IsLoopback() {
   172  					t.Errorf("Addr.IsLoopback() != net.IP.IsLoopback(): ip=%q stdip=%q", ip, stdip)
   173  				}
   174  				if ip.IsMulticast() != stdip.IsMulticast() {
   175  					t.Errorf("Addr.IsMulticast() != net.IP.IsMulticast(): ip=%q stdip=%q", ip, stdip)
   176  				}
   177  				if ip.IsPrivate() != stdip.IsPrivate() {
   178  					t.Errorf("Addr.IsPrivate() != net.IP.IsPrivate(): ip=%q stdip=%q", ip, stdip)
   179  				}
   180  				if ip.IsUnspecified() != stdip.IsUnspecified() {
   181  					t.Errorf("Addr.IsUnspecified() != net.IP.IsUnspecified(): ip=%q stdip=%q", ip, stdip)
   182  				}
   183  			}
   184  		}
   185  
   186  		// Check that .Next().Prev() and .Prev().Next() preserve the IP.
   187  		if ip.IsValid() && ip.Next().IsValid() && ip.Next().Prev() != ip {
   188  			t.Errorf(".Next.Prev did not round trip: ip=%q .next=%q .next.prev=%q", ip, ip.Next(), ip.Next().Prev())
   189  		}
   190  		if ip.IsValid() && ip.Prev().IsValid() && ip.Prev().Next() != ip {
   191  			t.Errorf(".Prev.Next did not round trip: ip=%q .prev=%q .prev.next=%q", ip, ip.Prev(), ip.Prev().Next())
   192  		}
   193  
   194  		port, err := ParseAddrPort(s)
   195  		if err == nil {
   196  			checkStringParseRoundTrip(t, port, ParseAddrPort)
   197  			checkEncoding(t, port)
   198  		}
   199  		port = AddrPortFrom(ip, 80)
   200  		checkStringParseRoundTrip(t, port, ParseAddrPort)
   201  		checkEncoding(t, port)
   202  
   203  		ipp, err := ParsePrefix(s)
   204  		if err == nil {
   205  			checkStringParseRoundTrip(t, ipp, ParsePrefix)
   206  			checkEncoding(t, ipp)
   207  		}
   208  		ipp = PrefixFrom(ip, 8)
   209  		checkStringParseRoundTrip(t, ipp, ParsePrefix)
   210  		checkEncoding(t, ipp)
   211  	})
   212  }
   213  
   214  // checkTextMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly.
   215  func checkTextMarshaler(t *testing.T, x encoding.TextMarshaler) {
   216  	buf, err := x.MarshalText()
   217  	if err != nil {
   218  		t.Fatal(err)
   219  	}
   220  	y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler)
   221  	err = y.UnmarshalText(buf)
   222  	if err != nil {
   223  		t.Logf("(%v).MarshalText() = %q", x, buf)
   224  		t.Fatalf("(%T).UnmarshalText(%q) = %v", y, buf, err)
   225  	}
   226  	e := reflect.ValueOf(y).Elem().Interface()
   227  	if !reflect.DeepEqual(x, e) {
   228  		t.Logf("(%v).MarshalText() = %q", x, buf)
   229  		t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y)
   230  		t.Fatalf("MarshalText/UnmarshalText failed to round trip: %#v != %#v", x, e)
   231  	}
   232  	buf2, err := y.(encoding.TextMarshaler).MarshalText()
   233  	if err != nil {
   234  		t.Logf("(%v).MarshalText() = %q", x, buf)
   235  		t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y)
   236  		t.Fatalf("failed to MarshalText a second time: %v", err)
   237  	}
   238  	if !bytes.Equal(buf, buf2) {
   239  		t.Logf("(%v).MarshalText() = %q", x, buf)
   240  		t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y)
   241  		t.Logf("(%v).MarshalText() = %q", y, buf2)
   242  		t.Fatalf("second MarshalText differs from first: %q != %q", buf, buf2)
   243  	}
   244  }
   245  
   246  // checkBinaryMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly.
   247  func checkBinaryMarshaler(t *testing.T, x encoding.BinaryMarshaler) {
   248  	buf, err := x.MarshalBinary()
   249  	if err != nil {
   250  		t.Fatal(err)
   251  	}
   252  	y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler)
   253  	err = y.UnmarshalBinary(buf)
   254  	if err != nil {
   255  		t.Logf("(%v).MarshalBinary() = %q", x, buf)
   256  		t.Fatalf("(%T).UnmarshalBinary(%q) = %v", y, buf, err)
   257  	}
   258  	e := reflect.ValueOf(y).Elem().Interface()
   259  	if !reflect.DeepEqual(x, e) {
   260  		t.Logf("(%v).MarshalBinary() = %q", x, buf)
   261  		t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
   262  		t.Fatalf("MarshalBinary/UnmarshalBinary failed to round trip: %#v != %#v", x, e)
   263  	}
   264  	buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary()
   265  	if err != nil {
   266  		t.Logf("(%v).MarshalBinary() = %q", x, buf)
   267  		t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
   268  		t.Fatalf("failed to MarshalBinary a second time: %v", err)
   269  	}
   270  	if !bytes.Equal(buf, buf2) {
   271  		t.Logf("(%v).MarshalBinary() = %q", x, buf)
   272  		t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
   273  		t.Logf("(%v).MarshalBinary() = %q", y, buf2)
   274  		t.Fatalf("second MarshalBinary differs from first: %q != %q", buf, buf2)
   275  	}
   276  }
   277  
   278  func checkTextMarshalMatchesString(t *testing.T, x netipType) {
   279  	buf, err := x.MarshalText()
   280  	if err != nil {
   281  		t.Fatal(err)
   282  	}
   283  	str := x.String()
   284  	if string(buf) != str {
   285  		t.Fatalf("%v: MarshalText = %q, String = %q", x, buf, str)
   286  	}
   287  }
   288  
   289  type appendMarshaler interface {
   290  	encoding.TextMarshaler
   291  	AppendTo([]byte) []byte
   292  }
   293  
   294  // checkTextMarshalMatchesAppendTo checks that x's MarshalText matches x's AppendTo.
   295  func checkTextMarshalMatchesAppendTo(t *testing.T, x appendMarshaler) {
   296  	buf, err := x.MarshalText()
   297  	if err != nil {
   298  		t.Fatal(err)
   299  	}
   300  
   301  	buf2 := make([]byte, 0, len(buf))
   302  	buf2 = x.AppendTo(buf2)
   303  	if !bytes.Equal(buf, buf2) {
   304  		t.Fatalf("%v: MarshalText = %q, AppendTo = %q", x, buf, buf2)
   305  	}
   306  }
   307  
   308  type netipType interface {
   309  	encoding.BinaryMarshaler
   310  	encoding.TextMarshaler
   311  	fmt.Stringer
   312  	IsValid() bool
   313  }
   314  
   315  type netipTypeCmp interface {
   316  	comparable
   317  	netipType
   318  }
   319  
   320  // checkStringParseRoundTrip checks that x's String method and the provided parse function can round trip correctly.
   321  func checkStringParseRoundTrip[P netipTypeCmp](t *testing.T, x P, parse func(string) (P, error)) {
   322  	if !x.IsValid() {
   323  		// Ignore invalid values.
   324  		return
   325  	}
   326  
   327  	s := x.String()
   328  	y, err := parse(s)
   329  	if err != nil {
   330  		t.Fatalf("s=%q err=%v", s, err)
   331  	}
   332  	if x != y {
   333  		t.Fatalf("%T round trip identity failure: s=%q x=%#v y=%#v", x, s, x, y)
   334  	}
   335  	s2 := y.String()
   336  	if s != s2 {
   337  		t.Fatalf("%T String round trip identity failure: s=%#v s2=%#v", x, s, s2)
   338  	}
   339  }
   340  
   341  func checkEncoding(t *testing.T, x netipType) {
   342  	if x.IsValid() {
   343  		checkTextMarshaler(t, x)
   344  		checkBinaryMarshaler(t, x)
   345  		checkTextMarshalMatchesString(t, x)
   346  	}
   347  
   348  	if am, ok := x.(appendMarshaler); ok {
   349  		checkTextMarshalMatchesAppendTo(t, am)
   350  	}
   351  }
   352  

View as plain text