1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package net 6 7 import ( 8 "encoding/hex" 9 "reflect" 10 "testing" 11 ) 12 13 func TestStructPackUnpack(t *testing.T) { 14 want := dnsQuestion{ 15 Name: ".", 16 Qtype: dnsTypeA, 17 Qclass: dnsClassINET, 18 } 19 buf := make([]byte, 50) 20 n, ok := packStruct(&want, buf, 0) 21 if !ok { 22 t.Fatal("packing failed") 23 } 24 buf = buf[:n] 25 got := dnsQuestion{} 26 n, ok = unpackStruct(&got, buf, 0) 27 if !ok { 28 t.Fatal("unpacking failed") 29 } 30 if n != len(buf) { 31 t.Errorf("unpacked different amount than packed: got n = %d, want = %d", n, len(buf)) 32 } 33 if !reflect.DeepEqual(got, want) { 34 t.Errorf("got = %+v, want = %+v", got, want) 35 } 36 } 37 38 func TestDomainNamePackUnpack(t *testing.T) { 39 tests := []struct { 40 in string 41 want string 42 ok bool 43 }{ 44 {"", ".", true}, 45 {".", ".", true}, 46 {"google..com", "", false}, 47 {"google.com", "google.com.", true}, 48 {"google..com.", "", false}, 49 {"google.com.", "google.com.", true}, 50 {".google.com.", "", false}, 51 {"www..google.com.", "", false}, 52 {"www.google.com.", "www.google.com.", true}, 53 } 54 55 for _, test := range tests { 56 buf := make([]byte, 30) 57 n, ok := packDomainName(test.in, buf, 0) 58 if ok != test.ok { 59 t.Errorf("packing of %s: got ok = %t, want = %t", test.in, ok, test.ok) 60 continue 61 } 62 if !test.ok { 63 continue 64 } 65 buf = buf[:n] 66 got, n, ok := unpackDomainName(buf, 0) 67 if !ok { 68 t.Errorf("unpacking for %s failed", test.in) 69 continue 70 } 71 if n != len(buf) { 72 t.Errorf( 73 "unpacked different amount than packed for %s: got n = %d, want = %d", 74 test.in, 75 n, 76 len(buf), 77 ) 78 } 79 if got != test.want { 80 t.Errorf("unpacking packing of %s: got = %s, want = %s", test.in, got, test.want) 81 } 82 } 83 } 84 85 func TestDNSPackUnpack(t *testing.T) { 86 want := dnsMsg{ 87 question: []dnsQuestion{{ 88 Name: ".", 89 Qtype: dnsTypeAAAA, 90 Qclass: dnsClassINET, 91 }}, 92 answer: []dnsRR{}, 93 ns: []dnsRR{}, 94 extra: []dnsRR{}, 95 } 96 b, ok := want.Pack() 97 if !ok { 98 t.Fatal("packing failed") 99 } 100 var got dnsMsg 101 ok = got.Unpack(b) 102 if !ok { 103 t.Fatal("unpacking failed") 104 } 105 if !reflect.DeepEqual(got, want) { 106 t.Errorf("got = %+v, want = %+v", got, want) 107 } 108 } 109 110 func TestDNSParseSRVReply(t *testing.T) { 111 data, err := hex.DecodeString(dnsSRVReply) 112 if err != nil { 113 t.Fatal(err) 114 } 115 msg := new(dnsMsg) 116 ok := msg.Unpack(data) 117 if !ok { 118 t.Fatal("unpacking packet failed") 119 } 120 _ = msg.String() // exercise this code path 121 if g, e := len(msg.answer), 5; g != e { 122 t.Errorf("len(msg.answer) = %d; want %d", g, e) 123 } 124 for idx, rr := range msg.answer { 125 if g, e := rr.Header().Rrtype, uint16(dnsTypeSRV); g != e { 126 t.Errorf("rr[%d].Header().Rrtype = %d; want %d", idx, g, e) 127 } 128 if _, ok := rr.(*dnsRR_SRV); !ok { 129 t.Errorf("answer[%d] = %T; want *dnsRR_SRV", idx, rr) 130 } 131 } 132 for _, name := range [...]string{ 133 "_xmpp-server._tcp.google.com.", 134 "_XMPP-Server._TCP.Google.COM.", 135 "_XMPP-SERVER._TCP.GOOGLE.COM.", 136 } { 137 _, addrs, err := answer(name, "foo:53", msg, uint16(dnsTypeSRV)) 138 if err != nil { 139 t.Error(err) 140 } 141 if g, e := len(addrs), 5; g != e { 142 t.Errorf("len(addrs) = %d; want %d", g, e) 143 t.Logf("addrs = %#v", addrs) 144 } 145 } 146 // repack and unpack. 147 data2, ok := msg.Pack() 148 msg2 := new(dnsMsg) 149 msg2.Unpack(data2) 150 switch { 151 case !ok: 152 t.Error("failed to repack message") 153 case !reflect.DeepEqual(msg, msg2): 154 t.Error("repacked message differs from original") 155 } 156 } 157 158 func TestDNSParseCorruptSRVReply(t *testing.T) { 159 data, err := hex.DecodeString(dnsSRVCorruptReply) 160 if err != nil { 161 t.Fatal(err) 162 } 163 msg := new(dnsMsg) 164 ok := msg.Unpack(data) 165 if !ok { 166 t.Fatal("unpacking packet failed") 167 } 168 _ = msg.String() // exercise this code path 169 if g, e := len(msg.answer), 5; g != e { 170 t.Errorf("len(msg.answer) = %d; want %d", g, e) 171 } 172 for idx, rr := range msg.answer { 173 if g, e := rr.Header().Rrtype, uint16(dnsTypeSRV); g != e { 174 t.Errorf("rr[%d].Header().Rrtype = %d; want %d", idx, g, e) 175 } 176 if idx == 4 { 177 if _, ok := rr.(*dnsRR_Header); !ok { 178 t.Errorf("answer[%d] = %T; want *dnsRR_Header", idx, rr) 179 } 180 } else { 181 if _, ok := rr.(*dnsRR_SRV); !ok { 182 t.Errorf("answer[%d] = %T; want *dnsRR_SRV", idx, rr) 183 } 184 } 185 } 186 _, addrs, err := answer("_xmpp-server._tcp.google.com.", "foo:53", msg, uint16(dnsTypeSRV)) 187 if err != nil { 188 t.Fatalf("answer: %v", err) 189 } 190 if g, e := len(addrs), 4; g != e { 191 t.Errorf("len(addrs) = %d; want %d", g, e) 192 t.Logf("addrs = %#v", addrs) 193 } 194 } 195 196 func TestDNSParseTXTReply(t *testing.T) { 197 expectedTxt1 := "v=spf1 redirect=_spf.google.com" 198 expectedTxt2 := "v=spf1 ip4:69.63.179.25 ip4:69.63.178.128/25 ip4:69.63.184.0/25 " + 199 "ip4:66.220.144.128/25 ip4:66.220.155.0/24 " + 200 "ip4:69.171.232.0/25 ip4:66.220.157.0/25 " + 201 "ip4:69.171.244.0/24 mx -all" 202 203 replies := []string{dnsTXTReply1, dnsTXTReply2} 204 expectedTxts := []string{expectedTxt1, expectedTxt2} 205 206 for i := range replies { 207 data, err := hex.DecodeString(replies[i]) 208 if err != nil { 209 t.Fatal(err) 210 } 211 212 msg := new(dnsMsg) 213 ok := msg.Unpack(data) 214 if !ok { 215 t.Errorf("test %d: unpacking packet failed", i) 216 continue 217 } 218 219 if len(msg.answer) != 1 { 220 t.Errorf("test %d: len(rr.answer) = %d; want 1", i, len(msg.answer)) 221 continue 222 } 223 224 rr := msg.answer[0] 225 rrTXT, ok := rr.(*dnsRR_TXT) 226 if !ok { 227 t.Errorf("test %d: answer[0] = %T; want *dnsRR_TXT", i, rr) 228 continue 229 } 230 231 if rrTXT.Txt != expectedTxts[i] { 232 t.Errorf("test %d: Txt = %s; want %s", i, rrTXT.Txt, expectedTxts[i]) 233 } 234 } 235 } 236 237 func TestDNSParseTXTCorruptDataLengthReply(t *testing.T) { 238 replies := []string{dnsTXTCorruptDataLengthReply1, dnsTXTCorruptDataLengthReply2} 239 240 for i := range replies { 241 data, err := hex.DecodeString(replies[i]) 242 if err != nil { 243 t.Fatal(err) 244 } 245 246 msg := new(dnsMsg) 247 ok := msg.Unpack(data) 248 if ok { 249 t.Errorf("test %d: expected to fail on unpacking corrupt packet", i) 250 } 251 } 252 } 253 254 func TestDNSParseTXTCorruptTXTLengthReply(t *testing.T) { 255 replies := []string{dnsTXTCorruptTXTLengthReply1, dnsTXTCorruptTXTLengthReply2} 256 257 for i := range replies { 258 data, err := hex.DecodeString(replies[i]) 259 if err != nil { 260 t.Fatal(err) 261 } 262 263 msg := new(dnsMsg) 264 ok := msg.Unpack(data) 265 // Unpacking should succeed, but we should just get the header. 266 if !ok { 267 t.Errorf("test %d: unpacking packet failed", i) 268 continue 269 } 270 271 if len(msg.answer) != 1 { 272 t.Errorf("test %d: len(rr.answer) = %d; want 1", i, len(msg.answer)) 273 continue 274 } 275 276 rr := msg.answer[0] 277 if _, justHeader := rr.(*dnsRR_Header); !justHeader { 278 t.Errorf("test %d: rr = %T; expected *dnsRR_Header", i, rr) 279 } 280 } 281 } 282 283 func TestIsResponseTo(t *testing.T) { 284 // Sample DNS query. 285 query := dnsMsg{ 286 dnsMsgHdr: dnsMsgHdr{ 287 id: 42, 288 }, 289 question: []dnsQuestion{ 290 { 291 Name: "www.example.com.", 292 Qtype: dnsTypeA, 293 Qclass: dnsClassINET, 294 }, 295 }, 296 } 297 298 resp := query 299 resp.response = true 300 if !resp.IsResponseTo(&query) { 301 t.Error("got false, want true") 302 } 303 304 badResponses := []dnsMsg{ 305 // Different ID. 306 { 307 dnsMsgHdr: dnsMsgHdr{ 308 id: 43, 309 response: true, 310 }, 311 question: []dnsQuestion{ 312 { 313 Name: "www.example.com.", 314 Qtype: dnsTypeA, 315 Qclass: dnsClassINET, 316 }, 317 }, 318 }, 319 320 // Different query name. 321 { 322 dnsMsgHdr: dnsMsgHdr{ 323 id: 42, 324 response: true, 325 }, 326 question: []dnsQuestion{ 327 { 328 Name: "www.google.com.", 329 Qtype: dnsTypeA, 330 Qclass: dnsClassINET, 331 }, 332 }, 333 }, 334 335 // Different query type. 336 { 337 dnsMsgHdr: dnsMsgHdr{ 338 id: 42, 339 response: true, 340 }, 341 question: []dnsQuestion{ 342 { 343 Name: "www.example.com.", 344 Qtype: dnsTypeAAAA, 345 Qclass: dnsClassINET, 346 }, 347 }, 348 }, 349 350 // Different query class. 351 { 352 dnsMsgHdr: dnsMsgHdr{ 353 id: 42, 354 response: true, 355 }, 356 question: []dnsQuestion{ 357 { 358 Name: "www.example.com.", 359 Qtype: dnsTypeA, 360 Qclass: dnsClassCSNET, 361 }, 362 }, 363 }, 364 365 // No questions. 366 { 367 dnsMsgHdr: dnsMsgHdr{ 368 id: 42, 369 response: true, 370 }, 371 }, 372 373 // Extra questions. 374 { 375 dnsMsgHdr: dnsMsgHdr{ 376 id: 42, 377 response: true, 378 }, 379 question: []dnsQuestion{ 380 { 381 Name: "www.example.com.", 382 Qtype: dnsTypeA, 383 Qclass: dnsClassINET, 384 }, 385 { 386 Name: "www.golang.org.", 387 Qtype: dnsTypeAAAA, 388 Qclass: dnsClassINET, 389 }, 390 }, 391 }, 392 } 393 394 for i := range badResponses { 395 if badResponses[i].IsResponseTo(&query) { 396 t.Errorf("%v: got true, want false", i) 397 } 398 } 399 } 400 401 // Valid DNS SRV reply 402 const dnsSRVReply = "0901818000010005000000000c5f786d70702d736572766572045f74637006676f6f67" + 403 "6c6503636f6d0000210001c00c002100010000012c00210014000014950c786d70702d" + 404 "73657276657234016c06676f6f676c6503636f6d00c00c002100010000012c00210014" + 405 "000014950c786d70702d73657276657232016c06676f6f676c6503636f6d00c00c0021" + 406 "00010000012c00210014000014950c786d70702d73657276657233016c06676f6f676c" + 407 "6503636f6d00c00c002100010000012c00200005000014950b786d70702d7365727665" + 408 "72016c06676f6f676c6503636f6d00c00c002100010000012c00210014000014950c78" + 409 "6d70702d73657276657231016c06676f6f676c6503636f6d00" 410 411 // Corrupt DNS SRV reply, with its final RR having a bogus length 412 // (perhaps it was truncated, or it's malicious) The mutation is the 413 // capital "FF" below, instead of the proper "21". 414 const dnsSRVCorruptReply = "0901818000010005000000000c5f786d70702d736572766572045f74637006676f6f67" + 415 "6c6503636f6d0000210001c00c002100010000012c00210014000014950c786d70702d" + 416 "73657276657234016c06676f6f676c6503636f6d00c00c002100010000012c00210014" + 417 "000014950c786d70702d73657276657232016c06676f6f676c6503636f6d00c00c0021" + 418 "00010000012c00210014000014950c786d70702d73657276657233016c06676f6f676c" + 419 "6503636f6d00c00c002100010000012c00200005000014950b786d70702d7365727665" + 420 "72016c06676f6f676c6503636f6d00c00c002100010000012c00FF0014000014950c78" + 421 "6d70702d73657276657231016c06676f6f676c6503636f6d00" 422 423 // TXT reply with one <character-string> 424 const dnsTXTReply1 = "b3458180000100010004000505676d61696c03636f6d0000100001c00c001000010000012c00" + 425 "201f763d737066312072656469726563743d5f7370662e676f6f676c652e636f6dc00" + 426 "c0002000100025d4c000d036e733406676f6f676c65c012c00c0002000100025d4c00" + 427 "06036e7331c057c00c0002000100025d4c0006036e7333c057c00c0002000100025d4" + 428 "c0006036e7332c057c06c00010001000248b50004d8ef200ac09000010001000248b5" + 429 "0004d8ef220ac07e00010001000248b50004d8ef240ac05300010001000248b50004d" + 430 "8ef260a0000291000000000000000" 431 432 // TXT reply with more than one <character-string>. 433 // See https://tools.ietf.org/html/rfc1035#section-3.3.14 434 const dnsTXTReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + 435 "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + 436 "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + 437 "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + 438 "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" + 439 "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + 440 "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + 441 "f0cc0fd0001000100025d15000445abff0c" 442 443 // DataLength field should be sum of all TXT fields. In this case it's less. 444 const dnsTXTCorruptDataLengthReply1 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + 445 "100000e1000967f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + 446 "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + 447 "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + 448 "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" + 449 "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + 450 "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + 451 "f0cc0fd0001000100025d15000445abff0c" 452 453 // Same as above but DataLength is more than sum of TXT fields. 454 const dnsTXTCorruptDataLengthReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + 455 "100000e1001227f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + 456 "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + 457 "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + 458 "343a36392e3137312e3233322e302f323520692e70343a36362e3232302e3135372e302f32352" + 459 "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + 460 "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + 461 "f0cc0fd0001000100025d15000445abff0c" 462 463 // TXT Length field is less than actual length. 464 const dnsTXTCorruptTXTLengthReply1 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + 465 "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + 466 "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + 467 "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + 468 "343a36392e3137312e3233322e302f323520691470343a36362e3232302e3135372e302f32352" + 469 "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + 470 "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + 471 "f0cc0fd0001000100025d15000445abff0c" 472 473 // TXT Length field is more than actual length. 474 const dnsTXTCorruptTXTLengthReply2 = "a0a381800001000100020002045f7370660866616365626f6f6b03636f6d0000100001c00c0010000" + 475 "100000e1000af7f763d73706631206970343a36392e36332e3137392e3235206970343a36392e" + 476 "36332e3137382e3132382f3235206970343a36392e36332e3138342e302f3235206970343a363" + 477 "62e3232302e3134342e3132382f3235206970343a36362e3232302e3135352e302f3234206970" + 478 "343a36392e3137312e3233322e302f323520693370343a36362e3232302e3135372e302f32352" + 479 "06970343a36392e3137312e3234342e302f3234206d78202d616c6cc0110002000100025d1500" + 480 "070161026e73c011c0110002000100025d1500040162c0ecc0ea0001000100025d15000445abe" + 481 "f0cc0fd0001000100025d15000445abff0c" 482