github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/src/net/dnsmsg_test.go (about)

     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"