FuzzCheckResolvedIP: no NAT64 prefix lets a private IPv4 address through

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.13
dev 1 day ago
parent 69dbb0c88c
commit 416c15534c

@ -42,6 +42,10 @@ verdict.
`https://[[2000::]/`, whose brackets `checkHost` trimmed all at once. `https://[[2000::]/`, whose brackets `checkHost` trimmed all at once.
`isCIDv1` refuses 5 or more bits left over, and `checkHost` takes one pair `isCIDv1` refuses 5 or more bits left over, and `checkHost` takes one pair
of brackets. No normative change: `TestAddressCanonicalForms`. of brackets. No normative change: `TestAddressCanonicalForms`.
- **Fuzzing.** `FuzzCheckResolvedIP`, the 26th target of `scripts/fuzz.sh`:
an address that `CheckResolvedIP` accepts is public, or holds a public
IPv4 address at the positions of RFC 6052 in the NAT64 prefix that
contains it, whatever the prefix given.
- **Test data.** `vectors/resolved_ip.json`, new: 42 addresses, with the - **Test data.** `vectors/resolved_ip.json`, new: 42 addresses, with the
prefix of the network or none, and the result of `CheckResolvedIP`, with prefix of the network or none, and the result of `CheckResolvedIP`, with
its text. The other files do not change. its text. The other files do not change.

@ -3,6 +3,7 @@ package locator_test
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"net/netip"
"testing" "testing"
"g.activething.com/go/DateKeys/extension" "g.activething.com/go/DateKeys/extension"
@ -83,3 +84,59 @@ func FuzzCheckURI(f *testing.F) {
} }
}) })
} }
// FuzzCheckResolvedIP checks the address a name resolves to, with a NAT64
// prefix of the network or none (spec v0.13, §44.1): it never panics, and an
// address it accepts is public, or holds a public IPv4 address at the
// positions of RFC 6052 in 64:ff9b::/96 or in the prefix, with bits 64 to
// 71 zero. No prefix ever lets an address that holds a private IPv4 address
// through.
func FuzzCheckResolvedIP(f *testing.F) {
f.Add(net16("64:ff9b::cb00:7205"), net16("64:ff9b:1::"), uint8(48), false)
f.Add(net16("64:ff9b:1:c0a8:1:a00::"), net16("64:ff9b:1::"), uint8(48), true)
f.Add(net16("2a01:4f8:c0:64::c0a8:10a"), net16("2a01:4f8:c0:64::"), uint8(96), true)
f.Add(net16("2a01:4f8::1"), net16("::"), uint8(0), false)
f.Fuzz(func(t *testing.T, ipb, pb []byte, bits uint8, withPrefix bool) {
if len(ipb) != 16 || len(pb) != 16 {
return
}
ip := netip.AddrFrom16([16]byte(ipb))
var p netip.Prefix
if withPrefix {
p = netip.PrefixFrom(netip.AddrFrom16([16]byte(pb)), int(bits))
}
if locator.CheckResolvedIP(ip, p) != nil {
return
}
public := locator.CheckResolvedIP(ip, netip.Prefix{}) == nil
for _, q := range []netip.Prefix{netip.MustParsePrefix("64:ff9b::/96"), p} {
if !q.IsValid() || !q.Contains(ip) {
continue
}
b := ip.As16()
if q.Bits() < 96 && b[8] != 0 {
t.Fatalf("%s accepted with %v: bits 64 to 71 set", ip, p)
}
var v4 [4]byte
n := 0
for i := q.Bits() / 8; n < 4; i++ {
if i != 8 {
v4[n] = b[i]
n++
}
}
if locator.CheckResolvedIP(netip.AddrFrom4(v4), netip.Prefix{}) != nil {
t.Fatalf("%s accepted with %v: it holds %v, which is not public", ip, p, netip.AddrFrom4(v4))
}
return
}
if !public {
t.Fatalf("%s accepted with %v, outside every NAT64 prefix, and not public", ip, p)
}
})
}
func net16(s string) []byte {
b := netip.MustParseAddr(s).As16()
return b[:]
}

@ -31,6 +31,7 @@ targets=(
"./locator FuzzUnmarshal" "./locator FuzzUnmarshal"
"./locator FuzzParseInfo" "./locator FuzzParseInfo"
"./locator FuzzCheckURI" "./locator FuzzCheckURI"
"./locator FuzzCheckResolvedIP"
"./internal/der FuzzDERCheck" "./internal/der FuzzDERCheck"
"./internal/cms FuzzParseSignature" "./internal/cms FuzzParseSignature"
"./internal/cms FuzzParseToken" "./internal/cms FuzzParseToken"

Loading…
Cancel
Save

Powered by TurnKey Linux.