mirror of https://github.com/k3d-io/k3d
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
6.6 KiB
203 lines
6.6 KiB
// 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
|
|
|