1package dns
2
3import (
4	"encoding/hex"
5	"fmt"
6	"net"
7	"strings"
8	"testing"
9)
10
11func TestCompressLength(t *testing.T) {
12	m := new(Msg)
13	m.SetQuestion("miek.nl.", TypeMX)
14	ul := m.Len()
15	m.Compress = true
16	if ul != m.Len() {
17		t.Fatalf("should be equal")
18	}
19}
20
21// Does the predicted length match final packed length?
22func TestMsgCompressLength(t *testing.T) {
23	makeMsg := func(question string, ans, ns, e []RR) *Msg {
24		msg := new(Msg)
25		msg.SetQuestion(Fqdn(question), TypeANY)
26		msg.Answer = append(msg.Answer, ans...)
27		msg.Ns = append(msg.Ns, ns...)
28		msg.Extra = append(msg.Extra, e...)
29		msg.Compress = true
30		return msg
31	}
32
33	name1 := "12345678901234567890123456789012345.12345678.123."
34	rrA := testRR(name1 + " 3600 IN A 192.0.2.1")
35	rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
36	tests := []*Msg{
37		makeMsg(name1, []RR{rrA}, nil, nil),
38		makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
39
40	for _, msg := range tests {
41		predicted := msg.Len()
42		buf, err := msg.Pack()
43		if err != nil {
44			t.Error(err)
45		}
46		if predicted < len(buf) {
47			t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
48				msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
49		}
50	}
51}
52
53func TestMsgLength(t *testing.T) {
54	makeMsg := func(question string, ans, ns, e []RR) *Msg {
55		msg := new(Msg)
56		msg.Compress = true
57		msg.SetQuestion(Fqdn(question), TypeANY)
58		msg.Answer = append(msg.Answer, ans...)
59		msg.Ns = append(msg.Ns, ns...)
60		msg.Extra = append(msg.Extra, e...)
61		return msg
62	}
63
64	name1 := "12345678901234567890123456789012345.12345678.123."
65	rrA := testRR(name1 + " 3600 IN A 192.0.2.1")
66	rrMx := testRR(name1 + " 3600 IN MX 10 " + name1)
67	tests := []*Msg{
68		makeMsg(name1, []RR{rrA}, nil, nil),
69		makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
70
71	for _, msg := range tests {
72		predicted := msg.Len()
73		buf, err := msg.Pack()
74		if err != nil {
75			t.Error(err)
76		}
77		if predicted < len(buf) {
78			t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d",
79				msg.Question[0].Name, predicted, len(buf))
80		}
81	}
82}
83
84func TestCompressionLenSearchInsert(t *testing.T) {
85	c := make(map[string]struct{})
86	compressionLenSearch(c, "example.com", 12)
87	if _, ok := c["example.com"]; !ok {
88		t.Errorf("bad example.com")
89	}
90	if _, ok := c["com"]; !ok {
91		t.Errorf("bad com")
92	}
93
94	// Test boundaries
95	c = make(map[string]struct{})
96	// foo label starts at 16379
97	// com label starts at 16384
98	compressionLenSearch(c, "foo.com", 16379)
99	if _, ok := c["foo.com"]; !ok {
100		t.Errorf("bad foo.com")
101	}
102	// com label is accessible
103	if _, ok := c["com"]; !ok {
104		t.Errorf("bad com")
105	}
106
107	c = make(map[string]struct{})
108	// foo label starts at 16379
109	// com label starts at 16385 => outside range
110	compressionLenSearch(c, "foo.com", 16380)
111	if _, ok := c["foo.com"]; !ok {
112		t.Errorf("bad foo.com")
113	}
114	// com label is NOT accessible
115	if _, ok := c["com"]; ok {
116		t.Errorf("bad com")
117	}
118
119	c = make(map[string]struct{})
120	compressionLenSearch(c, "example.com", 16375)
121	if _, ok := c["example.com"]; !ok {
122		t.Errorf("bad example.com")
123	}
124	// com starts AFTER 16384
125	if _, ok := c["com"]; !ok {
126		t.Errorf("bad com")
127	}
128
129	c = make(map[string]struct{})
130	compressionLenSearch(c, "example.com", 16376)
131	if _, ok := c["example.com"]; !ok {
132		t.Errorf("bad example.com")
133	}
134	// com starts AFTER 16384
135	if _, ok := c["com"]; ok {
136		t.Errorf("bad com")
137	}
138}
139
140func TestCompressionLenSearch(t *testing.T) {
141	c := make(map[string]struct{})
142	compressed, ok := compressionLenSearch(c, "a.b.org.", maxCompressionOffset)
143	if compressed != 0 || ok {
144		t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok)
145	}
146	c["org."] = struct{}{}
147	compressed, ok = compressionLenSearch(c, "a.b.org.", maxCompressionOffset)
148	if compressed != 4 || !ok {
149		t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok)
150	}
151	c["b.org."] = struct{}{}
152	compressed, ok = compressionLenSearch(c, "a.b.org.", maxCompressionOffset)
153	if compressed != 2 || !ok {
154		t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok)
155	}
156	// Not found long compression
157	c["x.b.org."] = struct{}{}
158	compressed, ok = compressionLenSearch(c, "a.b.org.", maxCompressionOffset)
159	if compressed != 2 || !ok {
160		t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok)
161	}
162	// Found long compression
163	c["a.b.org."] = struct{}{}
164	compressed, ok = compressionLenSearch(c, "a.b.org.", maxCompressionOffset)
165	if compressed != 0 || !ok {
166		t.Errorf("Failed: compressed:=%d, ok:=%v", compressed, ok)
167	}
168}
169
170func TestMsgLength2(t *testing.T) {
171	// Serialized replies
172	var testMessages = []string{
173		// google.com. IN A?
174		"064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000",
175		// amazon.com. IN A? (reply has no EDNS0 record)
176		"6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001",
177		// yahoo.com. IN A?
178		"fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000",
179		// microsoft.com. IN A?
180		"f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000",
181		// google.com. IN MX?
182		"724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000",
183		// reddit.com. IN A?
184		"12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000",
185	}
186
187	for i, hexData := range testMessages {
188		// we won't fail the decoding of the hex
189		input, _ := hex.DecodeString(hexData)
190
191		m := new(Msg)
192		m.Unpack(input)
193		m.Compress = true
194		lenComp := m.Len()
195		b, _ := m.Pack()
196		pacComp := len(b)
197		m.Compress = false
198		lenUnComp := m.Len()
199		b, _ = m.Pack()
200		pacUnComp := len(b)
201		if pacComp != lenComp {
202			t.Errorf("msg.Len(compressed)=%d actual=%d for test %d", lenComp, pacComp, i)
203		}
204		if pacUnComp != lenUnComp {
205			t.Errorf("msg.Len(uncompressed)=%d actual=%d for test %d", lenUnComp, pacUnComp, i)
206		}
207	}
208}
209
210func TestMsgLengthCompressionMalformed(t *testing.T) {
211	// SOA with empty hostmaster, which is illegal
212	soa := &SOA{Hdr: RR_Header{Name: ".", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345},
213		Ns:      ".",
214		Mbox:    "",
215		Serial:  0,
216		Refresh: 28800,
217		Retry:   7200,
218		Expire:  604800,
219		Minttl:  60}
220	m := new(Msg)
221	m.Compress = true
222	m.Ns = []RR{soa}
223	m.Len() // Should not crash.
224}
225
226func TestMsgCompressLength2(t *testing.T) {
227	msg := new(Msg)
228	msg.Compress = true
229	msg.SetQuestion(Fqdn("bliep."), TypeANY)
230	msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "blaat.", Rrtype: 0x21, Class: 0x1, Ttl: 0x3c}, Port: 0x4c57, Target: "foo.bar."})
231	msg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: "foo.bar.", Rrtype: 0x1, Class: 0x1, Ttl: 0x3c}, A: net.IP{0xac, 0x11, 0x0, 0x3}})
232	predicted := msg.Len()
233	buf, err := msg.Pack()
234	if err != nil {
235		t.Error(err)
236	}
237	if predicted != len(buf) {
238		t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
239			msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
240	}
241}
242
243func TestMsgCompressLengthLargeRecords(t *testing.T) {
244	msg := new(Msg)
245	msg.Compress = true
246	msg.SetQuestion("my.service.acme.", TypeSRV)
247	j := 1
248	for i := 0; i < 250; i++ {
249		target := fmt.Sprintf("host-redis-1-%d.test.acme.com.node.dc1.consul.", i)
250		msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
251		msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: 1, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", j, i)})
252	}
253	predicted := msg.Len()
254	buf, err := msg.Pack()
255	if err != nil {
256		t.Error(err)
257	}
258	if predicted != len(buf) {
259		t.Fatalf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
260	}
261}
262
263func compressionMapsEqual(a map[string]struct{}, b map[string]int) bool {
264	if len(a) != len(b) {
265		return false
266	}
267
268	for k := range a {
269		if _, ok := b[k]; !ok {
270			return false
271		}
272	}
273
274	return true
275}
276
277func compressionMapsDifference(a map[string]struct{}, b map[string]int) string {
278	var s strings.Builder
279
280	var c int
281	fmt.Fprintf(&s, "length compression map (%d):", len(a))
282	for k := range b {
283		if _, ok := a[k]; !ok {
284			if c > 0 {
285				s.WriteString(",")
286			}
287
288			fmt.Fprintf(&s, " missing %q", k)
289			c++
290		}
291	}
292
293	c = 0
294	fmt.Fprintf(&s, "\npack compression map (%d):", len(b))
295	for k := range a {
296		if _, ok := b[k]; !ok {
297			if c > 0 {
298				s.WriteString(",")
299			}
300
301			fmt.Fprintf(&s, " missing %q", k)
302			c++
303		}
304	}
305
306	return s.String()
307}
308
309func TestCompareCompressionMapsForANY(t *testing.T) {
310	msg := new(Msg)
311	msg.Compress = true
312	msg.SetQuestion("a.service.acme.", TypeANY)
313	// Be sure to have more than 14bits
314	for i := 0; i < 2000; i++ {
315		target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i)
316		msg.Answer = append(msg.Answer, &AAAA{Hdr: RR_Header{Name: target, Rrtype: TypeAAAA, Class: ClassINET, Ttl: 0x3c}, AAAA: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(i / 255), byte(i % 255)}})
317		msg.Answer = append(msg.Answer, &A{Hdr: RR_Header{Name: target, Rrtype: TypeA, Class: ClassINET, Ttl: 0x3c}, A: net.IP{127, 0, byte(i / 255), byte(i % 255)}})
318		if msg.Len() > 16384 {
319			break
320		}
321	}
322	for labelSize := 0; labelSize < 63; labelSize++ {
323		msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeANY)
324
325		compressionFake := make(map[string]struct{})
326		lenFake := msgLenWithCompressionMap(msg, compressionFake)
327
328		compressionReal := make(map[string]int)
329		buf, err := msg.packBufferWithCompressionMap(nil, compressionMap{ext: compressionReal}, true)
330		if err != nil {
331			t.Fatal(err)
332		}
333		if lenFake != len(buf) {
334			t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf))
335		}
336		if !compressionMapsEqual(compressionFake, compressionReal) {
337			t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n%s", labelSize, compressionMapsDifference(compressionFake, compressionReal))
338		}
339	}
340}
341
342func TestCompareCompressionMapsForSRV(t *testing.T) {
343	msg := new(Msg)
344	msg.Compress = true
345	msg.SetQuestion("a.service.acme.", TypeSRV)
346	// Be sure to have more than 14bits
347	for i := 0; i < 2000; i++ {
348		target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i)
349		msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: ClassINET, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
350		msg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: target, Rrtype: TypeA, Class: ClassINET, Ttl: 0x3c}, A: net.IP{127, 0, byte(i / 255), byte(i % 255)}})
351		if msg.Len() > 16384 {
352			break
353		}
354	}
355	for labelSize := 0; labelSize < 63; labelSize++ {
356		msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeAAAA)
357
358		compressionFake := make(map[string]struct{})
359		lenFake := msgLenWithCompressionMap(msg, compressionFake)
360
361		compressionReal := make(map[string]int)
362		buf, err := msg.packBufferWithCompressionMap(nil, compressionMap{ext: compressionReal}, true)
363		if err != nil {
364			t.Fatal(err)
365		}
366		if lenFake != len(buf) {
367			t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf))
368		}
369		if !compressionMapsEqual(compressionFake, compressionReal) {
370			t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n%s", labelSize, compressionMapsDifference(compressionFake, compressionReal))
371		}
372	}
373}
374
375func TestMsgCompressLengthLargeRecordsWithPaddingPermutation(t *testing.T) {
376	msg := new(Msg)
377	msg.Compress = true
378	msg.SetQuestion("my.service.acme.", TypeSRV)
379
380	for i := 0; i < 250; i++ {
381		target := fmt.Sprintf("host-redis-x-%d.test.acme.com.node.dc1.consul.", i)
382		msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
383		msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.x.%d.", i)})
384	}
385	for labelSize := 1; labelSize < 63; labelSize++ {
386		msg.SetQuestion(fmt.Sprintf("my.%s.service.acme.", strings.Repeat("x", labelSize)), TypeSRV)
387		predicted := msg.Len()
388		buf, err := msg.Pack()
389		if err != nil {
390			t.Error(err)
391		}
392		if predicted != len(buf) {
393			t.Fatalf("padding= %d ; predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", labelSize, msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
394		}
395	}
396}
397
398func TestMsgCompressLengthLargeRecordsAllValues(t *testing.T) {
399	// we want to cross the 14 (16384) bit boundary here, so we build it up to just below and go slightly over.
400	msg := new(Msg)
401	msg.Compress = true
402	msg.SetQuestion("redis.service.consul.", TypeSRV)
403	for i := 0; i < 170; i++ {
404		target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256)
405		msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
406		msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", i/256, i%256)})
407	}
408	// msg.Len() == 15458
409	// msg.Len() == 16470 at 180
410
411	for i := 170; i < 181; i++ {
412		target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256)
413		msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
414		msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", i/256, i%256)})
415		predicted := msg.Len()
416		buf, err := msg.Pack()
417		if err != nil {
418			t.Error(err)
419		}
420		if predicted != len(buf) {
421			t.Fatalf("predicted compressed length is wrong for %d records: predicted %s (len=%d) %d, actual %d", i, msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
422		}
423	}
424}
425
426func TestMsgCompressionMultipleQuestions(t *testing.T) {
427	msg := new(Msg)
428	msg.Compress = true
429	msg.SetQuestion("www.example.org.", TypeA)
430	msg.Question = append(msg.Question, Question{"other.example.org.", TypeA, ClassINET})
431
432	predicted := msg.Len()
433	buf, err := msg.Pack()
434	if err != nil {
435		t.Error(err)
436	}
437	if predicted != len(buf) {
438		t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf))
439	}
440}
441
442func TestMsgCompressMultipleCompressedNames(t *testing.T) {
443	msg := new(Msg)
444	msg.Compress = true
445	msg.SetQuestion("www.example.com.", TypeSRV)
446	msg.Answer = append(msg.Answer, &MINFO{
447		Hdr:   RR_Header{Name: "www.example.com.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c},
448		Rmail: "mail.example.org.",
449		Email: "mail.example.org.",
450	})
451	msg.Answer = append(msg.Answer, &SOA{
452		Hdr:  RR_Header{Name: "www.example.com.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c},
453		Ns:   "ns.example.net.",
454		Mbox: "mail.example.net.",
455	})
456
457	predicted := msg.Len()
458	buf, err := msg.Pack()
459	if err != nil {
460		t.Error(err)
461	}
462	if predicted != len(buf) {
463		t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf))
464	}
465}
466
467func TestMsgCompressLengthEscapingMatch(t *testing.T) {
468	// Although slightly non-optimal, "example.org." and "ex\\097mple.org."
469	// are not considered equal in the compression map, even though \097 is
470	// a valid escaping of a. This test ensures that the Len code and the
471	// Pack code don't disagree on this.
472
473	msg := new(Msg)
474	msg.Compress = true
475	msg.SetQuestion("www.example.org.", TypeA)
476	msg.Answer = append(msg.Answer, &NS{Hdr: RR_Header{Name: "ex\\097mple.org.", Rrtype: TypeNS, Class: ClassINET}, Ns: "ns.example.org."})
477
478	predicted := msg.Len()
479	buf, err := msg.Pack()
480	if err != nil {
481		t.Error(err)
482	}
483	if predicted != len(buf) {
484		t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf))
485	}
486}
487
488func TestMsgLengthEscaped(t *testing.T) {
489	msg := new(Msg)
490	msg.SetQuestion(`\000\001\002.\003\004\005\006\007\008\009.\a\b\c.`, TypeA)
491
492	predicted := msg.Len()
493	buf, err := msg.Pack()
494	if err != nil {
495		t.Error(err)
496	}
497	if predicted != len(buf) {
498		t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf))
499	}
500}
501
502func TestMsgCompressLengthEscaped(t *testing.T) {
503	msg := new(Msg)
504	msg.Compress = true
505	msg.SetQuestion("www.example.org.", TypeA)
506	msg.Answer = append(msg.Answer, &NS{Hdr: RR_Header{Name: `\000\001\002.example.org.`, Rrtype: TypeNS, Class: ClassINET}, Ns: `ns.\e\x\a\m\p\l\e.org.`})
507	msg.Answer = append(msg.Answer, &NS{Hdr: RR_Header{Name: `www.\e\x\a\m\p\l\e.org.`, Rrtype: TypeNS, Class: ClassINET}, Ns: "ns.example.org."})
508
509	predicted := msg.Len()
510	buf, err := msg.Pack()
511	if err != nil {
512		t.Error(err)
513	}
514	if predicted != len(buf) {
515		t.Fatalf("predicted compressed length is wrong: predicted %d, actual %d", predicted, len(buf))
516	}
517}
518