/src/ndpi/src/lib/protocols/dns.c
Line | Count | Source |
1 | | /* |
2 | | * dns.c |
3 | | * |
4 | | * Copyright (C) 2012-22 - ntop.org |
5 | | * |
6 | | * This file is part of nDPI, an open source deep packet inspection |
7 | | * library based on the OpenDPI and PACE technology by ipoque GmbH |
8 | | * |
9 | | * nDPI is free software: you can redistribute it and/or modify |
10 | | * it under the terms of the GNU Lesser General Public License as published by |
11 | | * the Free Software Foundation, either version 3 of the License, or |
12 | | * (at your option) any later version. |
13 | | * |
14 | | * nDPI is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | * GNU Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public License |
20 | | * along with nDPI. If not, see <http://www.gnu.org/licenses/>. |
21 | | * |
22 | | */ |
23 | | |
24 | | #include "ndpi_protocol_ids.h" |
25 | | |
26 | | #define NDPI_CURRENT_PROTO NDPI_PROTOCOL_DNS |
27 | | |
28 | | #include "ndpi_api.h" |
29 | | #include "ndpi_private.h" |
30 | | |
31 | 279k | #define FLAGS_MASK 0x8000 |
32 | | |
33 | | /* #define DNS_DEBUG 1 */ |
34 | | |
35 | 19.3M | #define DNS_PORT 53 |
36 | 6.94M | #define LLMNR_PORT 5355 |
37 | 21.1M | #define MDNS_PORT 5353 |
38 | | |
39 | 151k | #define PKT_LEN_ALERT 512 |
40 | | |
41 | | |
42 | | static void search_dns(struct ndpi_detection_module_struct *ndpi_struct, |
43 | | struct ndpi_flow_struct *flow); |
44 | | |
45 | | /* *********************************************** */ |
46 | | |
47 | | static void ndpi_check_dns_type(struct ndpi_detection_module_struct *ndpi_struct, |
48 | | struct ndpi_flow_struct *flow, |
49 | 327k | u_int16_t dns_type) { |
50 | | /* https://en.wikipedia.org/wiki/List_of_DNS_record_types */ |
51 | | |
52 | 327k | switch(dns_type) { |
53 | | /* Obsolete record types */ |
54 | 896 | case 3: |
55 | 2.44k | case 4: |
56 | 2.72k | case 254: |
57 | 3.22k | case 7: |
58 | 5.67k | case 8: |
59 | 6.25k | case 9: |
60 | 6.91k | case 14: |
61 | 7.38k | case 253: |
62 | 7.83k | case 11: |
63 | | /* case 33: */ /* SRV */ |
64 | 8.60k | case 10: |
65 | 8.98k | case 38: |
66 | 9.65k | case 30: |
67 | 10.0k | case 25: |
68 | 10.8k | case 24: |
69 | 11.1k | case 13: |
70 | 11.8k | case 17: |
71 | 12.7k | case 19: |
72 | 13.4k | case 20: |
73 | 14.0k | case 21: |
74 | 15.2k | case 22: |
75 | 15.6k | case 23: |
76 | 16.1k | case 26: |
77 | 16.7k | case 31: |
78 | 17.2k | case 32: |
79 | 17.7k | case 34: |
80 | 18.3k | case 42: |
81 | 18.8k | case 40: |
82 | 19.4k | case 27: |
83 | 20.3k | case 100: |
84 | 20.8k | case 101: |
85 | 21.1k | case 102: |
86 | 21.6k | case 103: |
87 | 22.0k | case 99: |
88 | 22.3k | case 56: |
89 | 22.6k | case 57: |
90 | 23.2k | case 58: |
91 | 23.4k | case 104: |
92 | 23.9k | case 105: |
93 | 24.6k | case 106: |
94 | 25.1k | case 107: |
95 | 25.5k | case 259: |
96 | 25.5k | ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_SUSPICIOUS_TRAFFIC, "Obsolete DNS record type"); |
97 | 25.5k | break; |
98 | 327k | } |
99 | 327k | } |
100 | | |
101 | | /* *********************************************** */ |
102 | | |
103 | 497k | static u_int16_t checkPort(u_int16_t port) { |
104 | 497k | switch(port) { |
105 | 216k | case DNS_PORT: |
106 | 216k | return(NDPI_PROTOCOL_DNS); |
107 | 19.8k | case LLMNR_PORT: |
108 | 19.8k | return(NDPI_PROTOCOL_LLMNR); |
109 | 25.0k | case MDNS_PORT: |
110 | 25.0k | return(NDPI_PROTOCOL_MDNS); |
111 | 497k | } |
112 | | |
113 | 235k | return(0); |
114 | 497k | } |
115 | | |
116 | | /* *********************************************** */ |
117 | | |
118 | | static int isMDNSMulticastAddress(struct ndpi_packet_struct * const packet) |
119 | 11.1k | { |
120 | 11.1k | return (packet->iph && ntohl(packet->iph->daddr) == 0xE00000FB /* multicast: 224.0.0.251 */) || |
121 | 7.40k | (packet->iphv6 && ntohl(packet->iphv6->ip6_dst.u6_addr.u6_addr32[0]) == 0xFF020000 && |
122 | 7.40k | ntohl(packet->iphv6->ip6_dst.u6_addr.u6_addr32[1]) == 0x00000000 && |
123 | 7.40k | ntohl(packet->iphv6->ip6_dst.u6_addr.u6_addr32[2]) == 0x00000000 && |
124 | 7.40k | ntohl(packet->iphv6->ip6_dst.u6_addr.u6_addr32[3]) == 0x000000FB /* multicast: FF02::FB */); |
125 | 11.1k | } |
126 | | |
127 | | static int isLLMNRMulticastAddress(struct ndpi_packet_struct *const packet) |
128 | 23.2k | { |
129 | 23.2k | return (packet->iph && ntohl(packet->iph->daddr) == 0xE00000FC /* multicast: 224.0.0.252 */) || |
130 | 12.9k | (packet->iphv6 && ntohl(packet->iphv6->ip6_dst.u6_addr.u6_addr32[0]) == 0xFF020000 && |
131 | 12.9k | ntohl(packet->iphv6->ip6_dst.u6_addr.u6_addr32[1]) == 0x00000000 && |
132 | 12.9k | ntohl(packet->iphv6->ip6_dst.u6_addr.u6_addr32[2]) == 0x00000000 && |
133 | 12.9k | ntohl(packet->iphv6->ip6_dst.u6_addr.u6_addr32[3]) == 0x00010003 /* multicast: FF02::1:3 */); |
134 | 23.2k | } |
135 | | |
136 | | /* *********************************************** */ |
137 | | |
138 | 261k | static u_int16_t checkDNSSubprotocol(u_int16_t sport, u_int16_t dport) { |
139 | 261k | u_int16_t rc = checkPort(sport); |
140 | | |
141 | 261k | if(rc == 0) |
142 | 235k | return(checkPort(dport)); |
143 | 25.6k | else |
144 | 25.6k | return(rc); |
145 | 261k | } |
146 | | |
147 | | /* *********************************************** */ |
148 | | |
149 | 997k | static u_int16_t get16(u_int *i, const u_int8_t *payload) { |
150 | 997k | u_int16_t v = *(u_int16_t*)&payload[*i]; |
151 | | |
152 | 997k | (*i) += 2; |
153 | | |
154 | 997k | return(ntohs(v)); |
155 | 997k | } |
156 | | |
157 | | /* *********************************************** */ |
158 | | |
159 | 1.60M | static u_int getNameLength(u_int i, const u_int8_t *payload, u_int payloadLen) { |
160 | 1.60M | if(i >= payloadLen) |
161 | 46.8k | return(0); |
162 | 1.56M | else if(payload[i] == 0x00) |
163 | 398k | return(1); |
164 | 1.16M | else if((payload[i] & 0xC0)== 0xC0) |
165 | 279k | return(2); |
166 | 883k | else { |
167 | 883k | u_int8_t len = payload[i]; |
168 | 883k | u_int8_t off = len + 1; |
169 | | |
170 | 883k | return(off + getNameLength(i+off, payload, payloadLen)); |
171 | 883k | } |
172 | 1.60M | } |
173 | | /* |
174 | | See |
175 | | - RFC 1035 |
176 | | - https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/naming-conventions-for-computer-domain-site-ou |
177 | | |
178 | | Allowed chars for dns names A-Z 0-9 _ - |
179 | | Perl script for generation map: |
180 | | my @M; |
181 | | for(my $ch=0; $ch < 256; $ch++) { |
182 | | $M[$ch >> 5] |= 1 << ($ch & 0x1f) if chr($ch) =~ /[a-z0-9_-]/i; |
183 | | } |
184 | | print join(',', map { sprintf "0x%08x",$_ } @M),"\n"; |
185 | | */ |
186 | | |
187 | | static uint32_t dns_validchar[8] = { |
188 | | 0x00000000,0x03ff2000,0x87fffffe,0x07fffffe,0,0,0,0 |
189 | | }; |
190 | | |
191 | | /* *********************************************** */ |
192 | | |
193 | 5.89k | static char* dns_error_code2string(u_int16_t error_code, char *buf, u_int buf_len) { |
194 | 5.89k | switch(error_code) { |
195 | 1.09k | case 1: return((char*)"FORMERR"); |
196 | 693 | case 2: return((char*)"SERVFAIL"); |
197 | 1.93k | case 3: return((char*)"NXDOMAIN"); |
198 | 329 | case 4: return((char*)"NOTIMP"); |
199 | 68 | case 5: return((char*)"REFUSED"); |
200 | 42 | case 6: return((char*)"YXDOMAIN"); |
201 | 106 | case 7: return((char*)"XRRSET"); |
202 | 82 | case 8: return((char*)"NOTAUTH"); |
203 | 91 | case 9: return((char*)"NOTZONE"); |
204 | | |
205 | 1.45k | default: |
206 | 1.45k | snprintf(buf, buf_len, "%u", error_code); |
207 | 1.45k | return(buf); |
208 | 5.89k | } |
209 | 5.89k | } |
210 | | |
211 | | /* *********************************************** */ |
212 | | |
213 | 4.85M | u_int64_t fpc_dns_cache_key_from_flow(struct ndpi_flow_struct *flow) { |
214 | 4.85M | u_int64_t key; |
215 | | |
216 | 4.85M | if(flow->is_ipv6) |
217 | 139k | key = ndpi_quick_hash64((const char *)flow->s_address.v6, 16); |
218 | 4.71M | else |
219 | 4.71M | key = (u_int64_t)(flow->s_address.v4); |
220 | | |
221 | 4.85M | return key; |
222 | 4.85M | } |
223 | | |
224 | | /* *********************************************** */ |
225 | | |
226 | 99.1k | static u_int64_t fpc_dns_cache_key_from_packet(const unsigned char *ip, int ip_len) { |
227 | 99.1k | u_int64_t key; |
228 | | |
229 | 99.1k | if(ip_len == 16) |
230 | 1.92k | key = ndpi_quick_hash64((const char *)ip, 16); |
231 | 97.2k | else |
232 | 97.2k | key = (u_int64_t)(*(u_int32_t *)ip); |
233 | | |
234 | 99.1k | return key; |
235 | 99.1k | } |
236 | | |
237 | | /* *********************************************** */ |
238 | | |
239 | | static u_int8_t ndpi_grab_dns_name(struct ndpi_packet_struct *packet, |
240 | | u_int *off /* payload offset */, |
241 | | char *_hostname, u_int max_len, |
242 | | u_int *_hostname_len, |
243 | 244k | u_int8_t ignore_checks) { |
244 | 244k | u_int8_t hostname_is_valid = 1; |
245 | 244k | u_int j = 0; |
246 | | |
247 | 244k | max_len--; |
248 | | |
249 | 932k | while((j < max_len) |
250 | 931k | && ((*off) < packet->payload_packet_len) |
251 | 931k | && (packet->payload[(*off)] != '\0')) { |
252 | 737k | u_int8_t c, cl = packet->payload[*off]; |
253 | | |
254 | 737k | if(((cl & 0xc0) != 0) || // we not support compressed names in query |
255 | 693k | (((*off)+1) + cl >= packet->payload_packet_len)) { |
256 | | /* Don't update the offset */ |
257 | 49.9k | j = 0; |
258 | 49.9k | break; |
259 | 49.9k | } |
260 | | |
261 | 687k | (*off)++; |
262 | | |
263 | 687k | if(j && (j < max_len)) _hostname[j++] = '.'; |
264 | | |
265 | 4.53M | while((j < max_len) && (cl != 0)) { |
266 | 3.84M | c = packet->payload[(*off)++]; |
267 | | |
268 | 3.84M | if(ignore_checks) |
269 | 419k | _hostname[j++] = tolower(c); |
270 | 3.43M | else { |
271 | 3.43M | u_int32_t shift; |
272 | | |
273 | 3.43M | shift = ((u_int32_t) 1) << (c & 0x1f); |
274 | | |
275 | 3.43M | if((dns_validchar[c >> 5] & shift)) { |
276 | 3.10M | _hostname[j++] = tolower(c); |
277 | 3.10M | } else { |
278 | | /* printf("---?? '%c'\n", c); */ |
279 | | |
280 | 321k | hostname_is_valid = 0; |
281 | | |
282 | 321k | if (ndpi_isprint(c) == 0) { |
283 | 272k | _hostname[j++] = '?'; |
284 | 272k | } else { |
285 | 48.6k | _hostname[j++] = '_'; |
286 | 48.6k | } |
287 | 321k | } |
288 | 3.43M | } |
289 | | |
290 | 3.84M | cl--; |
291 | 3.84M | } |
292 | 687k | } |
293 | | |
294 | 244k | _hostname[j] = '\0', *_hostname_len = j; |
295 | | |
296 | 244k | return(hostname_is_valid); |
297 | 244k | } |
298 | | |
299 | | /* *********************************************** */ |
300 | | |
301 | | static int process_queries(struct ndpi_detection_module_struct *ndpi_struct, |
302 | | struct ndpi_flow_struct *flow, |
303 | | struct ndpi_dns_packet_header *dns_header, |
304 | 246k | u_int payload_offset) { |
305 | 246k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
306 | 246k | u_int x = payload_offset; |
307 | 246k | u_int16_t rsp_type; |
308 | 246k | u_int16_t num; |
309 | | |
310 | 471k | for(num = 0; num < dns_header->num_queries; num++) { |
311 | 261k | u_int16_t data_len; |
312 | | |
313 | 261k | if((data_len = getNameLength(x, packet->payload, |
314 | 261k | packet->payload_packet_len)) == 0) { |
315 | 643 | return -1; |
316 | 643 | } else |
317 | 260k | x += data_len; |
318 | | |
319 | 260k | if(data_len > 253) |
320 | 2.95k | ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid DNS Query Lenght"); |
321 | | |
322 | 260k | if((x+4) > packet->payload_packet_len) { |
323 | 34.7k | ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid DNS Query Lenght"); |
324 | 34.7k | return -1; |
325 | 34.7k | } |
326 | | |
327 | 225k | rsp_type = get16(&x, packet->payload); |
328 | | |
329 | | #ifdef DNS_DEBUG |
330 | | printf("[DNS] [response (query)] response_type=%d\n", rsp_type); |
331 | | #endif |
332 | 225k | if(flow->protos.dns.query_type == 0) { |
333 | | /* In case we missed the query packet... */ |
334 | 130k | flow->protos.dns.query_type = rsp_type; |
335 | 130k | } |
336 | | |
337 | | /* here x points to the response "class" field */ |
338 | 225k | x += 2; /* Skip class */ |
339 | 225k | } |
340 | | |
341 | 210k | return x; |
342 | 246k | } |
343 | | |
344 | | static int process_answers(struct ndpi_detection_module_struct *ndpi_struct, |
345 | | struct ndpi_flow_struct *flow, |
346 | | struct ndpi_dns_packet_header *dns_header, |
347 | | u_int payload_offset, |
348 | 73.4k | ndpi_master_app_protocol *proto) { |
349 | 73.4k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
350 | 73.4k | u_int x = payload_offset; |
351 | 73.4k | u_int16_t rsp_type; |
352 | 73.4k | u_int32_t rsp_ttl; |
353 | 73.4k | u_int16_t num; |
354 | 73.4k | u_int8_t found = 0; |
355 | 73.4k | int ignore_checks; |
356 | | |
357 | 73.4k | ignore_checks = (proto->master_protocol == NDPI_PROTOCOL_MDNS); |
358 | | |
359 | 407k | for(num = 0; num < dns_header->num_answers; num++) { |
360 | 359k | u_int16_t data_len; |
361 | | |
362 | 359k | if((data_len = getNameLength(x, packet->payload, |
363 | 359k | packet->payload_packet_len)) == 0) { |
364 | 824 | return -1; |
365 | 824 | } else |
366 | 359k | x += data_len; |
367 | | |
368 | 359k | if((x+8) >= packet->payload_packet_len) { |
369 | 13.6k | return -1; |
370 | 13.6k | } |
371 | | |
372 | 345k | rsp_type = get16(&x, packet->payload); |
373 | 345k | rsp_ttl = ntohl(*((u_int32_t*)&packet->payload[x+2])); |
374 | | |
375 | 345k | if(rsp_ttl == 0) |
376 | 63.0k | ndpi_set_risk(ndpi_struct, flow, NDPI_MINOR_ISSUES, "DNS Record with zero TTL"); |
377 | | |
378 | | #ifdef DNS_DEBUG |
379 | | printf("[DNS] Date len %u; TTL = %u\n", data_len, rsp_ttl); |
380 | | printf("[DNS] [response] response_type=%d\n", rsp_type); |
381 | | #endif |
382 | | |
383 | 345k | if(found == 0) { |
384 | 327k | ndpi_check_dns_type(ndpi_struct, flow, rsp_type); |
385 | 327k | flow->protos.dns.rsp_type = rsp_type; |
386 | 327k | } |
387 | | |
388 | | /* x points to the response "class" field */ |
389 | 345k | if((x+12) <= packet->payload_packet_len) { |
390 | 341k | u_int32_t ttl = ntohl(*((u_int32_t*)&packet->payload[x+2])); |
391 | | |
392 | 341k | x += 6; |
393 | 341k | data_len = get16(&x, packet->payload); |
394 | | |
395 | 341k | if((x + data_len) <= packet->payload_packet_len) { |
396 | | #ifdef DNS_DEBUG |
397 | | printf("[DNS] [rsp_type: %u][data_len: %u]\n", rsp_type, data_len); |
398 | | #endif |
399 | | |
400 | 276k | if(rsp_type == 0x05 /* CNAME */) { |
401 | 30.3k | ; |
402 | 245k | } else if(rsp_type == 0x0C /* PTR */) { |
403 | 8.55k | u_int16_t ptr_len = (packet->payload[x-2] << 8) + packet->payload[x-1]; |
404 | | |
405 | 8.55k | if((x + ptr_len) <= packet->payload_packet_len) { |
406 | 8.55k | if(found == 0) { |
407 | 5.00k | u_int len, orig_x; |
408 | | |
409 | 5.00k | orig_x = x; |
410 | 5.00k | ndpi_grab_dns_name(packet, &x, |
411 | 5.00k | flow->protos.dns.ptr_domain_name, |
412 | 5.00k | sizeof(flow->protos.dns.ptr_domain_name), &len, |
413 | 5.00k | ignore_checks); |
414 | | /* ndpi_grab_dns_name doesn't update the offset if it failed. |
415 | | We unconditionally update it at the end of the for loop */ |
416 | 5.00k | x = orig_x; |
417 | 5.00k | found = 1; |
418 | 5.00k | } |
419 | 8.55k | } |
420 | 237k | } else if((((rsp_type == 0x1) && (data_len == 4)) /* A */ |
421 | 100k | || ((rsp_type == 0x1c) && (data_len == 16)) /* AAAA */ |
422 | 237k | )) { |
423 | 140k | if(found == 0) { |
424 | | |
425 | 136k | if(flow->protos.dns.num_rsp_addr < MAX_NUM_DNS_RSP_ADDRESSES) { |
426 | | /* Necessary for IP address comparison */ |
427 | 136k | memset(&flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr], 0, sizeof(ndpi_ip_addr_t)); |
428 | | |
429 | 136k | memcpy(&flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr], packet->payload + x, data_len); |
430 | 136k | flow->protos.dns.is_rsp_addr_ipv6[flow->protos.dns.num_rsp_addr] = (data_len == 16) ? 1 : 0; |
431 | 136k | flow->protos.dns.rsp_addr_ttl[flow->protos.dns.num_rsp_addr] = ttl; |
432 | | |
433 | 136k | if(ndpi_struct->cfg.address_cache_size) |
434 | 132k | ndpi_cache_address(ndpi_struct, |
435 | 132k | flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr], |
436 | 132k | flow->host_server_name, |
437 | 132k | packet->current_time_ms/1000, |
438 | 132k | flow->protos.dns.rsp_addr_ttl[flow->protos.dns.num_rsp_addr]); |
439 | | |
440 | 136k | if(ndpi_struct->cfg.hostname_dns_check_enabled) |
441 | 129k | ndpi_cache_hostname_ip(ndpi_struct, |
442 | 129k | &flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr], |
443 | 129k | flow->host_server_name); |
444 | | |
445 | 136k | ++flow->protos.dns.num_rsp_addr; |
446 | 136k | } |
447 | | |
448 | 136k | if(flow->protos.dns.num_rsp_addr >= MAX_NUM_DNS_RSP_ADDRESSES) |
449 | 9.22k | found = 1; |
450 | 136k | } |
451 | | |
452 | | /* Add (all addresses) to FPC DNS cache */ |
453 | 140k | if(ndpi_struct->cfg.fpc_enabled && |
454 | 140k | proto->app_protocol != NDPI_PROTOCOL_UNKNOWN && |
455 | 99.9k | proto->app_protocol != proto->master_protocol && |
456 | 99.2k | ndpi_struct->fpc_dns_cache) { |
457 | 99.1k | ndpi_lru_add_to_cache(ndpi_struct->fpc_dns_cache, |
458 | 99.1k | fpc_dns_cache_key_from_packet(packet->payload + x, data_len), |
459 | 99.1k | proto->app_protocol, |
460 | 99.1k | ndpi_get_current_time(flow)); |
461 | | |
462 | 99.1k | NDPI_LOG_DBG(ndpi_struct, "Adding entry to fpc_dns: %s proto %d\n", |
463 | 99.1k | data_len == 4 ? "ipv4" : "ipv6", proto->app_protocol); |
464 | 99.1k | } |
465 | 140k | } |
466 | | |
467 | 276k | x += data_len; |
468 | 276k | } |
469 | 341k | } |
470 | | |
471 | 345k | if(found && (dns_header->additional_rrs == 0)) { |
472 | | /* |
473 | | In case we have RR we need to iterate |
474 | | all the answers and not just consider the |
475 | | first one as we need to properly move 'x' |
476 | | to the right offset |
477 | | */ |
478 | 11.5k | break; |
479 | 11.5k | } |
480 | 345k | } |
481 | | |
482 | 59.0k | return x; |
483 | 73.4k | } |
484 | | |
485 | | static int process_additionals(struct ndpi_detection_module_struct *ndpi_struct, |
486 | | struct ndpi_flow_struct *flow, |
487 | | struct ndpi_dns_packet_header *dns_header, |
488 | 59.0k | u_int payload_offset) { |
489 | 59.0k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
490 | 59.0k | u_int x = payload_offset; |
491 | | |
492 | | /* |
493 | | Dissect the rest of the packet only if there are |
494 | | additional_rrs as we need to check for: |
495 | | * EDNS(0) |
496 | | * NSID |
497 | | |
498 | | In this case we need to go through the whole packet |
499 | | as we need to update the 'x' offset |
500 | | */ |
501 | | |
502 | 59.0k | if(dns_header->additional_rrs == 0) |
503 | 48.2k | return x; |
504 | | |
505 | 10.7k | if(dns_header->authority_rrs > 0) { |
506 | | #ifdef DNS_DEBUG |
507 | | u_int16_t rsp_type; |
508 | | #endif |
509 | 4.36k | u_int16_t num; |
510 | | |
511 | 28.5k | for(num = 0; num < dns_header->authority_rrs; num++) { |
512 | 26.1k | u_int16_t data_len; |
513 | | |
514 | 26.1k | if((x+6) >= packet->payload_packet_len) { |
515 | 450 | return -1; |
516 | 450 | } |
517 | | |
518 | 25.6k | if((data_len = getNameLength(x, packet->payload, |
519 | 25.6k | packet->payload_packet_len)) == 0) { |
520 | 0 | return -1; |
521 | 0 | } else |
522 | 25.6k | x += data_len; |
523 | | |
524 | 25.6k | if((x+8) >= packet->payload_packet_len) { |
525 | 1.46k | return -1; |
526 | 1.46k | } |
527 | | |
528 | | /* To avoid warning: variable ‘rsp_type’ set but not used [-Wunused-but-set-variable] */ |
529 | | #ifdef DNS_DEBUG |
530 | | rsp_type = get16(&x, packet->payload); |
531 | | #else |
532 | 24.1k | get16(&x, packet->payload); |
533 | 24.1k | #endif |
534 | | |
535 | | #ifdef DNS_DEBUG |
536 | | printf("[DNS] [RRS response] response_type=%d\n", rsp_type); |
537 | | #endif |
538 | | |
539 | | /* here x points to the response "class" field */ |
540 | 24.1k | if((x+12) <= packet->payload_packet_len) { |
541 | 23.4k | x += 6; |
542 | 23.4k | data_len = get16(&x, packet->payload); |
543 | | |
544 | 23.4k | if((x + data_len) <= packet->payload_packet_len) |
545 | 17.1k | x += data_len; |
546 | 23.4k | } |
547 | 24.1k | } |
548 | 4.36k | } |
549 | | |
550 | 8.81k | if(dns_header->additional_rrs > 0) { |
551 | 8.81k | u_int16_t rsp_type; |
552 | 8.81k | u_int16_t num; |
553 | | |
554 | 46.2k | for(num = 0; num < dns_header->additional_rrs; num++) { |
555 | 41.7k | u_int16_t data_len, rdata_len, opt_code, opt_len; |
556 | 41.7k | const unsigned char *opt; |
557 | | |
558 | | #ifdef DNS_DEBUG |
559 | | printf("[DNS] [RR response %d/%d]\n", num + 1, dns_header->additional_rrs); |
560 | | #endif |
561 | | |
562 | 41.7k | if((x+6) > packet->payload_packet_len) { |
563 | 1.73k | return -1; |
564 | 1.73k | } |
565 | | |
566 | 39.9k | if((data_len = getNameLength(x, packet->payload, packet->payload_packet_len)) == 0) { |
567 | 0 | return -1; |
568 | 0 | } else |
569 | 39.9k | x += data_len; |
570 | | |
571 | 39.9k | if((x+10) > packet->payload_packet_len) { |
572 | 2.51k | return -1; |
573 | 2.51k | } |
574 | | |
575 | 37.4k | rsp_type = get16(&x, packet->payload); |
576 | | |
577 | | #ifdef DNS_DEBUG |
578 | | printf("[DNS] [RR response] response_type=%d\n", rsp_type); |
579 | | #endif |
580 | | |
581 | 37.4k | if(rsp_type == 41 /* OPT */) { |
582 | | /* https://en.wikipedia.org/wiki/Extension_Mechanisms_for_DNS */ |
583 | 5.31k | flow->protos.dns.edns0_udp_payload_size = ntohs(*((u_int16_t*)&packet->payload[x])); /* EDNS(0) */ |
584 | | |
585 | | #ifdef DNS_DEBUG |
586 | | printf("[DNS] [response] edns0_udp_payload_size: %u\n", flow->protos.dns.edns0_udp_payload_size); |
587 | | #endif |
588 | 5.31k | x += 6; |
589 | | |
590 | 5.31k | rdata_len = ntohs(*((u_int16_t *)&packet->payload[x])); |
591 | | #ifdef DNS_DEBUG |
592 | | printf("[DNS] [response] rdata len: %u\n", rdata_len); |
593 | | #endif |
594 | 5.31k | if(rdata_len > 0 && |
595 | 4.22k | x + 6 <= packet->payload_packet_len) { |
596 | 4.12k | opt_code = ntohs(*((u_int16_t *)&packet->payload[x + 2])); |
597 | 4.12k | opt_len = ntohs(*((u_int16_t *)&packet->payload[x + 4])); |
598 | 4.12k | opt = &packet->payload[x + 6]; |
599 | | /* TODO: parse the TLV list */ |
600 | 4.12k | if(opt_code == 0x03 && |
601 | 2.51k | opt_len <= rdata_len + 4 && |
602 | 2.10k | opt_len > 6 && |
603 | 1.81k | x + 6 + opt_len <= packet->payload_packet_len) { |
604 | | #ifdef DNS_DEBUG |
605 | | printf("[DNS] NSID: [%.*s]\n", opt_len, opt); |
606 | | #endif |
607 | 1.20k | if(memcmp(opt, "gpdns-", 6) == 0) { |
608 | | #ifdef DNS_DEBUG |
609 | | printf("[DNS] NSID Airport code [%.*s]\n", opt_len - 6, opt + 6); |
610 | | #endif |
611 | 805 | memcpy(flow->protos.dns.geolocation_iata_code, opt + 6, |
612 | 805 | ndpi_min(opt_len - 6, (int)sizeof(flow->protos.dns.geolocation_iata_code) - 1)); |
613 | 805 | } |
614 | 1.20k | } |
615 | | |
616 | 4.12k | } |
617 | 32.1k | } else { |
618 | 32.1k | x += 6; |
619 | 32.1k | } |
620 | | |
621 | 37.4k | if((data_len = getNameLength(x, packet->payload, packet->payload_packet_len)) == 0) { |
622 | 0 | return -1; |
623 | 0 | } else |
624 | 37.4k | x += data_len; |
625 | 37.4k | } |
626 | 8.81k | } |
627 | | |
628 | 4.56k | return x; |
629 | 8.81k | } |
630 | | |
631 | | static int is_valid_dns(struct ndpi_detection_module_struct *ndpi_struct, |
632 | | struct ndpi_flow_struct *flow, |
633 | | struct ndpi_dns_packet_header *dns_header, |
634 | 280k | u_int payload_offset, u_int8_t *is_query) { |
635 | 280k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
636 | | |
637 | 280k | if(packet->payload_packet_len < sizeof(struct ndpi_dns_packet_header) + payload_offset) |
638 | 482 | return 0; |
639 | | |
640 | 279k | memcpy(dns_header, (struct ndpi_dns_packet_header*)&packet->payload[payload_offset], |
641 | 279k | sizeof(struct ndpi_dns_packet_header)); |
642 | | |
643 | 279k | dns_header->tr_id = ntohs(dns_header->tr_id); |
644 | 279k | dns_header->flags = ntohs(dns_header->flags); |
645 | 279k | dns_header->num_queries = ntohs(dns_header->num_queries); |
646 | 279k | dns_header->num_answers = ntohs(dns_header->num_answers); |
647 | 279k | dns_header->authority_rrs = ntohs(dns_header->authority_rrs); |
648 | 279k | dns_header->additional_rrs = ntohs(dns_header->additional_rrs); |
649 | | |
650 | 279k | if((dns_header->flags & FLAGS_MASK) == 0x0000) |
651 | 187k | *is_query = 1; |
652 | 92.5k | else |
653 | 92.5k | *is_query = 0; |
654 | | |
655 | 279k | if(*is_query) { |
656 | 187k | if(dns_header->num_queries <= NDPI_MAX_DNS_REQUESTS && |
657 | | /* dns_header->num_answers == 0 && */ |
658 | 169k | ((dns_header->flags & 0x2800) == 0x2800 /* Dynamic DNS Update */ || |
659 | 167k | (dns_header->flags & 0xFCF0) == 0x00 /* Standard Query */ || |
660 | 9.19k | (dns_header->flags & 0xFCFF) == 0x0800 /* Inverse query */ || |
661 | 166k | (dns_header->num_answers == 0 && dns_header->authority_rrs == 0))) { |
662 | | /* This is a good query */ |
663 | 166k | return 1; |
664 | 166k | } |
665 | 187k | } else { |
666 | 92.5k | if(((dns_header->num_queries > 0 && dns_header->num_queries <= NDPI_MAX_DNS_REQUESTS) || /* Don't assume that num_queries must be zero */ |
667 | 15.3k | (checkDNSSubprotocol(ntohs(flow->c_port), ntohs(flow->s_port)) == NDPI_PROTOCOL_MDNS && dns_header->num_queries == 0)) && |
668 | 82.5k | ((dns_header->num_answers > 0 && dns_header->num_answers <= NDPI_MAX_DNS_REQUESTS) || |
669 | 13.9k | (dns_header->authority_rrs > 0 && dns_header->authority_rrs <= NDPI_MAX_DNS_REQUESTS) || |
670 | 9.79k | (dns_header->additional_rrs > 0 && dns_header->additional_rrs <= NDPI_MAX_DNS_REQUESTS) || |
671 | 79.5k | (dns_header->num_answers == 0 && dns_header->authority_rrs == 0 && dns_header->additional_rrs == 0))) { |
672 | | /* This is a good reply */ |
673 | 79.5k | return 1; |
674 | 79.5k | } |
675 | 12.9k | if(dns_header->num_queries == 0 && dns_header->num_answers == 0 && |
676 | 1.10k | dns_header->authority_rrs == 0 && dns_header->additional_rrs == 0 && |
677 | 533 | packet->payload_packet_len == sizeof(struct ndpi_dns_packet_header)) { |
678 | | /* This is a good empty reply */ |
679 | 201 | return 1; |
680 | 201 | } |
681 | 12.9k | } |
682 | 33.8k | return 0; |
683 | 279k | } |
684 | | |
685 | | /* *********************************************** */ |
686 | | |
687 | | static int keep_extra_dissection(struct ndpi_flow_struct *flow) |
688 | 226k | { |
689 | | /* As a general rule, we wait for a valid response |
690 | | (in the ideal world, we want to process the request/response pair) */ |
691 | 226k | return !(!flow->protos.dns.is_query && flow->protos.dns.num_answers != 0); |
692 | 226k | } |
693 | | |
694 | | /* *********************************************** */ |
695 | | |
696 | 111k | static int search_dns_again(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { |
697 | 111k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
698 | | |
699 | 111k | if(packet->tcp_retransmission || packet->payload_packet_len == 0) |
700 | 829 | return keep_extra_dissection(flow); |
701 | | |
702 | | /* possibly dissect the DNS reply */ |
703 | 110k | search_dns(ndpi_struct, flow); |
704 | | |
705 | 110k | return keep_extra_dissection(flow); |
706 | 111k | } |
707 | | |
708 | | /* *********************************************** */ |
709 | | |
710 | | static int process_hostname(struct ndpi_detection_module_struct *ndpi_struct, |
711 | | struct ndpi_flow_struct *flow, |
712 | | struct ndpi_dns_packet_header *dns_header, |
713 | 246k | ndpi_master_app_protocol *proto) { |
714 | 246k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
715 | 246k | char *dot; |
716 | 246k | u_int len, is_mdns, off = sizeof(struct ndpi_dns_packet_header) + (packet->tcp ? 2 : 0); |
717 | 246k | char _hostname[256]; |
718 | 246k | u_int8_t hostname_is_valid; |
719 | | |
720 | 246k | proto->master_protocol = checkDNSSubprotocol(ntohs(flow->c_port), ntohs(flow->s_port)); |
721 | 246k | proto->app_protocol = flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN ? flow->detected_protocol_stack[0] : NDPI_PROTOCOL_UNKNOWN; |
722 | | |
723 | | /* We try to get hostname only from "standard" query/answer */ |
724 | 246k | if(dns_header->num_queries == 0 && dns_header->num_answers == 0) |
725 | 6.68k | return -1; |
726 | | |
727 | 239k | is_mdns = (proto->master_protocol == NDPI_PROTOCOL_MDNS); |
728 | | |
729 | | /* TODO: should we overwrite existing hostname? |
730 | | For the time being, keep the old/current behavior */ |
731 | | |
732 | 239k | hostname_is_valid = ndpi_grab_dns_name(packet, &off, _hostname, sizeof(_hostname), &len, is_mdns); |
733 | | |
734 | | #ifdef DNS_DEBUG |
735 | | printf("[DNS] [%s]\n", _hostname); |
736 | | #endif |
737 | | |
738 | 239k | ndpi_hostname_sni_set(flow, (const u_int8_t *)_hostname, len, is_mdns ? NDPI_HOSTNAME_NORM_LC : NDPI_HOSTNAME_NORM_ALL); |
739 | | |
740 | 239k | if (hostname_is_valid == 0) |
741 | 44.5k | ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, "Invalid chars detected in domain name"); |
742 | | |
743 | | /* Ignore reverse DNS queries */ |
744 | 239k | if(strstr(_hostname, ".in-addr.") == NULL) { |
745 | 232k | dot = strchr(_hostname, '.'); |
746 | | |
747 | 232k | if(dot) { |
748 | 151k | uintptr_t first_element_len = dot - _hostname; |
749 | | |
750 | 151k | if((first_element_len > 48) && (!is_mdns)) { |
751 | | /* |
752 | | The length of the first element in the query is very long |
753 | | and this might be an issue or indicate an exfiltration |
754 | | */ |
755 | | |
756 | 614 | if(ends_with(ndpi_struct, _hostname, "multi.surbl.org") |
757 | 614 | || ends_with(ndpi_struct, _hostname, "spamhaus.org") |
758 | 614 | || ends_with(ndpi_struct, _hostname, "rackcdn.com") |
759 | 614 | || ends_with(ndpi_struct, _hostname, "akamaiedge.net") |
760 | 536 | || ends_with(ndpi_struct, _hostname, "mx-verification.google.com") |
761 | 536 | || ends_with(ndpi_struct, _hostname, "amazonaws.com") |
762 | 614 | ) |
763 | 113 | ; /* Check common domain exceptions [TODO: if the list grows too much use a different datastructure] */ |
764 | 501 | else |
765 | 501 | ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_SUSPICIOUS_TRAFFIC, "Long DNS host name"); |
766 | 614 | } |
767 | 151k | } |
768 | 232k | } |
769 | | |
770 | 239k | if(strlen(flow->host_server_name) > 0) { |
771 | 179k | ndpi_protocol_match_result ret_match; |
772 | | |
773 | 179k | if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) { |
774 | 156k | proto->app_protocol = ndpi_match_host_subprotocol(ndpi_struct, flow, |
775 | 156k | flow->host_server_name, |
776 | 156k | strlen(flow->host_server_name), |
777 | 156k | &ret_match, |
778 | 156k | proto->master_protocol, |
779 | | /* Avoid updating classification if subclassification is disabled */ |
780 | 156k | ndpi_struct->cfg.dns_subclassification_enabled ? 1 : 0); |
781 | 156k | } |
782 | | |
783 | 179k | ndpi_check_dga_name(ndpi_struct, flow, flow->host_server_name, 1, 0, proto->app_protocol != NDPI_PROTOCOL_UNKNOWN); |
784 | 179k | } |
785 | | |
786 | 239k | return 0; |
787 | 246k | } |
788 | | |
789 | 280k | static void search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { |
790 | 280k | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
791 | 280k | int payload_offset = 0; |
792 | 280k | u_int8_t is_query; |
793 | 280k | struct ndpi_dns_packet_header dns_header; |
794 | 280k | u_int off; |
795 | 280k | ndpi_master_app_protocol proto; |
796 | 280k | int rc; |
797 | | |
798 | 280k | if(packet->udp != NULL) { |
799 | 267k | payload_offset = 0; |
800 | 267k | } else if(packet->tcp != NULL) { |
801 | 12.9k | payload_offset = 2; |
802 | 12.9k | } |
803 | | |
804 | 280k | if(!is_valid_dns(ndpi_struct, flow, &dns_header, payload_offset, &is_query)) { |
805 | | #ifdef DNS_DEBUG |
806 | | printf("[DNS] invalid packet\n"); |
807 | | #endif |
808 | 34.2k | if(flow->extra_packets_func == NULL) { |
809 | 24.4k | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
810 | 24.4k | } else { |
811 | 9.79k | ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid DNS Header"); |
812 | 9.79k | } |
813 | 34.2k | return; |
814 | 34.2k | } |
815 | | |
816 | 246k | process_hostname(ndpi_struct, flow, &dns_header, &proto); |
817 | | |
818 | 246k | off = sizeof(struct ndpi_dns_packet_header) + payload_offset; |
819 | | |
820 | 246k | if(is_query) { |
821 | 166k | flow->protos.dns.is_query = 1; |
822 | 166k | flow->protos.dns.transaction_id = dns_header.tr_id; |
823 | | |
824 | 166k | rc = process_queries(ndpi_struct, flow, &dns_header, off); |
825 | | #ifdef DNS_DEBUG |
826 | | if(rc == -1) |
827 | | printf("[DNS] Error queries (query msg)\n"); |
828 | | #endif |
829 | 166k | } else { |
830 | 79.7k | flow->protos.dns.is_query = 0; |
831 | 79.7k | flow->protos.dns.transaction_id = dns_header.tr_id; |
832 | 79.7k | flow->protos.dns.reply_code = dns_header.flags & 0x0F; |
833 | 79.7k | flow->protos.dns.num_queries = dns_header.num_queries; |
834 | 79.7k | flow->protos.dns.num_answers = dns_header.num_answers + dns_header.authority_rrs + dns_header.additional_rrs; |
835 | | |
836 | 79.7k | if(flow->protos.dns.reply_code != 0) { |
837 | 6.76k | if(is_flowrisk_info_enabled(ndpi_struct, NDPI_ERROR_CODE_DETECTED)) { |
838 | 5.89k | char str[32], buf[16]; |
839 | | |
840 | 5.89k | snprintf(str, sizeof(str), "DNS Error Code %s", |
841 | 5.89k | dns_error_code2string(flow->protos.dns.reply_code, buf, sizeof(buf))); |
842 | 5.89k | ndpi_set_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED, str); |
843 | 5.89k | } else { |
844 | 870 | ndpi_set_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED, NULL); |
845 | 870 | } |
846 | 73.0k | } else { |
847 | 73.0k | if(ndpi_isset_risk(flow, NDPI_SUSPICIOUS_DGA_DOMAIN)) { |
848 | 2.19k | ndpi_set_risk(ndpi_struct, flow, NDPI_RISKY_DOMAIN, "DGA Name Query with no Error Code"); |
849 | 2.19k | } |
850 | 73.0k | } |
851 | | |
852 | 79.7k | rc = process_queries(ndpi_struct, flow, &dns_header, off); |
853 | 79.7k | if(rc == -1) { |
854 | | #ifdef DNS_DEBUG |
855 | | printf("[DNS] Error queries (response msg)\n"); |
856 | | #endif |
857 | 73.4k | } else { |
858 | 73.4k | off = rc; |
859 | 73.4k | rc = process_answers(ndpi_struct, flow, &dns_header, off, &proto); |
860 | 73.4k | if(rc == -1) { |
861 | | #ifdef DNS_DEBUG |
862 | | printf("[DNS] Error answers\n"); |
863 | | #endif |
864 | 59.0k | } else { |
865 | 59.0k | off = rc; |
866 | 59.0k | rc = process_additionals(ndpi_struct, flow, &dns_header, off); |
867 | | #ifdef DNS_DEBUG |
868 | | if(rc == -1) |
869 | | printf("[DNS] Error additionals\n"); |
870 | | #endif |
871 | 59.0k | } |
872 | 73.4k | } |
873 | | |
874 | 79.7k | if(proto.master_protocol == NDPI_PROTOCOL_DNS && |
875 | | /* TODO: add support to RFC6891 to avoid some false positives */ |
876 | 73.8k | packet->udp && |
877 | 71.4k | packet->payload_packet_len > PKT_LEN_ALERT && |
878 | 5.04k | packet->payload_packet_len > flow->protos.dns.edns0_udp_payload_size) { |
879 | 4.84k | if(is_flowrisk_info_enabled(ndpi_struct, NDPI_DNS_LARGE_PACKET)) { |
880 | 3.69k | char str[48]; |
881 | | |
882 | 3.69k | snprintf(str, sizeof(str), "%u Bytes DNS Packet", packet->payload_packet_len); |
883 | 3.69k | ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_LARGE_PACKET, str); |
884 | 3.69k | } else { |
885 | 1.14k | ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_LARGE_PACKET, NULL); |
886 | 1.14k | } |
887 | 4.84k | } |
888 | | |
889 | 79.7k | NDPI_LOG_DBG2(ndpi_struct, "Response: [num_queries=%d][num_answers=%d][reply_code=%u][rsp_type=%u][host_server_name=%s]\n", |
890 | 79.7k | flow->protos.dns.num_queries, flow->protos.dns.num_answers, |
891 | 79.7k | flow->protos.dns.reply_code, flow->protos.dns.rsp_type, flow->host_server_name); |
892 | 79.7k | } |
893 | | |
894 | 246k | if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) { |
895 | 115k | if(ndpi_struct->cfg.dns_subclassification_enabled) |
896 | 100k | ndpi_set_detected_protocol(ndpi_struct, flow, proto.app_protocol, proto.master_protocol, NDPI_CONFIDENCE_DPI); |
897 | 15.1k | else |
898 | 15.1k | ndpi_set_detected_protocol(ndpi_struct, flow, |
899 | 15.1k | (proto.master_protocol == NDPI_PROTOCOL_UNKNOWN) ? NDPI_PROTOCOL_DNS : proto.master_protocol, |
900 | 15.1k | NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI); |
901 | 115k | } |
902 | | /* Category is always NDPI_PROTOCOL_CATEGORY_NETWORK, regardless of the subprotocol. Same for the breed */ |
903 | 246k | flow->category = NDPI_PROTOCOL_CATEGORY_NETWORK; |
904 | 246k | flow->breed = NDPI_PROTOCOL_ACCEPTABLE; |
905 | | |
906 | 246k | if(!flow->extra_packets_func && |
907 | 145k | ndpi_struct->cfg.dns_parse_response_enabled && |
908 | | /* We have never triggered extra-dissection for LLMNR. Keep the old behavior */ |
909 | 132k | flow->detected_protocol_stack[0] != NDPI_PROTOCOL_LLMNR && |
910 | 115k | flow->detected_protocol_stack[1] != NDPI_PROTOCOL_LLMNR) { |
911 | 115k | if(keep_extra_dissection(flow)) { |
912 | 87.6k | NDPI_LOG_DBG(ndpi_struct, "Enabling extra dissection\n"); |
913 | 87.6k | flow->max_extra_packets_to_check = 5; |
914 | 87.6k | flow->extra_packets_func = search_dns_again; |
915 | 87.6k | } |
916 | 115k | } |
917 | | |
918 | | /* The bigger packets are usually the replies, but it shouldn't harm |
919 | | to check the requests, too */ |
920 | 246k | if((flow->detected_protocol_stack[0] == NDPI_PROTOCOL_DNS) |
921 | 207k | || (flow->detected_protocol_stack[1] == NDPI_PROTOCOL_DNS)) { |
922 | | |
923 | 207k | if(packet->iph != NULL) { |
924 | | /* IPv4 */ |
925 | 204k | u_int8_t flags = ((u_int8_t*)packet->iph)[6]; |
926 | | |
927 | | /* 0: fragmented; 1: not fragmented */ |
928 | 204k | if((flags & 0x20) |
929 | 202k | || (iph_is_valid_and_not_fragmented(ndpi_struct, packet->iph, packet->l3_packet_len) == 0)) { |
930 | 1.29k | ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_FRAGMENTED, NULL); |
931 | 1.29k | } |
932 | 204k | } else if(packet->iphv6 != NULL) { |
933 | | /* IPv6 */ |
934 | 3.74k | const struct ndpi_ip6_hdrctl *ip6_hdr = &packet->iphv6->ip6_hdr; |
935 | | |
936 | 3.74k | if(ip6_hdr->ip6_un1_nxt == 0x2C /* Next Header: Fragment Header for IPv6 (44) */) { |
937 | 278 | ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_FRAGMENTED, NULL); |
938 | 278 | } |
939 | 3.74k | } |
940 | 207k | } |
941 | 246k | } |
942 | | |
943 | | /* *********************************************** */ |
944 | | |
945 | 4.78M | static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { |
946 | 4.78M | struct ndpi_packet_struct *packet = &ndpi_struct->packet; |
947 | 4.78M | u_int16_t s_port = 0, d_port = 0; |
948 | 4.78M | int payload_offset = 0; |
949 | | |
950 | 4.78M | NDPI_LOG_DBG(ndpi_struct, "search DNS\n"); |
951 | | |
952 | 4.78M | if(packet->udp != NULL) { |
953 | 1.15M | s_port = ntohs(packet->udp->source); |
954 | 1.15M | d_port = ntohs(packet->udp->dest); |
955 | 1.15M | payload_offset = 0; |
956 | | |
957 | | /* For MDNS/LLMNR: If the packet is not a response, dest addr needs to be multicast. */ |
958 | 1.15M | if ((d_port == MDNS_PORT && isMDNSMulticastAddress(packet) == 0) || |
959 | 1.14M | (d_port == LLMNR_PORT && isLLMNRMulticastAddress(packet) == 0)) |
960 | 10.1k | { |
961 | 10.1k | if (packet->payload_packet_len > 5 && |
962 | 10.1k | ntohs(get_u_int16_t(packet->payload, 2)) != 0 && |
963 | 10.1k | ntohs(get_u_int16_t(packet->payload, 4)) != 0) |
964 | 1.60k | { |
965 | 1.60k | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
966 | 1.60k | return; |
967 | 1.60k | } |
968 | 10.1k | } |
969 | 3.63M | } else if(packet->tcp != NULL) { |
970 | 3.63M | s_port = ntohs(packet->tcp->source); |
971 | 3.63M | d_port = ntohs(packet->tcp->dest); |
972 | 3.63M | payload_offset = 2; |
973 | 3.63M | } |
974 | | |
975 | | /* We are able to detect DNS/MDNS/LLMNR only on standard ports (see #1788) */ |
976 | 4.78M | if(!(s_port == DNS_PORT || d_port == DNS_PORT || |
977 | 4.64M | s_port == MDNS_PORT || d_port == MDNS_PORT || |
978 | 4.63M | d_port == LLMNR_PORT)) { |
979 | 4.61M | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
980 | 4.61M | return; |
981 | 4.61M | } |
982 | | |
983 | | /* Since: |
984 | | UDP: every packet must contains a complete/valid DNS message; |
985 | | TCP: we are not able to handle DNS messages spanning multiple TCP packets; |
986 | | we must be able to detect these protocols on the first packet |
987 | | */ |
988 | 174k | if(packet->payload_packet_len < sizeof(struct ndpi_dns_packet_header) + payload_offset) { |
989 | 5.08k | NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow); |
990 | 5.08k | return; |
991 | 5.08k | } |
992 | | |
993 | 169k | search_dns(ndpi_struct, flow); |
994 | 169k | } |
995 | | |
996 | | /* *********************************************** */ |
997 | | |
998 | 14.2k | void init_dns_dissector(struct ndpi_detection_module_struct *ndpi_struct) { |
999 | 14.2k | ndpi_register_dissector("DNS", ndpi_struct, |
1000 | 14.2k | ndpi_search_dns, |
1001 | 14.2k | NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION, |
1002 | 14.2k | 1, NDPI_PROTOCOL_DNS); |
1003 | 14.2k | } |