Home | History | Annotate | Download | only in net
      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