a go dns packet parser
1package magna 2 3import ( 4 "bytes" 5 "encoding/binary" 6 "net" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10) 11 12func TestMessageDecode(t *testing.T) { 13 tests := []struct { 14 name string 15 input []byte 16 expected Message 17 wantErr bool 18 }{ 19 { 20 name: "Valid DNS message with one question", 21 input: func() []byte { 22 buf := new(bytes.Buffer) 23 binary.Write(buf, binary.BigEndian, uint16(1234)) 24 binary.Write(buf, binary.BigEndian, uint16(0x0100)) 25 binary.Write(buf, binary.BigEndian, uint16(1)) 26 binary.Write(buf, binary.BigEndian, uint16(0)) 27 binary.Write(buf, binary.BigEndian, uint16(0)) 28 binary.Write(buf, binary.BigEndian, uint16(0)) 29 buf.Write([]byte{3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0}) 30 binary.Write(buf, binary.BigEndian, uint16(1)) 31 binary.Write(buf, binary.BigEndian, uint16(1)) 32 return buf.Bytes() 33 }(), 34 expected: Message{ 35 Header: Header{ 36 ID: 1234, 37 QR: false, 38 RD: true, 39 OPCode: 0, 40 QDCount: 1, 41 }, 42 Question: []Question{ 43 { 44 QName: "www.example.com", 45 QType: 1, 46 QClass: 1, 47 }, 48 }, 49 }, 50 wantErr: false, 51 }, 52 { 53 name: "Valid DNS message with one answer", 54 input: func() []byte { 55 buf := new(bytes.Buffer) 56 binary.Write(buf, binary.BigEndian, uint16(5678)) 57 binary.Write(buf, binary.BigEndian, uint16(0x8180)) 58 binary.Write(buf, binary.BigEndian, uint16(0)) 59 binary.Write(buf, binary.BigEndian, uint16(1)) 60 binary.Write(buf, binary.BigEndian, uint16(0)) 61 binary.Write(buf, binary.BigEndian, uint16(0)) 62 buf.Write([]byte{3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0}) 63 binary.Write(buf, binary.BigEndian, uint16(1)) 64 binary.Write(buf, binary.BigEndian, uint16(1)) 65 binary.Write(buf, binary.BigEndian, uint32(3600)) 66 binary.Write(buf, binary.BigEndian, uint16(4)) 67 binary.Write(buf, binary.BigEndian, uint32(0x0A000001)) 68 return buf.Bytes() 69 }(), 70 expected: Message{ 71 Header: Header{ 72 ID: 5678, 73 QR: true, 74 OPCode: 0, 75 AA: false, 76 RD: true, 77 RA: true, 78 RCode: 0, 79 ANCount: 1, 80 }, 81 Answer: []ResourceRecord{ 82 { 83 Name: "www.example.com", 84 RType: 1, 85 RClass: 1, 86 TTL: 3600, 87 RDLength: 4, 88 RData: &A{net.IP([]byte{10, 0, 0, 1})}, 89 }, 90 }, 91 }, 92 wantErr: false, 93 }, 94 { 95 name: "Invalid input - empty buffer", 96 input: []byte{}, 97 wantErr: true, 98 }, 99 } 100 101 for _, tt := range tests { 102 t.Run(tt.name, func(t *testing.T) { 103 m := &Message{} 104 err := m.Decode(tt.input) 105 106 if tt.wantErr { 107 assert.Error(t, err) 108 } else { 109 assert.NoError(t, err) 110 assert.Equal(t, tt.expected.Header, m.Header) 111 assert.Equal(t, tt.expected.Question, m.Question) 112 assert.Equal(t, tt.expected.Answer, m.Answer) 113 assert.Equal(t, tt.expected.Authority, m.Authority) 114 assert.Equal(t, tt.expected.Additional, m.Additional) 115 } 116 }) 117 } 118} 119 120func FuzzDecodeMessage(f *testing.F) { 121 testcases := [][]byte{ 122 { 123 0x8e, 0x19, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x6e, 0x65, 124 0x77, 0x73, 0x0b, 0x79, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x03, 125 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 126 0x00, 0x00, 0x01, 0x00, 0x04, 0xd1, 0xd8, 0xe6, 0xcf, 127 }, 128 } 129 for _, tc := range testcases { 130 f.Add(tc) 131 } 132 f.Fuzz(func(t *testing.T, msg []byte) { 133 var m Message 134 m.Decode(msg) 135 }) 136}