| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // Copyright 2020 The Inet.Af AUTHORS. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- //go:build gofuzz
- // +build gofuzz
- package netaddr
- import (
- "bytes"
- "encoding"
- "fmt"
- "net"
- "reflect"
- "strings"
- )
- func Fuzz(b []byte) int {
- s := string(b)
- ip, _ := ParseIP(s)
- checkStringParseRoundTrip(ip, parseIP)
- checkEncoding(ip)
- // Check that we match the standard library's IP parser, modulo zones.
- if !strings.Contains(s, "%") {
- stdip := net.ParseIP(s)
- if ip.IsZero() != (stdip == nil) {
- fmt.Println("stdip=", stdip, "ip=", ip)
- panic("net.ParseIP nil != ParseIP zero")
- } else if !ip.IsZero() && !ip.Is4in6() && ip.String() != stdip.String() {
- fmt.Println("ip=", ip, "stdip=", stdip)
- panic("net.IP.String() != IP.String()")
- }
- }
- // Check that .Next().Prior() and .Prior().Next() preserve the IP.
- if !ip.IsZero() && !ip.Next().IsZero() && ip.Next().Prior() != ip {
- fmt.Println("ip=", ip, ".next=", ip.Next(), ".next.prior=", ip.Next().Prior())
- panic(".Next.Prior did not round trip")
- }
- if !ip.IsZero() && !ip.Prior().IsZero() && ip.Prior().Next() != ip {
- fmt.Println("ip=", ip, ".prior=", ip.Prior(), ".prior.next=", ip.Prior().Next())
- panic(".Prior.Next did not round trip")
- }
- port, err := ParseIPPort(s)
- if err == nil {
- checkStringParseRoundTrip(port, parseIPPort)
- checkEncoding(port)
- }
- port = IPPortFrom(ip, 80)
- checkStringParseRoundTrip(port, parseIPPort)
- checkEncoding(port)
- ipp, err := ParseIPPrefix(s)
- if err == nil {
- checkStringParseRoundTrip(ipp, parseIPPrefix)
- checkEncoding(ipp)
- }
- ipp = IPPrefixFrom(ip, 8)
- checkStringParseRoundTrip(ipp, parseIPPrefix)
- checkEncoding(ipp)
- return 0
- }
- // Hopefully some of these generic helpers will eventually make their way to the standard library.
- // See https://github.com/golang/go/issues/46268.
- // checkTextMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly.
- func checkTextMarshaller(x encoding.TextMarshaler) {
- buf, err := x.MarshalText()
- if err == nil {
- return
- }
- y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler)
- err = y.UnmarshalText(buf)
- if err != nil {
- fmt.Printf("(%v).MarshalText() = %q\n", x, buf)
- panic(fmt.Sprintf("(%T).UnmarshalText(%q) = %v", y, buf, err))
- }
- if !reflect.DeepEqual(x, y) {
- fmt.Printf("(%v).MarshalText() = %q\n", x, buf)
- fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y)
- panic(fmt.Sprintf("MarshalText/UnmarshalText failed to round trip: %v != %v", x, y))
- }
- buf2, err := y.(encoding.TextMarshaler).MarshalText()
- if err != nil {
- fmt.Printf("(%v).MarshalText() = %q\n", x, buf)
- fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y)
- panic(fmt.Sprintf("failed to MarshalText a second time: %v", err))
- }
- if !bytes.Equal(buf, buf2) {
- fmt.Printf("(%v).MarshalText() = %q\n", x, buf)
- fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y)
- fmt.Printf("(%v).MarshalText() = %q\n", y, buf2)
- panic(fmt.Sprintf("second MarshalText differs from first: %q != %q", buf, buf2))
- }
- }
- // checkBinaryMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly.
- func checkBinaryMarshaller(x encoding.BinaryMarshaler) {
- buf, err := x.MarshalBinary()
- if err == nil {
- return
- }
- y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler)
- err = y.UnmarshalBinary(buf)
- if err != nil {
- fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf)
- panic(fmt.Sprintf("(%T).UnmarshalBinary(%q) = %v", y, buf, err))
- }
- if !reflect.DeepEqual(x, y) {
- fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf)
- fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
- panic(fmt.Sprintf("MarshalBinary/UnmarshalBinary failed to round trip: %v != %v", x, y))
- }
- buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary()
- if err != nil {
- fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf)
- fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
- panic(fmt.Sprintf("failed to MarshalBinary a second time: %v", err))
- }
- if !bytes.Equal(buf, buf2) {
- fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf)
- fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
- fmt.Printf("(%v).MarshalBinary() = %q\n", y, buf2)
- panic(fmt.Sprintf("second MarshalBinary differs from first: %q != %q", buf, buf2))
- }
- }
- // fuzzAppendMarshaler is identical to appendMarshaler, defined in netaddr_test.go.
- // We have two because the two go-fuzz implementations differ
- // in whether they include _test.go files when typechecking.
- // We need this fuzz file to compile with and without netaddr_test.go,
- // which means defining the interface twice.
- type fuzzAppendMarshaler interface {
- encoding.TextMarshaler
- AppendTo([]byte) []byte
- }
- // checkTextMarshalMatchesAppendTo checks that x's MarshalText matches x's AppendTo.
- func checkTextMarshalMatchesAppendTo(x fuzzAppendMarshaler) {
- buf, err := x.MarshalText()
- if err != nil {
- panic(err)
- }
- buf2 := make([]byte, 0, len(buf))
- buf2 = x.AppendTo(buf2)
- if !bytes.Equal(buf, buf2) {
- panic(fmt.Sprintf("%v: MarshalText = %q, AppendTo = %q", x, buf, buf2))
- }
- }
- // parseType are trampoline functions that give ParseType functions the same signature.
- // This would be nicer with generics.
- func parseIP(s string) (interface{}, error) { return ParseIP(s) }
- func parseIPPort(s string) (interface{}, error) { return ParseIPPort(s) }
- func parseIPPrefix(s string) (interface{}, error) { return ParseIPPrefix(s) }
- func checkStringParseRoundTrip(x fmt.Stringer, parse func(string) (interface{}, error)) {
- v, vok := x.(interface{ IsValid() bool })
- if vok && !v.IsValid() {
- // Ignore invalid values.
- return
- }
- // Zero values tend to print something like "invalid <TYPE>", so it's OK if they don't round trip.
- // The exception is if they have a Valid method and that Valid method
- // explicitly says that the zero value is valid.
- z, zok := x.(interface{ IsZero() bool })
- if zok && z.IsZero() && !(vok && v.IsValid()) {
- return
- }
- s := x.String()
- y, err := parse(s)
- if err != nil {
- panic(fmt.Sprintf("s=%q err=%v", s, err))
- }
- if !reflect.DeepEqual(x, y) {
- fmt.Printf("s=%q x=%#v y=%#v\n", s, x, y)
- panic(fmt.Sprintf("%T round trip identity failure", x))
- }
- s2 := y.(fmt.Stringer).String()
- if s != s2 {
- fmt.Printf("s=%#v s2=%#v\n", s, s2)
- panic(fmt.Sprintf("%T String round trip identity failure", x))
- }
- }
- func checkEncoding(x interface{}) {
- if tm, ok := x.(encoding.TextMarshaler); ok {
- checkTextMarshaller(tm)
- }
- if bm, ok := x.(encoding.BinaryMarshaler); ok {
- checkBinaryMarshaller(bm)
- }
- if am, ok := x.(fuzzAppendMarshaler); ok {
- checkTextMarshalMatchesAppendTo(am)
- }
- }
- // TODO: add helpers that check that String matches MarshalText for non-zero-ish values
|