aboutsummaryrefslogtreecommitdiff
path: root/src/lib/protocols/dns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/protocols/dns.c')
-rw-r--r--src/lib/protocols/dns.c1105
1 files changed, 576 insertions, 529 deletions
diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c
index ce991735d..59566d5c7 100644
--- a/src/lib/protocols/dns.c
+++ b/src/lib/protocols/dns.c
@@ -39,8 +39,8 @@
#define PKT_LEN_ALERT 512
-static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct,
- struct ndpi_flow_struct *flow);
+static void search_dns(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow);
/* *********************************************** */
@@ -161,16 +161,13 @@ static u_int getNameLength(u_int i, const u_int8_t *payload, u_int payloadLen) {
return(0);
else if(payload[i] == 0x00)
return(1);
- else if(payload[i] == 0xC0)
+ else if((payload[i] & 0xC0)== 0xC0)
return(2);
else {
u_int8_t len = payload[i];
u_int8_t off = len + 1;
- if(off == 0) /* Bad packet */
- return(0);
- else
- return(off + getNameLength(i+off, payload, payloadLen));
+ return(off + getNameLength(i+off, payload, payloadLen));
}
}
/*
@@ -213,6 +210,32 @@ static char* dns_error_code2string(u_int16_t error_code, char *buf, u_int buf_le
/* *********************************************** */
+u_int64_t fpc_dns_cache_key_from_flow(struct ndpi_flow_struct *flow) {
+ u_int64_t key;
+
+ if(flow->is_ipv6)
+ key = ndpi_quick_hash64((const char *)flow->s_address.v6, 16);
+ else
+ key = (u_int64_t)(flow->s_address.v4);
+
+ return key;
+}
+
+/* *********************************************** */
+
+static u_int64_t fpc_dns_cache_key_from_packet(const unsigned char *ip, int ip_len) {
+ u_int64_t key;
+
+ if(ip_len == 16)
+ key = ndpi_quick_hash64((const char *)ip, 16);
+ else
+ key = (u_int64_t)(*(u_int32_t *)ip);
+
+ return key;
+}
+
+/* *********************************************** */
+
static u_int8_t ndpi_grab_dns_name(struct ndpi_packet_struct *packet,
u_int *off /* payload offset */,
char *_hostname, u_int max_len,
@@ -226,14 +249,17 @@ static u_int8_t ndpi_grab_dns_name(struct ndpi_packet_struct *packet,
while((j < max_len)
&& ((*off) < packet->payload_packet_len)
&& (packet->payload[(*off)] != '\0')) {
- u_int8_t c, cl = packet->payload[(*off)++];
+ u_int8_t c, cl = packet->payload[*off];
if(((cl & 0xc0) != 0) || // we not support compressed names in query
- ((*off) + cl >= packet->payload_packet_len)) {
+ (((*off)+1) + cl >= packet->payload_packet_len)) {
+ /* Don't update the offset */
j = 0;
break;
}
+ (*off)++;
+
if(j && (j < max_len)) _hostname[j++] = '.';
while((j < max_len) && (cl != 0)) {
@@ -272,645 +298,619 @@ static u_int8_t ndpi_grab_dns_name(struct ndpi_packet_struct *packet,
/* *********************************************** */
-static int search_valid_dns(struct ndpi_detection_module_struct *ndpi_struct,
- struct ndpi_flow_struct *flow,
- struct ndpi_dns_packet_header *dns_header,
- u_int payload_offset, u_int8_t *is_query,
- u_int8_t ignore_checks) {
+static int process_queries(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow,
+ struct ndpi_dns_packet_header *dns_header,
+ u_int payload_offset) {
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
u_int x = payload_offset;
+ u_int16_t rsp_type;
+ u_int16_t num;
- memcpy(dns_header, (struct ndpi_dns_packet_header*)&packet->payload[x],
- sizeof(struct ndpi_dns_packet_header));
-
- dns_header->tr_id = ntohs(dns_header->tr_id);
- dns_header->flags = ntohs(dns_header->flags);
- dns_header->num_queries = ntohs(dns_header->num_queries);
- dns_header->num_answers = ntohs(dns_header->num_answers);
- dns_header->authority_rrs = ntohs(dns_header->authority_rrs);
- dns_header->additional_rrs = ntohs(dns_header->additional_rrs);
+ for(num = 0; num < dns_header->num_queries; num++) {
+ u_int16_t data_len;
- x += sizeof(struct ndpi_dns_packet_header);
+ if((data_len = getNameLength(x, packet->payload,
+ packet->payload_packet_len)) == 0) {
+ return -1;
+ } else
+ x += data_len;
- /* 0x0000 QUERY */
- if((dns_header->flags & FLAGS_MASK) == 0x0000)
- *is_query = 1;
- /* 0x8000 RESPONSE */
- else
- *is_query = 0;
+ if(data_len > 253)
+ ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid DNS Query Lenght");
- if(*is_query) {
- /* DNS Request */
- if((dns_header->num_queries <= NDPI_MAX_DNS_REQUESTS)
- // && (dns_header->num_answers == 0)
- && (((dns_header->flags & 0x2800) == 0x2800 /* Dynamic DNS Update */)
- || ((dns_header->flags & 0xFCF0) == 0x00) /* Standard Query */
- || ((dns_header->flags & 0xFCFF) == 0x0800) /* Inverse query */
- || ((dns_header->num_answers == 0) && (dns_header->authority_rrs == 0)))) {
- /* This is a good query */
- while(x+2 < packet->payload_packet_len) {
- if(packet->payload[x] == '\0') {
- x++;
- flow->protos.dns.query_type = get16(&x, packet->payload);
-#ifdef DNS_DEBUG
- NDPI_LOG_DBG2(ndpi_struct, "query_type=%2d\n", flow->protos.dns.query_type);
- printf("[DNS] [request] query_type=%d\n", flow->protos.dns.query_type);
-#endif
- break;
- } else
- x++;
- }
- } else {
- if(flow->detected_protocol_stack[0] != NDPI_PROTOCOL_UNKNOWN)
- ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid DNS Header");
- return(1 /* invalid */);
+ if((x+4) > packet->payload_packet_len) {
+ ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid DNS Query Lenght");
+ return -1;
}
- } else {
- /* DNS Reply */
- if(flow->protos.dns.query_type == 0) {
- /* In case we missed the query packet... */
+ rsp_type = get16(&x, packet->payload);
- while(x+2 < packet->payload_packet_len) {
- if(packet->payload[x] == '\0') {
- x++;
- flow->protos.dns.query_type = get16(&x, packet->payload);
#ifdef DNS_DEBUG
- NDPI_LOG_DBG2(ndpi_struct, "query_type=%2d\n", flow->protos.dns.query_type);
- printf("[DNS] [request] query_type=%d\n", flow->protos.dns.query_type);
+ printf("[DNS] [response (query)] response_type=%d\n", rsp_type);
#endif
- break;
- } else
- x++;
- }
+ if(flow->protos.dns.query_type == 0) {
+ /* In case we missed the query packet... */
+ flow->protos.dns.query_type = rsp_type;
}
- flow->protos.dns.reply_code = dns_header->flags & 0x0F;
+ /* here x points to the response "class" field */
+ x += 2; /* Skip class */
+ }
- if(flow->protos.dns.reply_code != 0) {
- char str[32], buf[16];
+ return x;
+}
- snprintf(str, sizeof(str), "DNS Error Code %s",
- dns_error_code2string(flow->protos.dns.reply_code, buf, sizeof(buf)));
- ndpi_set_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED, str);
- } else {
- if(ndpi_isset_risk(flow, NDPI_SUSPICIOUS_DGA_DOMAIN)) {
- ndpi_set_risk(ndpi_struct, flow, NDPI_RISKY_DOMAIN, "DGA Name Query with no Error Code");
- }
- }
+static int process_answers(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow,
+ struct ndpi_dns_packet_header *dns_header,
+ u_int payload_offset,
+ ndpi_master_app_protocol *proto) {
+ struct ndpi_packet_struct *packet = &ndpi_struct->packet;
+ u_int x = payload_offset;
+ u_int16_t rsp_type;
+ u_int32_t rsp_ttl;
+ u_int16_t num;
+ u_int8_t found = 0;
+ int ignore_checks;
- if((dns_header->num_queries > 0) && (dns_header->num_queries <= NDPI_MAX_DNS_REQUESTS) /* Don't assume that num_queries must be zero */
- && ((((dns_header->num_answers > 0) && (dns_header->num_answers <= NDPI_MAX_DNS_REQUESTS))
- || ((dns_header->authority_rrs > 0) && (dns_header->authority_rrs <= NDPI_MAX_DNS_REQUESTS))
- || ((dns_header->additional_rrs > 0) && (dns_header->additional_rrs <= NDPI_MAX_DNS_REQUESTS))))
- ) {
- /* This is a good reply: we dissect it both for request and response */
-
- if(dns_header->num_queries > 0) {
-#ifdef DNS_DEBUG
- u_int16_t rsp_type;
-#endif
- u_int16_t num;
+ ignore_checks = (proto->master_protocol == NDPI_PROTOCOL_MDNS);
- for(num = 0; num < dns_header->num_queries; num++) {
- u_int16_t data_len;
+ for(num = 0; num < dns_header->num_answers; num++) {
+ u_int16_t data_len;
- if((x+6) >= packet->payload_packet_len) {
- break;
- }
+ if((data_len = getNameLength(x, packet->payload,
+ packet->payload_packet_len)) == 0) {
+ return -1;
+ } else
+ x += data_len;
- if((data_len = getNameLength(x, packet->payload,
- packet->payload_packet_len)) == 0) {
- break;
- } else
- x += data_len;
+ if((x+8) >= packet->payload_packet_len) {
+ return -1;
+ }
- if((x+8) >= packet->payload_packet_len) {
- break;
- }
+ rsp_type = get16(&x, packet->payload);
+ rsp_ttl = ntohl(*((u_int32_t*)&packet->payload[x+2]));
- /* To avoid warning: variable ‘rsp_type’ set but not used [-Wunused-but-set-variable] */
-#ifdef DNS_DEBUG
- rsp_type = get16(&x, packet->payload);
-#else
- get16(&x, packet->payload);
-#endif
+ if(rsp_ttl == 0)
+ ndpi_set_risk(ndpi_struct, flow, NDPI_MINOR_ISSUES, "DNS Record with zero TTL");
#ifdef DNS_DEBUG
- printf("[DNS] [response (query)] response_type=%d\n", rsp_type);
+ printf("[DNS] Date len %u; TTL = %u\n", data_len, rsp_ttl);
+ printf("[DNS] [response] response_type=%d\n", rsp_type);
#endif
- /* here x points to the response "class" field */
- x += 2; /* Skip class */
- }
- }
+ if(found == 0) {
+ ndpi_check_dns_type(ndpi_struct, flow, rsp_type);
+ flow->protos.dns.rsp_type = rsp_type;
+ }
- if(dns_header->num_answers > 0) {
- u_int16_t rsp_type;
- u_int32_t rsp_ttl;
- u_int16_t num;
- u_int8_t found = 0;
+ /* x points to the response "class" field */
+ if((x+12) <= packet->payload_packet_len) {
+ u_int32_t ttl = ntohl(*((u_int32_t*)&packet->payload[x+2]));
- for(num = 0; num < dns_header->num_answers; num++) {
- u_int16_t data_len;
+ x += 6;
+ data_len = get16(&x, packet->payload);
- if((x+6) >= packet->payload_packet_len) {
- break;
- }
+ if((x + data_len) <= packet->payload_packet_len) {
+#ifdef DNS_DEBUG
+ printf("[DNS] [rsp_type: %u][data_len: %u]\n", rsp_type, data_len);
+#endif
- if((data_len = getNameLength(x, packet->payload,
- packet->payload_packet_len)) == 0) {
- break;
- } else
- x += data_len;
+ if(rsp_type == 0x05 /* CNAME */) {
+ ;
+ } else if(rsp_type == 0x0C /* PTR */) {
+ u_int16_t ptr_len = (packet->payload[x-2] << 8) + packet->payload[x-1];
+
+ if((x + ptr_len) <= packet->payload_packet_len) {
+ if(found == 0) {
+ u_int len, orig_x;
+
+ orig_x = x;
+ ndpi_grab_dns_name(packet, &x,
+ flow->protos.dns.ptr_domain_name,
+ sizeof(flow->protos.dns.ptr_domain_name), &len,
+ ignore_checks);
+ /* ndpi_grab_dns_name doesn't update the offset if it failed.
+ We unconditionally update it at the end of the for loop */
+ x = orig_x;
+ found = 1;
+ }
+ }
+ } else if((((rsp_type == 0x1) && (data_len == 4)) /* A */
+ || ((rsp_type == 0x1c) && (data_len == 16)) /* AAAA */
+ )) {
+ if(found == 0) {
+
+ if(flow->protos.dns.num_rsp_addr < MAX_NUM_DNS_RSP_ADDRESSES) {
+ /* Necessary for IP address comparison */
+ memset(&flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr], 0, sizeof(ndpi_ip_addr_t));
+
+ memcpy(&flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr], packet->payload + x, data_len);
+ flow->protos.dns.is_rsp_addr_ipv6[flow->protos.dns.num_rsp_addr] = (data_len == 16) ? 1 : 0;
+ flow->protos.dns.rsp_addr_ttl[flow->protos.dns.num_rsp_addr] = ttl;
+
+ if(ndpi_struct->cfg.address_cache_size)
+ ndpi_cache_address(ndpi_struct,
+ flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr],
+ flow->host_server_name,
+ packet->current_time_ms/1000,
+ flow->protos.dns.rsp_addr_ttl[flow->protos.dns.num_rsp_addr]);
+
+ ++flow->protos.dns.num_rsp_addr;
+ }
+
+ if(flow->protos.dns.num_rsp_addr >= MAX_NUM_DNS_RSP_ADDRESSES)
+ found = 1;
+ }
+
+ /* Add (all addresses) to FPC DNS cache */
+ if(ndpi_struct->cfg.fpc_enabled &&
+ proto->app_protocol != NDPI_PROTOCOL_UNKNOWN &&
+ proto->app_protocol != proto->master_protocol &&
+ ndpi_struct->fpc_dns_cache) {
+ ndpi_lru_add_to_cache(ndpi_struct->fpc_dns_cache,
+ fpc_dns_cache_key_from_packet(packet->payload + x, data_len),
+ proto->app_protocol,
+ ndpi_get_current_time(flow));
- if((x+8) >= packet->payload_packet_len) {
- break;
- }
+ NDPI_LOG_DBG(ndpi_struct, "Adding entry to fpc_dns: %s proto %d\n",
+ data_len == 4 ? "ipv4" : "ipv6", proto->app_protocol);
+ }
+ }
- rsp_type = get16(&x, packet->payload);
- rsp_ttl = ntohl(*((u_int32_t*)&packet->payload[x+2]));
+ x += data_len;
+ }
+ }
- if(rsp_ttl == 0)
- ndpi_set_risk(ndpi_struct, flow, NDPI_MINOR_ISSUES, "DNS Record with zero TTL");
-
-#ifdef DNS_DEBUG
- printf("[DNS] TTL = %u\n", rsp_ttl);
- printf("[DNS] [response] response_type=%d\n", rsp_type);
-#endif
+ if(found && (dns_header->additional_rrs == 0)) {
+ /*
+ In case we have RR we need to iterate
+ all the answers and not just consider the
+ first one as we need to properly move 'x'
+ to the right offset
+ */
+ break;
+ }
+ }
- if(found == 0) {
- ndpi_check_dns_type(ndpi_struct, flow, rsp_type);
- flow->protos.dns.rsp_type = rsp_type;
- }
-
- /* x points to the response "class" field */
- if((x+12) <= packet->payload_packet_len) {
- u_int32_t ttl = ntohl(*((u_int32_t*)&packet->payload[x+2]));
-
- x += 6;
- data_len = get16(&x, packet->payload);
-
- if((x + data_len) <= packet->payload_packet_len) {
-#ifdef DNS_DEBUG
- printf("[DNS] [rsp_type: %u][data_len: %u]\n", rsp_type, data_len);
-#endif
+ return x;
+}
- if(rsp_type == 0x05 /* CNAME */) {
- ;
- } else if(rsp_type == 0x0C /* PTR */) {
- u_int16_t ptr_len = (packet->payload[x-2] << 8) + packet->payload[x-1];
-
- if((x + ptr_len) <= packet->payload_packet_len) {
- if(found == 0) {
- u_int len;
-
- ndpi_grab_dns_name(packet, &x,
- flow->protos.dns.ptr_domain_name,
- sizeof(flow->protos.dns.ptr_domain_name), &len,
- ignore_checks);
- found = 1;
- }
- }
- } else if((((rsp_type == 0x1) && (data_len == 4)) /* A */
- || ((rsp_type == 0x1c) && (data_len == 16)) /* AAAA */
- )) {
- if(found == 0) {
-
- if(flow->protos.dns.num_rsp_addr < MAX_NUM_DNS_RSP_ADDRESSES) {
- /* Necessary for IP address comparison */
- memset(&flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr], 0, sizeof(ndpi_ip_addr_t));
-
- memcpy(&flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr], packet->payload + x, data_len);
- flow->protos.dns.is_rsp_addr_ipv6[flow->protos.dns.num_rsp_addr] = (data_len == 16) ? 1 : 0;
- flow->protos.dns.rsp_addr_ttl[flow->protos.dns.num_rsp_addr] = ttl;
-
- if(ndpi_struct->cfg.address_cache_size)
- ndpi_cache_address(ndpi_struct,
- flow->protos.dns.rsp_addr[flow->protos.dns.num_rsp_addr],
- flow->host_server_name,
- packet->current_time_ms/1000,
- flow->protos.dns.rsp_addr_ttl[flow->protos.dns.num_rsp_addr]);
-
- ++flow->protos.dns.num_rsp_addr;
- }
-
- if(flow->protos.dns.num_rsp_addr >= MAX_NUM_DNS_RSP_ADDRESSES)
- found = 1;
- }
- }
-
- x += data_len;
- }
- }
-
- if(found && (dns_header->additional_rrs == 0)) {
- /*
- In case we have RR we need to iterate
- all the answers and not just consider the
- first one as we need to properly move 'x'
- to the right offset
- */
- break;
- }
- }
- }
+static int process_additionals(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow,
+ struct ndpi_dns_packet_header *dns_header,
+ u_int payload_offset) {
+ struct ndpi_packet_struct *packet = &ndpi_struct->packet;
+ u_int x = payload_offset;
- if(dns_header->additional_rrs > 0) {
- /*
- Dissect the rest of the packet only if there are
- additional_rrs as we need to check fo EDNS(0)
+ /*
+ Dissect the rest of the packet only if there are
+ additional_rrs as we need to check for:
+ * EDNS(0)
+ * NSID
- In this case we need to go through the whole packet
- as we need to update the 'x' offset
- */
- if(dns_header->authority_rrs > 0) {
+ In this case we need to go through the whole packet
+ as we need to update the 'x' offset
+ */
+
+ if(dns_header->additional_rrs == 0)
+ return x;
+
+ if(dns_header->authority_rrs > 0) {
#ifdef DNS_DEBUG
- u_int16_t rsp_type;
+ u_int16_t rsp_type;
#endif
- u_int16_t num;
+ u_int16_t num;
- for(num = 0; num < dns_header->authority_rrs; num++) {
- u_int16_t data_len;
+ for(num = 0; num < dns_header->authority_rrs; num++) {
+ u_int16_t data_len;
- if((x+6) >= packet->payload_packet_len) {
- break;
- }
+ if((x+6) >= packet->payload_packet_len) {
+ return -1;
+ }
- if((data_len = getNameLength(x, packet->payload,
- packet->payload_packet_len)) == 0) {
- break;
- } else
- x += data_len;
+ if((data_len = getNameLength(x, packet->payload,
+ packet->payload_packet_len)) == 0) {
+ return -1;
+ } else
+ x += data_len;
- if((x+8) >= packet->payload_packet_len) {
- break;
- }
+ if((x+8) >= packet->payload_packet_len) {
+ return -1;
+ }
- /* To avoid warning: variable ‘rsp_type’ set but not used [-Wunused-but-set-variable] */
+ /* To avoid warning: variable ‘rsp_type’ set but not used [-Wunused-but-set-variable] */
#ifdef DNS_DEBUG
- rsp_type = get16(&x, packet->payload);
+ rsp_type = get16(&x, packet->payload);
#else
- get16(&x, packet->payload);
+ get16(&x, packet->payload);
#endif
#ifdef DNS_DEBUG
- printf("[DNS] [RRS response] response_type=%d\n", rsp_type);
+ printf("[DNS] [RRS response] response_type=%d\n", rsp_type);
#endif
- /* here x points to the response "class" field */
- if((x+12) <= packet->payload_packet_len) {
- x += 6;
- data_len = get16(&x, packet->payload);
+ /* here x points to the response "class" field */
+ if((x+12) <= packet->payload_packet_len) {
+ x += 6;
+ data_len = get16(&x, packet->payload);
- if((x + data_len) <= packet->payload_packet_len)
- x += data_len;
- }
- }
- }
+ if((x + data_len) <= packet->payload_packet_len)
+ x += data_len;
+ }
+ }
+ }
+
+ if(dns_header->additional_rrs > 0) {
+ u_int16_t rsp_type;
+ u_int16_t num;
- if(dns_header->additional_rrs > 0) {
- u_int16_t rsp_type;
- u_int16_t num;
+ for(num = 0; num < dns_header->additional_rrs; num++) {
+ u_int16_t data_len, rdata_len, opt_code, opt_len;
+ const unsigned char *opt;
- for(num = 0; num < dns_header->additional_rrs; num++) {
- u_int16_t data_len, rdata_len, opt_code, opt_len;
- const unsigned char *opt;
+#ifdef DNS_DEBUG
+ printf("[DNS] [RR response %d/%d]\n", num + 1, dns_header->additional_rrs);
+#endif
- if((x+6) > packet->payload_packet_len) {
- break;
- }
+ if((x+6) > packet->payload_packet_len) {
+ return -1;
+ }
- if((data_len = getNameLength(x, packet->payload, packet->payload_packet_len)) == 0) {
- break;
- } else
- x += data_len;
+ if((data_len = getNameLength(x, packet->payload, packet->payload_packet_len)) == 0) {
+ return -1;
+ } else
+ x += data_len;
- if((x+10) > packet->payload_packet_len) {
- break;
- }
+ if((x+10) > packet->payload_packet_len) {
+ return -1;
+ }
- rsp_type = get16(&x, packet->payload);
+ rsp_type = get16(&x, packet->payload);
#ifdef DNS_DEBUG
- printf("[DNS] [RR response] response_type=%d\n", rsp_type);
+ printf("[DNS] [RR response] response_type=%d\n", rsp_type);
#endif
- if(rsp_type == 41 /* OPT */) {
- /* https://en.wikipedia.org/wiki/Extension_Mechanisms_for_DNS */
- flow->protos.dns.edns0_udp_payload_size = ntohs(*((u_int16_t*)&packet->payload[x])); /* EDNS(0) */
+ if(rsp_type == 41 /* OPT */) {
+ /* https://en.wikipedia.org/wiki/Extension_Mechanisms_for_DNS */
+ flow->protos.dns.edns0_udp_payload_size = ntohs(*((u_int16_t*)&packet->payload[x])); /* EDNS(0) */
#ifdef DNS_DEBUG
- printf("[DNS] [response] edns0_udp_payload_size: %u\n", flow->protos.dns.edns0_udp_payload_size);
+ printf("[DNS] [response] edns0_udp_payload_size: %u\n", flow->protos.dns.edns0_udp_payload_size);
#endif
- x += 6;
+ x += 6;
- rdata_len = ntohs(*((u_int16_t *)&packet->payload[x]));
+ rdata_len = ntohs(*((u_int16_t *)&packet->payload[x]));
#ifdef DNS_DEBUG
- printf("[DNS] [response] rdata len: %u\n", rdata_len);
+ printf("[DNS] [response] rdata len: %u\n", rdata_len);
#endif
- if(rdata_len > 0 &&
- x + 6 <= packet->payload_packet_len) {
- opt_code = ntohs(*((u_int16_t *)&packet->payload[x + 2]));
- opt_len = ntohs(*((u_int16_t *)&packet->payload[x + 4]));
- opt = &packet->payload[x + 6];
- /* TODO: parse the TLV list */
- if(opt_code == 0x03 &&
- opt_len <= rdata_len + 4 &&
- opt_len > 6 &&
- x + 6 + opt_len <= packet->payload_packet_len) {
+ if(rdata_len > 0 &&
+ x + 6 <= packet->payload_packet_len) {
+ opt_code = ntohs(*((u_int16_t *)&packet->payload[x + 2]));
+ opt_len = ntohs(*((u_int16_t *)&packet->payload[x + 4]));
+ opt = &packet->payload[x + 6];
+ /* TODO: parse the TLV list */
+ if(opt_code == 0x03 &&
+ opt_len <= rdata_len + 4 &&
+ opt_len > 6 &&
+ x + 6 + opt_len <= packet->payload_packet_len) {
#ifdef DNS_DEBUG
- printf("[DNS] NSID: [%.*s]\n", opt_len, opt);
+ printf("[DNS] NSID: [%.*s]\n", opt_len, opt);
#endif
- if(memcmp(opt, "gpdns-", 6) == 0) {
+ if(memcmp(opt, "gpdns-", 6) == 0) {
#ifdef DNS_DEBUG
- printf("[DNS] NSID Airport code [%.*s]\n", opt_len - 6, opt + 6);
+ printf("[DNS] NSID Airport code [%.*s]\n", opt_len - 6, opt + 6);
#endif
- memcpy(flow->protos.dns.geolocation_iata_code, opt + 6,
- ndpi_min(opt_len - 6, (int)sizeof(flow->protos.dns.geolocation_iata_code) - 1));
- }
- }
-
- }
- } else {
- x += 6;
- }
-
- if((data_len = getNameLength(x, packet->payload, packet->payload_packet_len)) == 0) {
- break;
- } else
- x += data_len;
- }
- }
+ memcpy(flow->protos.dns.geolocation_iata_code, opt + 6,
+ ndpi_min(opt_len - 6, (int)sizeof(flow->protos.dns.geolocation_iata_code) - 1));
+ }
+ }
- if((flow->detected_protocol_stack[0] == NDPI_PROTOCOL_DNS)
- || (flow->detected_protocol_stack[1] == NDPI_PROTOCOL_DNS)) {
- /* Request already set the protocol */
- // flow->extra_packets_func = NULL; /* Removed so the caller can keep dissecting DNS flows */
- } else {
- /* We missed the request */
- u_int16_t s_port = packet->udp ? ntohs(packet->udp->source) : ntohs(packet->tcp->source);
-
- ndpi_set_detected_protocol(ndpi_struct, flow, checkPort(s_port), NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
- }
+ }
+ } else {
+ x += 6;
}
+
+ if((data_len = getNameLength(x, packet->payload, packet->payload_packet_len)) == 0) {
+ return -1;
+ } else
+ x += data_len;
}
}
- /* Valid */
- return(0);
+ return x;
+}
+
+static int is_valid_dns(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow,
+ struct ndpi_dns_packet_header *dns_header,
+ u_int payload_offset, u_int8_t *is_query) {
+ struct ndpi_packet_struct *packet = &ndpi_struct->packet;
+
+ if(packet->payload_packet_len < sizeof(struct ndpi_dns_packet_header) + payload_offset)
+ return 0;
+
+ memcpy(dns_header, (struct ndpi_dns_packet_header*)&packet->payload[payload_offset],
+ sizeof(struct ndpi_dns_packet_header));
+
+ dns_header->tr_id = ntohs(dns_header->tr_id);
+ dns_header->flags = ntohs(dns_header->flags);
+ dns_header->num_queries = ntohs(dns_header->num_queries);
+ dns_header->num_answers = ntohs(dns_header->num_answers);
+ dns_header->authority_rrs = ntohs(dns_header->authority_rrs);
+ dns_header->additional_rrs = ntohs(dns_header->additional_rrs);
+
+ if((dns_header->flags & FLAGS_MASK) == 0x0000)
+ *is_query = 1;
+ else
+ *is_query = 0;
+
+ if(*is_query) {
+ if(dns_header->num_queries <= NDPI_MAX_DNS_REQUESTS &&
+ /* dns_header->num_answers == 0 && */
+ ((dns_header->flags & 0x2800) == 0x2800 /* Dynamic DNS Update */ ||
+ (dns_header->flags & 0xFCF0) == 0x00 /* Standard Query */ ||
+ (dns_header->flags & 0xFCFF) == 0x0800 /* Inverse query */ ||
+ (dns_header->num_answers == 0 && dns_header->authority_rrs == 0))) {
+ /* This is a good query */
+ return 1;
+ }
+ } else {
+ if(((dns_header->num_queries > 0 && dns_header->num_queries <= NDPI_MAX_DNS_REQUESTS) || /* Don't assume that num_queries must be zero */
+ (checkDNSSubprotocol(ntohs(flow->c_port), ntohs(flow->s_port)) == NDPI_PROTOCOL_MDNS && dns_header->num_queries == 0)) &&
+ ((dns_header->num_answers > 0 && dns_header->num_answers <= NDPI_MAX_DNS_REQUESTS) ||
+ (dns_header->authority_rrs > 0 && dns_header->authority_rrs <= NDPI_MAX_DNS_REQUESTS) ||
+ (dns_header->additional_rrs > 0 && dns_header->additional_rrs <= NDPI_MAX_DNS_REQUESTS) ||
+ (dns_header->num_answers == 0 && dns_header->authority_rrs == 0 && dns_header->additional_rrs == 0))) {
+ /* This is a good reply */
+ return 1;
+ }
+ if(dns_header->num_queries == 0 && dns_header->num_answers == 0 &&
+ dns_header->authority_rrs == 0 && dns_header->additional_rrs == 0 &&
+ packet->payload_packet_len == sizeof(struct ndpi_dns_packet_header)) {
+ /* This is a good empty reply */
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* *********************************************** */
+
+static int keep_extra_dissection(struct ndpi_flow_struct *flow)
+{
+ /* As a general rule, we wait for a valid response
+ (in the ideal world, we want to process the request/response pair) */
+ return !(!flow->protos.dns.is_query && flow->protos.dns.num_answers != 0);
}
/* *********************************************** */
static int search_dns_again(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) {
- /* possibly dissect the DNS reply */
- ndpi_search_dns(ndpi_struct, flow);
+ struct ndpi_packet_struct *packet = &ndpi_struct->packet;
- if(flow->protos.dns.num_answers != 0)
- return(0);
+ if(packet->tcp_retransmission || packet->payload_packet_len == 0)
+ return keep_extra_dissection(flow);
+
+ /* possibly dissect the DNS reply */
+ search_dns(ndpi_struct, flow);
- /* Possibly more processing */
- return(1);
+ return keep_extra_dissection(flow);
}
/* *********************************************** */
-static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) {
+static int process_hostname(struct ndpi_detection_module_struct *ndpi_struct,
+ struct ndpi_flow_struct *flow,
+ struct ndpi_dns_packet_header *dns_header,
+ ndpi_master_app_protocol *proto) {
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
- int payload_offset;
- u_int8_t is_query, is_mdns;
- u_int16_t s_port = 0, d_port = 0;
+ char *dot;
+ u_int len, is_mdns, off = sizeof(struct ndpi_dns_packet_header) + (packet->tcp ? 2 : 0);
+ char _hostname[256];
+ u_int8_t hostname_is_valid;
- NDPI_LOG_DBG(ndpi_struct, "search DNS\n");
+ proto->master_protocol = checkDNSSubprotocol(ntohs(flow->c_port), ntohs(flow->s_port));
+ proto->app_protocol = flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN ? flow->detected_protocol_stack[0] : NDPI_PROTOCOL_UNKNOWN;
- if(packet->udp != NULL) {
- s_port = ntohs(packet->udp->source);
- d_port = ntohs(packet->udp->dest);
- payload_offset = 0;
+ /* We try to get hostname only from "standard" query/answer */
+ if(dns_header->num_queries == 0 && dns_header->num_answers == 0)
+ return -1;
- /* For MDNS/LLMNR: If the packet is not a response, dest addr needs to be multicast. */
- if ((d_port == MDNS_PORT && isMDNSMulticastAddress(packet) == 0) ||
- (d_port == LLMNR_PORT && isLLMNRMulticastAddress(packet) == 0))
- {
- if (packet->payload_packet_len > 5 &&
- ntohs(get_u_int16_t(packet->payload, 2)) != 0 &&
- ntohs(get_u_int16_t(packet->payload, 4)) != 0)
- {
- NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
- return;
+ is_mdns = (proto->master_protocol == NDPI_PROTOCOL_MDNS);
+
+ /* TODO: should we overwrite existing hostname?
+ For the time being, keep the old/current behavior */
+
+ hostname_is_valid = ndpi_grab_dns_name(packet, &off, _hostname, sizeof(_hostname), &len, is_mdns);
+
+#ifdef DNS_DEBUG
+ printf("[DNS] [%s]\n", _hostname);
+#endif
+
+ ndpi_hostname_sni_set(flow, (const u_int8_t *)_hostname, len, is_mdns ? NDPI_HOSTNAME_NORM_LC : NDPI_HOSTNAME_NORM_ALL);
+
+ if (hostname_is_valid == 0)
+ ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, "Invalid chars detected in domain name");
+
+ /* Ignore reverse DNS queries */
+ if(strstr(_hostname, ".in-addr.") == NULL) {
+ dot = strchr(_hostname, '.');
+
+ if(dot) {
+ uintptr_t first_element_len = dot - _hostname;
+
+ if((first_element_len > 48) && (!is_mdns)) {
+ /*
+ The length of the first element in the query is very long
+ and this might be an issue or indicate an exfiltration
+ */
+
+ if(ends_with(ndpi_struct, _hostname, "multi.surbl.org")
+ || ends_with(ndpi_struct, _hostname, "spamhaus.org")
+ || ends_with(ndpi_struct, _hostname, "rackcdn.com")
+ || ends_with(ndpi_struct, _hostname, "akamaiedge.net")
+ || ends_with(ndpi_struct, _hostname, "mx-verification.google.com")
+ || ends_with(ndpi_struct, _hostname, "amazonaws.com")
+ )
+ ; /* Check common domain exceptions [TODO: if the list grows too much use a different datastructure] */
+ else
+ ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_SUSPICIOUS_TRAFFIC, "Long DNS host name");
}
}
- } else if(packet->tcp != NULL) /* pkt size > 512 bytes */ {
- s_port = ntohs(packet->tcp->source);
- d_port = ntohs(packet->tcp->dest);
- payload_offset = 2;
}
- is_mdns = ((s_port == MDNS_PORT) || (d_port == MDNS_PORT)) ? 1 : 0;
-
- if(((s_port == DNS_PORT) || (d_port == DNS_PORT)
- || is_mdns
- || (d_port == LLMNR_PORT))
- && (packet->payload_packet_len > sizeof(struct ndpi_dns_packet_header)+payload_offset)) {
- struct ndpi_dns_packet_header dns_header;
- char *dot;
- u_int len, off;
- int invalid = search_valid_dns(ndpi_struct, flow, &dns_header, payload_offset, &is_query, is_mdns);
- ndpi_protocol ret;
- u_int num_queries, idx;
- char _hostname[256];
-
- ret.proto.master_protocol = NDPI_PROTOCOL_UNKNOWN;
- ret.proto.app_protocol = (d_port == LLMNR_PORT) ? NDPI_PROTOCOL_LLMNR : (((d_port == MDNS_PORT) && isLLMNRMulticastAddress(packet) ) ? NDPI_PROTOCOL_MDNS : NDPI_PROTOCOL_DNS);
-
- if(invalid) {
- NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
- return;
+ if(strlen(flow->host_server_name) > 0) {
+ ndpi_protocol_match_result ret_match;
+
+ if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) {
+ proto->app_protocol = ndpi_match_host_subprotocol(ndpi_struct, flow,
+ flow->host_server_name,
+ strlen(flow->host_server_name),
+ &ret_match,
+ proto->master_protocol,
+ /* Avoid updating classification if subclassification is disabled */
+ ndpi_struct->cfg.dns_subclassification_enabled ? 1 : 0);
}
- /* extract host name server */
- off = sizeof(struct ndpi_dns_packet_header) + payload_offset;
+ ndpi_check_dga_name(ndpi_struct, flow, flow->host_server_name, 1, 0, proto->app_protocol != NDPI_PROTOCOL_UNKNOWN);
+ }
- /* Before continuing let's dissect the following queries to see if they are valid */
- for(idx=off, num_queries=0; (num_queries < dns_header.num_queries) && (idx < packet->payload_packet_len);) {
- u_int32_t i, tot_len = 0;
+ return 0;
+}
- for(i=idx; i<packet->payload_packet_len;) {
- u_int8_t is_ptr = 0, name_len = packet->payload[i]; /* Lenght of the individual name blocks aaa.bbb.com */
+static void search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) {
+ struct ndpi_packet_struct *packet = &ndpi_struct->packet;
+ int payload_offset = 0;
+ u_int8_t is_query;
+ struct ndpi_dns_packet_header dns_header;
+ u_int off;
+ ndpi_master_app_protocol proto;
+ int rc;
- if(name_len == 0) {
- tot_len++; /* \0 */
- /* End of query */
- break;
- } else if((name_len & 0xC0) == 0xC0)
- is_ptr = 1, name_len = 0; /* Pointer */
+ if(packet->udp != NULL) {
+ payload_offset = 0;
+ } else if(packet->tcp != NULL) {
+ payload_offset = 2;
+ }
+ if(!is_valid_dns(ndpi_struct, flow, &dns_header, payload_offset, &is_query)) {
#ifdef DNS_DEBUG
- if((!is_ptr) && (name_len > 0)) {
- printf("[DNS] [name_len: %u][", name_len);
-
- {
- int idx;
+ printf("[DNS] invalid packet\n");
+#endif
+ if(flow->extra_packets_func == NULL) {
+ NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
+ } else {
+ ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid DNS Header");
+ }
+ return;
+ }
- for(idx=0; idx<name_len; idx++)
- printf("%c", packet->payload[i+1+idx]);
+ process_hostname(ndpi_struct, flow, &dns_header, &proto);
- printf("]\n");
- }
- }
-#endif
+ off = sizeof(struct ndpi_dns_packet_header) + payload_offset;
- i += name_len+1, tot_len += name_len+1;
- if(is_ptr) break;
- } /* for */
+ if(is_query) {
+ flow->protos.dns.is_query = 1;
+ flow->protos.dns.transaction_id = dns_header.tr_id;
+ rc = process_queries(ndpi_struct, flow, &dns_header, off);
#ifdef DNS_DEBUG
- printf("[DNS] [tot_len: %u]\n\n", tot_len+4 /* type + class */);
+ if(rc == -1)
+ printf("[DNS] Error queries (query msg)\n");
#endif
+ } else {
+ flow->protos.dns.is_query = 0;
+ flow->protos.dns.transaction_id = dns_header.tr_id;
+ flow->protos.dns.reply_code = dns_header.flags & 0x0F;
+ flow->protos.dns.num_queries = dns_header.num_queries;
+ flow->protos.dns.num_answers = dns_header.num_answers + dns_header.authority_rrs + dns_header.additional_rrs;
- if(((i+4 /* Skip query type and class */) > packet->payload_packet_len)
- || ((packet->payload[i+1] == 0x0) && (packet->payload[i+2] == 0x0)) /* Query type cannot be 0 */
- || (tot_len > 253)
- ) {
- /* Invalid */
-#ifdef DNS_DEBUG
- printf("[DNS] Invalid query len [%u >= %u]\n", i+4, packet->payload_packet_len);
-#endif
- ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid DNS Query Lenght");
- break;
+ if(flow->protos.dns.reply_code != 0) {
+ if(is_flowrisk_info_enabled(ndpi_struct, NDPI_ERROR_CODE_DETECTED)) {
+ char str[32], buf[16];
+
+ snprintf(str, sizeof(str), "DNS Error Code %s",
+ dns_error_code2string(flow->protos.dns.reply_code, buf, sizeof(buf)));
+ ndpi_set_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED, str);
} else {
- idx = i+5, num_queries++;
+ ndpi_set_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED, NULL);
}
- } /* for */
-
- u_int8_t hostname_is_valid = ndpi_grab_dns_name(packet, &off, _hostname, sizeof(_hostname), &len, is_mdns);
-
- ndpi_hostname_sni_set(flow, (const u_int8_t *)_hostname, len, is_mdns ? NDPI_HOSTNAME_NORM_LC : NDPI_HOSTNAME_NORM_ALL);
-
- if (hostname_is_valid == 0)
- ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, "Invalid chars detected in domain name");
-
- /* Ignore reverse DNS queries */
- if(strstr(_hostname, ".in-addr.") == NULL) {
- dot = strchr(_hostname, '.');
-
- if(dot) {
- uintptr_t first_element_len = dot - _hostname;
-
- if((first_element_len > 48) && (!is_mdns)) {
- /*
- The lenght of the first element in the query is very long
- and this might be an issue or indicate an exfiltration
- */
-
- if(ends_with(ndpi_struct, _hostname, "multi.surbl.org")
- || ends_with(ndpi_struct, _hostname, "spamhaus.org")
- || ends_with(ndpi_struct, _hostname, "rackcdn.com")
- || ends_with(ndpi_struct, _hostname, "akamaiedge.net")
- || ends_with(ndpi_struct, _hostname, "mx-verification.google.com")
- || ends_with(ndpi_struct, _hostname, "amazonaws.com")
- )
- ; /* Check common domain exceptions [TODO: if the list grows too much use a different datastructure] */
- else
- ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_SUSPICIOUS_TRAFFIC, "Long DNS host name");
- }
+ } else {
+ if(ndpi_isset_risk(flow, NDPI_SUSPICIOUS_DGA_DOMAIN)) {
+ ndpi_set_risk(ndpi_struct, flow, NDPI_RISKY_DOMAIN, "DGA Name Query with no Error Code");
}
}
-
- if(len > 0) {
- if(ndpi_struct->cfg.dns_subclassification_enabled) {
- ndpi_protocol_match_result ret_match;
-
- ret.proto.app_protocol = ndpi_match_host_subprotocol(ndpi_struct, flow,
- flow->host_server_name,
- strlen(flow->host_server_name),
- &ret_match,
- NDPI_PROTOCOL_DNS);
- /* Add to FPC DNS cache */
- if(ret.proto.app_protocol != NDPI_PROTOCOL_UNKNOWN &&
- (flow->protos.dns.rsp_type == 0x1 || flow->protos.dns.rsp_type == 0x1c) && /* A, AAAA */
- ndpi_struct->fpc_dns_cache) {
- ndpi_lru_add_to_cache(ndpi_struct->fpc_dns_cache,
- fpc_dns_cache_key_from_dns_info(flow), ret.proto.app_protocol,
- ndpi_get_current_time(flow));
- }
-
- if(ret.proto.app_protocol == NDPI_PROTOCOL_UNKNOWN)
- ret.proto.master_protocol = checkDNSSubprotocol(s_port, d_port);
- else
- ret.proto.master_protocol = NDPI_PROTOCOL_DNS;
- ndpi_check_dga_name(ndpi_struct, flow, flow->host_server_name, 1, 0);
+ rc = process_queries(ndpi_struct, flow, &dns_header, off);
+ if(rc == -1) {
+#ifdef DNS_DEBUG
+ printf("[DNS] Error queries (response msg)\n");
+#endif
+ } else {
+ off = rc;
+ rc = process_answers(ndpi_struct, flow, &dns_header, off, &proto);
+ if(rc == -1) {
+#ifdef DNS_DEBUG
+ printf("[DNS] Error answers\n");
+#endif
} else {
- ret.proto.master_protocol = checkDNSSubprotocol(s_port, d_port);
- ret.proto.app_protocol = NDPI_PROTOCOL_UNKNOWN;
+ off = rc;
+ rc = process_additionals(ndpi_struct, flow, &dns_header, off);
+#ifdef DNS_DEBUG
+ if(rc == -1)
+ printf("[DNS] Error additionals\n");
+#endif
}
-
- /* Category is always NDPI_PROTOCOL_CATEGORY_NETWORK, regardless of the subprotocol */
- flow->category = NDPI_PROTOCOL_CATEGORY_NETWORK;
-
}
- /* Report if this is a DNS query or reply */
- flow->protos.dns.is_query = is_query;
+ if(proto.master_protocol == NDPI_PROTOCOL_DNS &&
+ /* TODO: add support to RFC6891 to avoid some false positives */
+ packet->udp &&
+ packet->payload_packet_len > PKT_LEN_ALERT &&
+ packet->payload_packet_len > flow->protos.dns.edns0_udp_payload_size) {
+ if(is_flowrisk_info_enabled(ndpi_struct, NDPI_DNS_LARGE_PACKET)) {
+ char str[48];
- if(is_query) {
- /* In this case we say that the protocol has been detected just to let apps carry on with their activities */
- ndpi_set_detected_protocol(ndpi_struct, flow, ret.proto.app_protocol, ret.proto.master_protocol, NDPI_CONFIDENCE_DPI);
-
- if(ndpi_struct->cfg.dns_parse_response_enabled) {
- /* We have never triggered extra-dissection for LLMNR. Keep the old behaviour */
- if(ret.proto.master_protocol != NDPI_PROTOCOL_LLMNR) {
- /* Don't use just 1 as in TCP DNS more packets could be returned (e.g. ACK). */
- flow->max_extra_packets_to_check = 5;
- flow->extra_packets_func = search_dns_again;
- }
+ snprintf(str, sizeof(str), "%u Bytes DNS Packet", packet->payload_packet_len);
+ ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_LARGE_PACKET, str);
+ } else {
+ ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_LARGE_PACKET, NULL);
}
- return; /* The response will set the verdict */
}
- flow->protos.dns.num_queries = (u_int8_t)dns_header.num_queries,
- flow->protos.dns.num_answers = (u_int8_t) (dns_header.num_answers + dns_header.authority_rrs + dns_header.additional_rrs);
-
-#ifdef DNS_DEBUG
- NDPI_LOG_DBG2(ndpi_struct, "[num_queries=%d][num_answers=%d][reply_code=%u][rsp_type=%u][host_server_name=%s]\n",
- flow->protos.dns.num_queries, flow->protos.dns.num_answers,
- flow->protos.dns.reply_code, flow->protos.dns.rsp_type, flow->host_server_name
- );
-#endif
+ NDPI_LOG_DBG2(ndpi_struct, "Response: [num_queries=%d][num_answers=%d][reply_code=%u][rsp_type=%u][host_server_name=%s]\n",
+ flow->protos.dns.num_queries, flow->protos.dns.num_answers,
+ flow->protos.dns.reply_code, flow->protos.dns.rsp_type, flow->host_server_name);
+ }
- if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) {
- /**
- Do not set the protocol with DNS if ndpi_match_host_subprotocol() has
- matched a subprotocol
- **/
- NDPI_LOG_INFO(ndpi_struct, "found DNS\n");
- ndpi_set_detected_protocol(ndpi_struct, flow, ret.proto.app_protocol, ret.proto.master_protocol, NDPI_CONFIDENCE_DPI);
- } else {
- if((flow->detected_protocol_stack[0] == NDPI_PROTOCOL_DNS)
- || (flow->detected_protocol_stack[1] == NDPI_PROTOCOL_DNS))
- ;
- else
- NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
+ if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) {
+ if(ndpi_struct->cfg.dns_subclassification_enabled)
+ ndpi_set_detected_protocol(ndpi_struct, flow, proto.app_protocol, proto.master_protocol, NDPI_CONFIDENCE_DPI);
+ else
+ ndpi_set_detected_protocol(ndpi_struct, flow, proto.master_protocol, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+ }
+ /* Category is always NDPI_PROTOCOL_CATEGORY_NETWORK, regardless of the subprotocol */
+ flow->category = NDPI_PROTOCOL_CATEGORY_NETWORK;
+
+ if(!flow->extra_packets_func &&
+ ndpi_struct->cfg.dns_parse_response_enabled &&
+ /* We have never triggered extra-dissection for LLMNR. Keep the old behavior */
+ flow->detected_protocol_stack[0] != NDPI_PROTOCOL_LLMNR &&
+ flow->detected_protocol_stack[1] != NDPI_PROTOCOL_LLMNR) {
+ if(keep_extra_dissection(flow)) {
+ NDPI_LOG_DBG(ndpi_struct, "Enabling extra dissection\n");
+ flow->max_extra_packets_to_check = 5;
+ flow->extra_packets_func = search_dns_again;
}
}
- if(flow->packet_counter > 3)
- NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
-
+ /* The bigger packets are usually the replies, but it shouldn't harm
+ to check the requests, too */
if((flow->detected_protocol_stack[0] == NDPI_PROTOCOL_DNS)
|| (flow->detected_protocol_stack[1] == NDPI_PROTOCOL_DNS)) {
- /* TODO: add support to RFC6891 to avoid some false positives */
- if((packet->udp != NULL)
- && (packet->payload_packet_len > PKT_LEN_ALERT)
- && (packet->payload_packet_len > flow->protos.dns.edns0_udp_payload_size)
- ) {
- char str[48];
-
- snprintf(str, sizeof(str), "%u Bytes DNS Packet", packet->payload_packet_len);
- ndpi_set_risk(ndpi_struct, flow, NDPI_DNS_LARGE_PACKET, str);
- }
if(packet->iph != NULL) {
/* IPv4 */
@@ -934,15 +934,62 @@ static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, st
/* *********************************************** */
-void init_dns_dissector(struct ndpi_detection_module_struct *ndpi_struct,
- u_int32_t *id) {
- ndpi_set_bitmask_protocol_detection("DNS", ndpi_struct, *id,
- NDPI_PROTOCOL_DNS,
- ndpi_search_dns,
- NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
- SAVE_DETECTION_BITMASK_AS_UNKNOWN,
- ADD_TO_DETECTION_BITMASK);
+static void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) {
+ struct ndpi_packet_struct *packet = &ndpi_struct->packet;
+ u_int16_t s_port = 0, d_port = 0;
+ int payload_offset = 0;
+
+ NDPI_LOG_DBG(ndpi_struct, "search DNS\n");
- *id += 1;
+ if(packet->udp != NULL) {
+ s_port = ntohs(packet->udp->source);
+ d_port = ntohs(packet->udp->dest);
+ payload_offset = 0;
+
+ /* For MDNS/LLMNR: If the packet is not a response, dest addr needs to be multicast. */
+ if ((d_port == MDNS_PORT && isMDNSMulticastAddress(packet) == 0) ||
+ (d_port == LLMNR_PORT && isLLMNRMulticastAddress(packet) == 0))
+ {
+ if (packet->payload_packet_len > 5 &&
+ ntohs(get_u_int16_t(packet->payload, 2)) != 0 &&
+ ntohs(get_u_int16_t(packet->payload, 4)) != 0)
+ {
+ NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
+ return;
+ }
+ }
+ } else if(packet->tcp != NULL) {
+ s_port = ntohs(packet->tcp->source);
+ d_port = ntohs(packet->tcp->dest);
+ payload_offset = 2;
+ }
+
+ /* We are able to detect DNS/MDNS/LLMNR only on standard ports (see #1788) */
+ if(!(s_port == DNS_PORT || d_port == DNS_PORT ||
+ s_port == MDNS_PORT || d_port == MDNS_PORT ||
+ d_port == LLMNR_PORT)) {
+ NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
+ return;
+ }
+
+ /* Since:
+ UDP: every packet must contains a complete/valid DNS message;
+ TCP: we are not able to handle DNS messages spanning multiple TCP packets;
+ we must be able to detect these protocols on the first packet
+ */
+ if(packet->payload_packet_len < sizeof(struct ndpi_dns_packet_header) + payload_offset) {
+ NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
+ return;
+ }
+
+ search_dns(ndpi_struct, flow);
+}
+
+/* *********************************************** */
+void init_dns_dissector(struct ndpi_detection_module_struct *ndpi_struct) {
+ register_dissector("DNS", ndpi_struct,
+ ndpi_search_dns,
+ NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
+ 1, NDPI_PROTOCOL_DNS);
}