1package dns 2 3import ( 4 "encoding/hex" 5 "fmt" 6 "net" 7 "reflect" 8 "strings" 9 "testing" 10) 11 12func TestCompressLength(t *testing.T) { 13 m := new(Msg) 14 m.SetQuestion("miek.nl", TypeMX) 15 ul := m.Len() 16 m.Compress = true 17 if ul != m.Len() { 18 t.Fatalf("should be equal") 19 } 20} 21 22// Does the predicted length match final packed length? 23func TestMsgCompressLength(t *testing.T) { 24 makeMsg := func(question string, ans, ns, e []RR) *Msg { 25 msg := new(Msg) 26 msg.SetQuestion(Fqdn(question), TypeANY) 27 msg.Answer = append(msg.Answer, ans...) 28 msg.Ns = append(msg.Ns, ns...) 29 msg.Extra = append(msg.Extra, e...) 30 msg.Compress = true 31 return msg 32 } 33 34 name1 := "12345678901234567890123456789012345.12345678.123." 35 rrA := testRR(name1 + " 3600 IN A 192.0.2.1") 36 rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) 37 tests := []*Msg{ 38 makeMsg(name1, []RR{rrA}, nil, nil), 39 makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)} 40 41 for _, msg := range tests { 42 predicted := msg.Len() 43 buf, err := msg.Pack() 44 if err != nil { 45 t.Error(err) 46 } 47 if predicted < len(buf) { 48 t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", 49 msg.Question[0].Name, len(msg.Answer), predicted, len(buf)) 50 } 51 } 52} 53 54func TestMsgLength(t *testing.T) { 55 makeMsg := func(question string, ans, ns, e []RR) *Msg { 56 msg := new(Msg) 57 msg.Compress = true 58 msg.SetQuestion(Fqdn(question), TypeANY) 59 msg.Answer = append(msg.Answer, ans...) 60 msg.Ns = append(msg.Ns, ns...) 61 msg.Extra = append(msg.Extra, e...) 62 return msg 63 } 64 65 name1 := "12345678901234567890123456789012345.12345678.123." 66 rrA := testRR(name1 + " 3600 IN A 192.0.2.1") 67 rrMx := testRR(name1 + " 3600 IN MX 10 " + name1) 68 tests := []*Msg{ 69 makeMsg(name1, []RR{rrA}, nil, nil), 70 makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)} 71 72 for _, msg := range tests { 73 predicted := msg.Len() 74 buf, err := msg.Pack() 75 if err != nil { 76 t.Error(err) 77 } 78 if predicted < len(buf) { 79 t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d", 80 msg.Question[0].Name, predicted, len(buf)) 81 } 82 } 83} 84 85func TestCompressionLenHelper(t *testing.T) { 86 c := make(map[string]int) 87 compressionLenHelper(c, "example.com", 12) 88 if c["example.com"] != 12 { 89 t.Errorf("bad %d", c["example.com"]) 90 } 91 if c["com"] != 20 { 92 t.Errorf("bad %d", c["com"]) 93 } 94 95 // Test boundaries 96 c = make(map[string]int) 97 // foo label starts at 16379 98 // com label starts at 16384 99 compressionLenHelper(c, "foo.com", 16379) 100 if c["foo.com"] != 16379 { 101 t.Errorf("bad %d", c["foo.com"]) 102 } 103 // com label is accessible 104 if c["com"] != 16383 { 105 t.Errorf("bad %d", c["com"]) 106 } 107 108 c = make(map[string]int) 109 // foo label starts at 16379 110 // com label starts at 16385 => outside range 111 compressionLenHelper(c, "foo.com", 16380) 112 if c["foo.com"] != 16380 { 113 t.Errorf("bad %d", c["foo.com"]) 114 } 115 // com label is NOT accessible 116 if c["com"] != 0 { 117 t.Errorf("bad %d", c["com"]) 118 } 119 120 c = make(map[string]int) 121 compressionLenHelper(c, "example.com", 16375) 122 if c["example.com"] != 16375 { 123 t.Errorf("bad %d", c["example.com"]) 124 } 125 // com starts AFTER 16384 126 if c["com"] != 16383 { 127 t.Errorf("bad %d", c["com"]) 128 } 129 130 c = make(map[string]int) 131 compressionLenHelper(c, "example.com", 16376) 132 if c["example.com"] != 16376 { 133 t.Errorf("bad %d", c["example.com"]) 134 } 135 // com starts AFTER 16384 136 if c["com"] != 0 { 137 t.Errorf("bad %d", c["com"]) 138 } 139} 140 141func TestCompressionLenSearch(t *testing.T) { 142 c := make(map[string]int) 143 compressed, ok, fullSize := compressionLenSearch(c, "a.b.org.") 144 if compressed != 0 || ok || fullSize != 14 { 145 panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize)) 146 } 147 c["org."] = 3 148 compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.") 149 if compressed != 4 || !ok || fullSize != 8 { 150 panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize)) 151 } 152 c["b.org."] = 5 153 compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.") 154 if compressed != 6 || !ok || fullSize != 4 { 155 panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize)) 156 } 157 // Not found long compression 158 c["x.b.org."] = 5 159 compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.") 160 if compressed != 6 || !ok || fullSize != 4 { 161 panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize)) 162 } 163 // Found long compression 164 c["a.b.org."] = 5 165 compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.") 166 if compressed != 8 || !ok || fullSize != 0 { 167 panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize)) 168 } 169} 170 171func TestMsgLength2(t *testing.T) { 172 // Serialized replies 173 var testMessages = []string{ 174 // google.com. IN A? 175 "064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000", 176 // amazon.com. IN A? (reply has no EDNS0 record) 177 // TODO(miek): this one is off-by-one, need to find out why 178 //"6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001", 179 // yahoo.com. IN A? 180 "fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000", 181 // microsoft.com. IN A? 182 "f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000", 183 // google.com. IN MX? 184 "724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000", 185 // reddit.com. IN A? 186 "12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000", 187 } 188 189 for i, hexData := range testMessages { 190 // we won't fail the decoding of the hex 191 input, _ := hex.DecodeString(hexData) 192 193 m := new(Msg) 194 m.Unpack(input) 195 m.Compress = true 196 lenComp := m.Len() 197 b, _ := m.Pack() 198 pacComp := len(b) 199 m.Compress = false 200 lenUnComp := m.Len() 201 b, _ = m.Pack() 202 pacUnComp := len(b) 203 if pacComp+1 != lenComp { 204 t.Errorf("msg.Len(compressed)=%d actual=%d for test %d", lenComp, pacComp, i) 205 } 206 if pacUnComp+1 != lenUnComp { 207 t.Errorf("msg.Len(uncompressed)=%d actual=%d for test %d", lenUnComp, pacUnComp, i) 208 } 209 } 210} 211 212func TestMsgLengthCompressionMalformed(t *testing.T) { 213 // SOA with empty hostmaster, which is illegal 214 soa := &SOA{Hdr: RR_Header{Name: ".", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345}, 215 Ns: ".", 216 Mbox: "", 217 Serial: 0, 218 Refresh: 28800, 219 Retry: 7200, 220 Expire: 604800, 221 Minttl: 60} 222 m := new(Msg) 223 m.Compress = true 224 m.Ns = []RR{soa} 225 m.Len() // Should not crash. 226} 227 228func TestMsgCompressLength2(t *testing.T) { 229 msg := new(Msg) 230 msg.Compress = true 231 msg.SetQuestion(Fqdn("bliep."), TypeANY) 232 msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "blaat.", Rrtype: 0x21, Class: 0x1, Ttl: 0x3c}, Port: 0x4c57, Target: "foo.bar."}) 233 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}}) 234 predicted := msg.Len() 235 buf, err := msg.Pack() 236 if err != nil { 237 t.Error(err) 238 } 239 if predicted != len(buf) { 240 t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", 241 msg.Question[0].Name, len(msg.Answer), predicted, len(buf)) 242 } 243} 244 245func TestMsgCompressLengthLargeRecords(t *testing.T) { 246 msg := new(Msg) 247 msg.Compress = true 248 msg.SetQuestion("my.service.acme.", TypeSRV) 249 j := 1 250 for i := 0; i < 250; i++ { 251 target := fmt.Sprintf("host-redis-1-%d.test.acme.com.node.dc1.consul.", i) 252 msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) 253 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)}) 254 } 255 predicted := msg.Len() 256 buf, err := msg.Pack() 257 if err != nil { 258 t.Error(err) 259 } 260 if predicted != len(buf) { 261 t.Fatalf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", msg.Question[0].Name, len(msg.Answer), predicted, len(buf)) 262 } 263} 264 265func TestCompareCompressionMapsForANY(t *testing.T) { 266 msg := new(Msg) 267 msg.Compress = true 268 msg.SetQuestion("a.service.acme.", TypeANY) 269 // Be sure to have more than 14bits 270 for i := 0; i < 2000; i++ { 271 target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i) 272 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)}}) 273 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)}}) 274 if msg.Len() > 16384 { 275 break 276 } 277 } 278 for labelSize := 0; labelSize < 63; labelSize++ { 279 msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeANY) 280 281 compressionFake := make(map[string]int) 282 lenFake := compressedLenWithCompressionMap(msg, compressionFake) 283 284 compressionReal := make(map[string]int) 285 buf, err := msg.packBufferWithCompressionMap(nil, compressionReal) 286 if err != nil { 287 t.Fatal(err) 288 } 289 if lenFake != len(buf) { 290 t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf)) 291 } 292 if !reflect.DeepEqual(compressionFake, compressionReal) { 293 t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n*** Real:= %v\n\n***Fake:= %v", labelSize, compressionReal, compressionFake) 294 } 295 } 296} 297 298func TestCompareCompressionMapsForSRV(t *testing.T) { 299 msg := new(Msg) 300 msg.Compress = true 301 msg.SetQuestion("a.service.acme.", TypeSRV) 302 // Be sure to have more than 14bits 303 for i := 0; i < 2000; i++ { 304 target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i) 305 msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: ClassINET, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) 306 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)}}) 307 if msg.Len() > 16384 { 308 break 309 } 310 } 311 for labelSize := 0; labelSize < 63; labelSize++ { 312 msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeAAAA) 313 314 compressionFake := make(map[string]int) 315 lenFake := compressedLenWithCompressionMap(msg, compressionFake) 316 317 compressionReal := make(map[string]int) 318 buf, err := msg.packBufferWithCompressionMap(nil, compressionReal) 319 if err != nil { 320 t.Fatal(err) 321 } 322 if lenFake != len(buf) { 323 t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf)) 324 } 325 if !reflect.DeepEqual(compressionFake, compressionReal) { 326 t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n*** Real:= %v\n\n***Fake:= %v", labelSize, compressionReal, compressionFake) 327 } 328 } 329} 330 331func TestMsgCompressLengthLargeRecordsWithPaddingPermutation(t *testing.T) { 332 msg := new(Msg) 333 msg.Compress = true 334 msg.SetQuestion("my.service.acme.", TypeSRV) 335 336 for i := 0; i < 250; i++ { 337 target := fmt.Sprintf("host-redis-x-%d.test.acme.com.node.dc1.consul.", i) 338 msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) 339 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)}) 340 } 341 for labelSize := 1; labelSize < 63; labelSize++ { 342 msg.SetQuestion(fmt.Sprintf("my.%s.service.acme.", strings.Repeat("x", labelSize)), TypeSRV) 343 predicted := msg.Len() 344 buf, err := msg.Pack() 345 if err != nil { 346 t.Error(err) 347 } 348 if predicted != len(buf) { 349 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)) 350 } 351 } 352} 353 354func TestMsgCompressLengthLargeRecordsAllValues(t *testing.T) { 355 msg := new(Msg) 356 msg.Compress = true 357 msg.SetQuestion("redis.service.consul.", TypeSRV) 358 for i := 0; i < 900; i++ { 359 target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256) 360 msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target}) 361 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)}) 362 predicted := msg.Len() 363 buf, err := msg.Pack() 364 if err != nil { 365 t.Error(err) 366 } 367 if predicted != len(buf) { 368 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)) 369 } 370 } 371} 372