diff options
Diffstat (limited to 'src/lib/protocols/dns.c')
-rw-r--r-- | src/lib/protocols/dns.c | 115 |
1 files changed, 76 insertions, 39 deletions
diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c index 90be9544c..1c2593feb 100644 --- a/src/lib/protocols/dns.c +++ b/src/lib/protocols/dns.c @@ -36,9 +36,9 @@ static u_int16_t get16(int *i, const u_int8_t *payload) { u_int16_t v = *(u_int16_t*)&payload[*i]; - + (*i) += 2; - + return(ntohs(v)); } @@ -52,36 +52,50 @@ static u_int getNameLength(u_int i, const u_int8_t *payload, u_int payloadLen) { 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)); } } +/* + allowed chars for dns names A-Z 0-9 _ - + Perl script for generation map: + my @M; + for(my $ch=0; $ch < 256; $ch++) { + $M[$ch >> 5] |= 1 << ($ch & 0x1f) if chr($ch) =~ /[a-z0-9_-]/i; + } + print join(',', map { sprintf "0x%08x",$_ } @M),"\n"; + */ +static uint32_t dns_validchar[8] = { + 0x00000000,0x03ff2000,0x87fffffe,0x07fffffe,0,0,0,0 +}; /* *********************************************** */ void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { - int x; + int x, payload_offset; u_int8_t is_query; u_int16_t s_port = 0, d_port = 0; - + NDPI_LOG_DBG(ndpi_struct, "search DNS\n"); if(flow->packet.udp != NULL) { s_port = ntohs(flow->packet.udp->source); d_port = ntohs(flow->packet.udp->dest); - x = 0; + payload_offset = 0; } else if(flow->packet.tcp != NULL) /* pkt size > 512 bytes */ { s_port = ntohs(flow->packet.tcp->source); d_port = ntohs(flow->packet.tcp->dest); - x = 2; + payload_offset = 2; } else { NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return; } + x = payload_offset; + if((s_port == 53 || d_port == 53 || d_port == 5355) && (flow->packet.payload_packet_len > sizeof(struct ndpi_dns_packet_header)+x)) { struct ndpi_dns_packet_header dns_header; @@ -106,6 +120,7 @@ void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct nd invalid = 1; if(!invalid) { + int j = 0, max_len, off; if(is_query) { /* DNS Request */ if((dns_header.num_queries > 0) && (dns_header.num_queries <= NDPI_MAX_DNS_REQUESTS) @@ -118,7 +133,7 @@ void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct nd if(flow->packet.payload[x] == '\0') { x++; flow->protos.dns.query_type = get16(&x, flow->packet.payload); -#ifdef DNS_DEBUG +#ifdef DNS_DEBUG NDPI_LOG_DBG2(ndpi_struct, "query_type=%2d\n", flow->protos.dns.query_type); #endif break; @@ -128,7 +143,6 @@ void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct nd } } else invalid = 1; - } else { /* DNS Reply */ @@ -139,16 +153,18 @@ void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct nd || ((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 */ - if(ndpi_struct->dns_dissect_response) { + /* This is a good reply: we dissect it both for request and response */ + + /* Leave the statement below commented necessary in case of call to ndpi_get_partial_detection() */ + /* if(ndpi_struct->dns_dont_dissect_response == 0) */ { x++; - + if(flow->packet.payload[x] != '\0') { while((x < flow->packet.payload_packet_len) && (flow->packet.payload[x] != '\0')) { x++; } - + x++; } @@ -160,7 +176,7 @@ void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct nd for(num = 0; num < dns_header.num_answers; num++) { u_int16_t data_len; - + if((x+6) >= flow->packet.payload_packet_len) { break; } @@ -169,14 +185,31 @@ void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct nd break; } else x += data_len; - + rsp_type = get16(&x, flow->packet.payload); flow->protos.dns.rsp_type = rsp_type; + + /* here x points to the response "class" field */ + if((x+12) < flow->packet.payload_packet_len) { + x += 6; + data_len = get16(&x, flow->packet.payload); + + if(((x + data_len) < flow->packet.payload_packet_len) + && (((rsp_type == 0x1) && (data_len == 4)) /* A */ +#ifdef NDPI_DETECTION_SUPPORT_IPV6 + || ((rsp_type == 0x1c) && (data_len == 16)) /* AAAA */ +#endif + )) { + memcpy(&flow->protos.dns.rsp_addr, flow->packet.payload + x, data_len); + } + } + break; } } } - } + } else + invalid = 1; } if(invalid) { @@ -185,57 +218,61 @@ void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct nd } /* extract host name server */ - int j = 0, max_len = sizeof(flow->host_server_name)-1, off = sizeof(struct ndpi_dns_packet_header) + 1; - - while(off < flow->packet.payload_packet_len && flow->packet.payload[off] != '\0') { - flow->host_server_name[j] = flow->packet.payload[off]; - if(j < max_len) { - if(flow->host_server_name[j] < ' ') - flow->host_server_name[j] = '.'; - j++; - } else - break; - - off++; + max_len = sizeof(flow->host_server_name)-1; + off = sizeof(struct ndpi_dns_packet_header) + payload_offset; + + while(j < max_len && off < flow->packet.payload_packet_len && flow->packet.payload[off] != '\0') { + uint8_t c,cl = flow->packet.payload[off++]; + if( (cl & 0xc0) != 0 || // we not support compressed names in query + off + cl >= flow->packet.payload_packet_len) { + j = 0; break; + } + if(j && j < max_len) flow->host_server_name[j++] = '.'; + while(j < max_len && cl != 0) { + c = flow->packet.payload[off++]; + flow->host_server_name[j++] = dns_validchar[c >> 5] & (1 << (c & 0x1f)) ? c:'_'; + cl--; + } } - - if(is_query && ndpi_struct->dns_dissect_response) - return; /* The response will set the verdict */ - flow->host_server_name[j] = '\0'; + if(is_query && (ndpi_struct->dns_dont_dissect_response == 0)) { + // dpi_set_detected_protocol(ndpi_struct, flow, (d_port == 5355) ? NDPI_PROTOCOL_LLMNR : NDPI_PROTOCOL_DNS, NDPI_PROTOCOL_UNKNOWN); + 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); + flow->protos.dns.num_answers = (u_int8_t) (dns_header.num_answers + dns_header.authority_rrs + dns_header.additional_rrs); if(j > 0) { ndpi_protocol_match_result ret_match; - - ndpi_match_host_subprotocol(ndpi_struct, flow, + + ndpi_match_host_subprotocol(ndpi_struct, flow, (char *)flow->host_server_name, strlen((const char*)flow->host_server_name), &ret_match, NDPI_PROTOCOL_DNS); } - + #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 - + if(flow->packet.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_LOG_INFO(ndpi_struct, "found DNS\n"); ndpi_set_detected_protocol(ndpi_struct, flow, (d_port == 5355) ? NDPI_PROTOCOL_LLMNR : NDPI_PROTOCOL_DNS, NDPI_PROTOCOL_UNKNOWN); } else { NDPI_EXCLUDE_PROTO(ndpi_struct, flow); } } - } + } } void init_dns_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id, NDPI_PROTOCOL_BITMASK *detection_bitmask) |