a go dns packet parser
1package magna 2 3import ( 4 "testing" 5 6 "github.com/stretchr/testify/assert" 7) 8 9func TestQuestionDecode(t *testing.T) { 10 tests := []struct { 11 name string 12 input []byte 13 expectedOffset int 14 expected Question 15 expectedErr error 16 }{ 17 { 18 name: "Valid question - example.com A IN", 19 input: []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 0, 1, 0, 1}, 20 expectedOffset: 17, 21 expected: Question{ 22 QName: "example.com", 23 QType: DNSType(1), 24 QClass: DNSClass(1), 25 }, 26 expectedErr: nil, 27 }, 28 { 29 name: "Valid question - example.com MX CH", 30 input: []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 0, 15, 0, 3}, 31 expectedOffset: 17, 32 expected: Question{ 33 QName: "example.com", 34 QType: DNSType(15), 35 QClass: DNSClass(3), 36 }, 37 expectedErr: nil, 38 }, 39 { 40 name: "Invalid domain name", 41 input: []byte{255, 'i', 'n', 'v', 'a', 'l', 'i', 'd', 0, 0, 1, 0, 1}, 42 expectedOffset: 13, 43 expected: Question{}, 44 expectedErr: &BufferOverflowError{}, 45 }, 46 { 47 name: "Insufficient buffer for QType", 48 input: []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 0}, 49 expectedOffset: 14, 50 expected: Question{QName: "example.com"}, 51 expectedErr: &BufferOverflowError{}, 52 }, 53 { 54 name: "Insufficient buffer for QClass", 55 input: []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 0, 1, 0}, 56 expectedOffset: 16, 57 expected: Question{QName: "example.com", QType: DNSType(1)}, 58 expectedErr: &BufferOverflowError{}, 59 }, 60 } 61 62 for _, tt := range tests { 63 t.Run(tt.name, func(t *testing.T) { 64 q := &Question{} 65 offset, err := q.Decode(tt.input, 0) 66 67 assert.Equal(t, tt.expectedOffset, offset) 68 69 if tt.expectedErr != nil { 70 assert.Error(t, err) 71 assert.IsType(t, tt.expectedErr, err) 72 } else { 73 assert.NoError(t, err) 74 assert.Equal(t, tt.expected, *q) 75 } 76 }) 77 } 78} 79 80func TestQuestionEncode(t *testing.T) { 81 tests := []struct { 82 name string 83 question Question 84 offsets map[string]uint16 85 expected []byte 86 }{ 87 { 88 name: "Simple domain - example.com A IN", 89 question: Question{ 90 QName: "example.com", 91 QType: DNSType(1), 92 QClass: DNSClass(1), 93 }, 94 offsets: make(map[string]uint16), 95 expected: []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 0, 1, 0, 1}, 96 }, 97 { 98 name: "Subdomain - subdomain.example.com AAAA IN", 99 question: Question{ 100 QName: "subdomain.example.com", 101 QType: DNSType(28), 102 QClass: DNSClass(1), 103 }, 104 offsets: make(map[string]uint16), 105 expected: []byte{9, 's', 'u', 'b', 'd', 'o', 'm', 'a', 'i', 'n', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 0, 28, 0, 1}, 106 }, 107 { 108 name: "Different class - example.com MX CH", 109 question: Question{ 110 QName: "example.com", 111 QType: DNSType(15), 112 QClass: DNSClass(3), 113 }, 114 offsets: make(map[string]uint16), 115 expected: []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 0, 15, 0, 3}, 116 }, 117 { 118 name: "Domain compression - example.com after subdomain.example.com", 119 question: Question{ 120 QName: "example.com", 121 QType: DNSType(1), 122 QClass: DNSClass(1), 123 }, 124 offsets: map[string]uint16{ 125 "com": 22, 126 "example.com": 19, 127 }, 128 expected: []byte{0xC0, 0x13, 0x00, 0x01, 0x00, 0x01}, 129 }, 130 } 131 132 for _, tt := range tests { 133 t.Run(tt.name, func(t *testing.T) { 134 result := tt.question.Encode(nil, &tt.offsets) 135 assert.Equal(t, tt.expected, result) 136 137 if len(tt.offsets) == 0 { 138 expectedOffsets := map[string]uint16{ 139 tt.question.QName: 0, 140 } 141 for i := 0; i < len(tt.question.QName); i++ { 142 if tt.question.QName[i] == '.' { 143 expectedOffsets[tt.question.QName[i+1:]] = uint16(i + 1) 144 } 145 } 146 assert.Equal(t, expectedOffsets, tt.offsets) 147 } 148 }) 149 } 150} 151 152func TestQuestionEncodeDecodeRoundTrip(t *testing.T) { 153 tests := []struct { 154 name string 155 question Question 156 }{ 157 { 158 name: "Simple domain - example.com A IN", 159 question: Question{ 160 QName: "example.com", 161 QType: DNSType(1), 162 QClass: DNSClass(1), 163 }, 164 }, 165 { 166 name: "Subdomain - subdomain.example.com AAAA IN", 167 question: Question{ 168 QName: "subdomain.example.com", 169 QType: DNSType(28), 170 QClass: DNSClass(1), 171 }, 172 }, 173 { 174 name: "Different class - example.com MX CH", 175 question: Question{ 176 QName: "example.com", 177 QType: DNSType(15), 178 QClass: DNSClass(3), 179 }, 180 }, 181 } 182 183 for _, tt := range tests { 184 t.Run(tt.name, func(t *testing.T) { 185 offsets := make(map[string]uint16) 186 encoded := tt.question.Encode(nil, &offsets) 187 188 decodedQuestion := &Question{} 189 _, err := decodedQuestion.Decode(encoded, 0) 190 191 assert.NoError(t, err) 192 assert.Equal(t, tt.question, *decodedQuestion) 193 }) 194 } 195} 196 197func TestQuestionEncodeWithExistingBuffer(t *testing.T) { 198 question := Question{ 199 QName: "example.com", 200 QType: DNSType(1), 201 QClass: DNSClass(1), 202 } 203 204 existingBuffer := []byte{0xFF, 0xFF, 0xFF, 0xFF} 205 offsets := make(map[string]uint16) 206 207 result := question.Encode(existingBuffer, &offsets) 208 209 expected := append( 210 existingBuffer, 211 []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 0, 1, 0, 1}..., 212 ) 213 214 assert.Equal(t, expected, result) 215} 216 217func TestQuestionEncodeLongDomainName(t *testing.T) { 218 longLabel := make([]byte, 63) 219 for i := range longLabel { 220 longLabel[i] = 'a' 221 } 222 longDomainName := string(longLabel) + "." + string(longLabel) + "." + string(longLabel) + "." + string(longLabel[:61]) 223 224 question := Question{ 225 QName: longDomainName, 226 QType: DNSType(1), 227 QClass: DNSClass(1), 228 } 229 230 offsets := make(map[string]uint16) 231 encoded := question.Encode(nil, &offsets) 232 233 assert.Equal(t, 259, len(encoded)) 234 235 decodedQuestion := &Question{} 236 _, err := decodedQuestion.Decode(encoded, 0) 237 238 assert.NoError(t, err) 239 assert.Equal(t, question, *decodedQuestion) 240}