dns/msg_helpers_test.go

594 lines
14 KiB
Go

package dns
import (
"bytes"
"net"
"testing"
)
// TestPacketDataNsec tests generated using fuzz.go and with a message pack
// containing the following bytes: 0000\x00\x00000000\x00\x002000000\x0060000\x00\x130000000000000000000"
// That bytes sequence created the overflow error and further permutations of that sequence were able to trigger
// the other code paths.
func TestPackDataNsec(t *testing.T) {
type args struct {
bitmap []uint16
msg []byte
off int
}
tests := []struct {
name string
args args
wantOff int
wantBytes []byte
wantErr bool
wantErrMsg string
}{
{
name: "overflow",
args: args{
bitmap: []uint16{
8962, 8963, 8970, 8971, 8978, 8979,
8986, 8987, 8994, 8995, 9002, 9003,
9010, 9011, 9018, 9019, 9026, 9027,
9034, 9035, 9042, 9043, 9050, 9051,
9058, 9059, 9066,
},
msg: []byte{
48, 48, 48, 48, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 50,
48, 48, 48, 48, 48, 48,
0, 54, 48, 48, 48, 48,
0, 19, 48, 48,
},
off: 48,
},
wantErr: true,
wantErrMsg: "dns: overflow packing nsec",
wantOff: 48,
},
{
name: "disordered nsec bits",
args: args{
bitmap: []uint16{
8962,
1,
},
msg: []byte{
48, 48, 48, 48, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 50, 48, 48, 48, 48, 48, 48, 0, 54, 48,
48, 48, 48, 0, 19, 48, 48, 48, 48, 48, 48, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 50, 48, 48,
48, 48, 48, 48, 0, 54, 48, 48, 48, 48, 0, 19,
48, 48, 48, 48, 48, 48, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 50, 48, 48, 48, 48, 48, 48, 0,
54, 48, 48, 48, 48, 0, 19, 48, 48, 48, 48, 48,
48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 50,
48, 48, 48, 48, 48, 48, 0, 54, 48, 48, 48, 48,
0, 19, 48, 48, 48, 48, 48, 48, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 50, 48, 48, 48, 48, 48,
48, 0, 54, 48, 48, 48, 48, 0, 19, 48, 48,
},
off: 0,
},
wantErr: true,
wantErrMsg: "dns: nsec bits out of order",
wantOff: 155,
},
{
name: "simple message with only one window",
args: args{
bitmap: []uint16{
1,
},
msg: []byte{
48, 48, 48, 48, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 50, 48, 48, 48,
48, 48, 48, 0, 54, 48,
48, 48, 48, 0, 19, 48, 48,
},
off: 0,
},
wantErr: false,
wantOff: 3,
wantBytes: []byte{0, 1, 64},
},
{
name: "multiple types",
args: args{
bitmap: []uint16{
TypeNS, TypeSOA, TypeRRSIG, TypeDNSKEY, TypeNSEC3PARAM,
},
msg: []byte{
48, 48, 48, 48, 0, 0,
0, 1, 0, 0, 0, 0,
0, 0, 50, 48, 48, 48,
48, 48, 48, 0, 54, 48,
48, 48, 48, 0, 19, 48, 48,
},
off: 0,
},
wantErr: false,
wantOff: 9,
wantBytes: []byte{0, 7, 34, 0, 0, 0, 0, 2, 144},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotOff, err := packDataNsec(tt.args.bitmap, tt.args.msg, tt.args.off)
if (err != nil) != tt.wantErr {
t.Errorf("packDataNsec() error = %v, wantErr %v", err, tt.wantErr)
return
}
if err != nil && tt.wantErrMsg != err.Error() {
t.Errorf("packDataNsec() error msg = %v, wantErrMsg %v", err.Error(), tt.wantErrMsg)
return
}
if gotOff != tt.wantOff {
t.Errorf("packDataNsec() = %v, want off %v", gotOff, tt.wantOff)
}
if err == nil && tt.args.off < len(tt.args.msg) && gotOff < len(tt.args.msg) {
if want, got := tt.wantBytes, tt.args.msg[tt.args.off:gotOff]; !bytes.Equal(got, want) {
t.Errorf("packDataNsec() = %v, want bytes %v", got, want)
}
}
})
}
}
func TestPackDataNsecDirtyBuffer(t *testing.T) {
zeroBuf := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0}
dirtyBuf := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}
off1, _ := packDataNsec([]uint16{TypeNS, TypeSOA, TypeRRSIG}, zeroBuf, 0)
off2, _ := packDataNsec([]uint16{TypeNS, TypeSOA, TypeRRSIG}, dirtyBuf, 0)
if off1 != off2 {
t.Errorf("off1 %v != off2 %v", off1, off2)
}
if !bytes.Equal(zeroBuf[:off1], dirtyBuf[:off2]) {
t.Errorf("dirty buffer differs from zero buffer: %v, %v", zeroBuf[:off1], dirtyBuf[:off2])
}
}
func BenchmarkPackDataNsec(b *testing.B) {
benches := []struct {
name string
types []uint16
}{
{"empty", nil},
{"typical", []uint16{TypeNS, TypeSOA, TypeRRSIG, TypeDNSKEY, TypeNSEC3PARAM}},
{"multiple_windows", []uint16{1, 300, 350, 10000, 20000}},
}
for _, bb := range benches {
b.Run(bb.name, func(b *testing.B) {
buf := make([]byte, 100)
for n := 0; n < b.N; n++ {
packDataNsec(bb.types, buf, 0)
}
})
}
}
func TestUnpackString(t *testing.T) {
msg := []byte("\x00abcdef\x0f\\\"ghi\x04mmm\x7f")
msg[0] = byte(len(msg) - 1)
got, _, err := unpackString(msg, 0)
if err != nil {
t.Fatal(err)
}
if want := `abcdef\015\\\"ghi\004mmm\127`; want != got {
t.Errorf("expected %q, got %q", want, got)
}
}
func BenchmarkUnpackString(b *testing.B) {
b.Run("Escaped", func(b *testing.B) {
msg := []byte("\x00abcdef\x0f\\\"ghi\x04mmm")
msg[0] = byte(len(msg) - 1)
for n := 0; n < b.N; n++ {
got, _, err := unpackString(msg, 0)
if err != nil {
b.Fatal(err)
}
if want := `abcdef\015\\\"ghi\004mmm`; want != got {
b.Errorf("expected %q, got %q", want, got)
}
}
})
b.Run("Unescaped", func(b *testing.B) {
msg := []byte("\x00large.example.com")
msg[0] = byte(len(msg) - 1)
for n := 0; n < b.N; n++ {
got, _, err := unpackString(msg, 0)
if err != nil {
b.Fatal(err)
}
if want := "large.example.com"; want != got {
b.Errorf("expected %q, got %q", want, got)
}
}
})
}
func TestPackDataAplPrefix(t *testing.T) {
tests := []struct {
name string
negation bool
ip net.IP
mask net.IPMask
expect []byte
}{
{
"1:192.0.2.0/24",
false,
net.ParseIP("192.0.2.0").To4(),
net.CIDRMask(24, 32),
[]byte{0x00, 0x01, 0x18, 0x03, 192, 0, 2},
},
{
"2:2001:db8:cafe::0/48",
false,
net.ParseIP("2001:db8:cafe::"),
net.CIDRMask(48, 128),
[]byte{0x00, 0x02, 0x30, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe},
},
{
"with trailing zero bytes 2:2001:db8:cafe::0/64",
false,
net.ParseIP("2001:db8:cafe::"),
net.CIDRMask(64, 128),
[]byte{0x00, 0x02, 0x40, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe},
},
{
"no non-zero bytes 2::/16",
false,
net.ParseIP("::"),
net.CIDRMask(16, 128),
[]byte{0x00, 0x02, 0x10, 0x00},
},
{
"!2:2001:db8::/32",
true,
net.ParseIP("2001:db8::"),
net.CIDRMask(32, 128),
[]byte{0x00, 0x02, 0x20, 0x84, 0x20, 0x01, 0x0d, 0xb8},
},
{
"normalize 1:198.51.103.255/22",
false,
net.ParseIP("198.51.103.255").To4(),
net.CIDRMask(22, 32),
[]byte{0x00, 0x01, 0x16, 0x03, 198, 51, 100}, // 1:198.51.100.0/22
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ap := &APLPrefix{
Negation: tt.negation,
Network: net.IPNet{IP: tt.ip, Mask: tt.mask},
}
out := make([]byte, 16)
off, err := packDataAplPrefix(ap, out, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if !bytes.Equal(tt.expect, out[:off]) {
t.Fatalf("expected output %02x, got %02x", tt.expect, out[:off])
}
// Make sure the packed bytes would be accepted by its own unpack
_, _, err = unpackDataAplPrefix(out, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
})
}
}
func TestPackDataAplPrefix_Failures(t *testing.T) {
tests := []struct {
name string
ip net.IP
mask net.IPMask
}{
{
"family mismatch",
net.ParseIP("2001:db8::"),
net.CIDRMask(24, 32),
},
{
"unrecognized family",
[]byte{0x42},
[]byte{0xff},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ap := &APLPrefix{Network: net.IPNet{IP: tt.ip, Mask: tt.mask}}
msg := make([]byte, 16)
off, err := packDataAplPrefix(ap, msg, 0)
if err == nil {
t.Fatal("expected error, got none")
}
if off != len(msg) {
t.Fatalf("expected %d, got %d", len(msg), off)
}
})
}
}
func TestPackDataAplPrefix_BufferBounds(t *testing.T) {
ap := &APLPrefix{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("2001:db8::"),
Mask: net.CIDRMask(32, 128),
},
}
wire := []byte{0x00, 0x02, 0x20, 0x04, 0x20, 0x01, 0x0d, 0xb8}
t.Run("small", func(t *testing.T) {
msg := make([]byte, len(wire))
_, err := packDataAplPrefix(ap, msg, 1) // offset
if err == nil {
t.Fatal("expected error, got none")
}
})
t.Run("exact fit", func(t *testing.T) {
msg := make([]byte, len(wire))
off, err := packDataAplPrefix(ap, msg, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if !bytes.Equal(wire, msg[:off]) {
t.Fatalf("expected %02x, got %02x", wire, msg[:off])
}
})
}
func TestPackDataApl(t *testing.T) {
in := []APLPrefix{
{
Negation: true,
Network: net.IPNet{
IP: net.ParseIP("198.51.0.0").To4(),
Mask: net.CIDRMask(16, 32),
},
},
{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("2001:db8:beef::"),
Mask: net.CIDRMask(48, 128),
},
},
}
expect := []byte{
// 1:192.51.0.0/16
0x00, 0x01, 0x10, 0x82, 0xc6, 0x33,
// 2:2001:db8:beef::0/48
0x00, 0x02, 0x30, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xbe, 0xef,
}
msg := make([]byte, 32)
off, err := packDataApl(in, msg, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if !bytes.Equal(expect, msg[:off]) {
t.Fatalf("expected %02x, got %02x", expect, msg[:off])
}
}
func TestUnpackDataAplPrefix(t *testing.T) {
tests := []struct {
name string
wire []byte
negation bool
ip net.IP
mask net.IPMask
}{
{
"1:192.0.2.0/24",
[]byte{0x00, 0x01, 0x18, 0x03, 192, 0, 2},
false,
net.ParseIP("192.0.2.0").To4(),
net.CIDRMask(24, 32),
},
{
"2:2001:db8::/32",
[]byte{0x00, 0x02, 0x20, 0x04, 0x20, 0x01, 0x0d, 0xb8},
false,
net.ParseIP("2001:db8::"),
net.CIDRMask(32, 128),
},
{
"!2:2001:db8:8000::/33",
[]byte{0x00, 0x02, 0x21, 0x85, 0x20, 0x01, 0x0d, 0xb8, 0x80},
true,
net.ParseIP("2001:db8:8000::"),
net.CIDRMask(33, 128),
},
{
"1:0.0.0.0/0",
[]byte{0x00, 0x01, 0x00, 0x00},
false,
net.ParseIP("0.0.0.0").To4(),
net.CIDRMask(0, 32),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, off, err := unpackDataAplPrefix(tt.wire, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if off != len(tt.wire) {
t.Fatalf("expected offset %d, got %d", len(tt.wire), off)
}
if got.Negation != tt.negation {
t.Errorf("expected negation %v, got %v", tt.negation, got.Negation)
}
if !bytes.Equal(got.Network.IP, tt.ip) {
t.Errorf("expected IP %02x, got %02x", tt.ip, got.Network.IP)
}
if !bytes.Equal(got.Network.Mask, tt.mask) {
t.Errorf("expected mask %02x, got %02x", tt.mask, got.Network.Mask)
}
})
}
}
func TestUnpackDataAplPrefix_Errors(t *testing.T) {
tests := []struct {
name string
wire []byte
want string
}{
{
"incomplete header",
[]byte{0x00, 0x01, 0x18},
"dns: overflow unpacking APL prefix",
},
{
"unrecognized family",
[]byte{0x00, 0x03, 0x00, 0x00},
"dns: unrecognized APL address family",
},
{
"prefix too large for family IPv4",
[]byte{0x00, 0x01, 0x21, 0x04, 192, 0, 2, 0},
"dns: APL prefix too long",
},
{
"prefix too large for family IPv6",
[]byte{0x00, 0x02, 0x81, 0x85, 0x20, 0x01, 0x0d, 0xb8, 0x80},
"dns: APL prefix too long",
},
{
"afdlen too long for address family IPv4",
[]byte{0x00, 0x01, 22, 0x05, 192, 0, 2, 0, 0},
"dns: APL length too long",
},
{
"overflow unpacking APL address",
[]byte{0x00, 0x01, 0x10, 0x02, 192},
"dns: overflow unpacking APL address",
},
{
"address included trailing zeros",
[]byte{0x00, 0x01, 0x10, 0x04, 192, 0, 2, 0},
"dns: extra APL address bits",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, _, err := unpackDataAplPrefix(tt.wire, 0)
if err == nil {
t.Fatal("expected error, got none")
}
if err.Error() != tt.want {
t.Errorf("expected %s, got %s", tt.want, err.Error())
}
})
}
}
func TestUnpackDataApl(t *testing.T) {
wire := []byte{
// 2:2001:db8:cafe:4200:0/56
0x00, 0x02, 0x38, 0x07, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0x42,
// 1:192.0.2.0/24
0x00, 0x01, 0x18, 0x03, 192, 0, 2,
// !1:192.0.2.128/25
0x00, 0x01, 0x19, 0x84, 192, 0, 2, 128,
// 1:10.0.0.0/24
0x00, 0x01, 0x18, 0x01, 0x0a,
// !1:10.0.0.1/32
0x00, 0x01, 0x20, 0x84, 0x0a, 0, 0, 1,
// !1:0.0.0.0/0
0x00, 0x01, 0x00, 0x80,
// 2::0/0
0x00, 0x02, 0x00, 0x00,
}
expect := []APLPrefix{
{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("2001:db8:cafe:4200::"),
Mask: net.CIDRMask(56, 128),
},
},
{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("192.0.2.0").To4(),
Mask: net.CIDRMask(24, 32),
},
},
{
Negation: true,
Network: net.IPNet{
IP: net.ParseIP("192.0.2.128").To4(),
Mask: net.CIDRMask(25, 32),
},
},
{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("10.0.0.0").To4(),
Mask: net.CIDRMask(24, 32),
},
},
{
Negation: true,
Network: net.IPNet{
IP: net.ParseIP("10.0.0.1").To4(),
Mask: net.CIDRMask(32, 32),
},
},
{
Negation: true,
Network: net.IPNet{
IP: net.ParseIP("0.0.0.0").To4(),
Mask: net.CIDRMask(0, 32),
},
},
{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("::").To16(),
Mask: net.CIDRMask(0, 128),
},
},
}
got, off, err := unpackDataApl(wire, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if off != len(wire) {
t.Fatalf("expected offset %d, got %d", len(wire), off)
}
if len(got) != len(expect) {
t.Fatalf("expected %d prefixes, got %d", len(expect), len(got))
}
for i, exp := range expect {
if got[i].Negation != exp.Negation {
t.Errorf("[%d] expected negation %v, got %v", i, exp.Negation, got[i].Negation)
}
if !bytes.Equal(got[i].Network.IP, exp.Network.IP) {
t.Errorf("[%d] expected IP %02x, got %02x", i, exp.Network.IP, got[i].Network.IP)
}
if !bytes.Equal(got[i].Network.Mask, exp.Network.Mask) {
t.Errorf("[%d] expected mask %02x, got %02x", i, exp.Network.Mask, got[i].Network.Mask)
}
}
}