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.c300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/lib/protocols/dns.c b/src/lib/protocols/dns.c
new file mode 100644
index 000000000..071039340
--- /dev/null
+++ b/src/lib/protocols/dns.c
@@ -0,0 +1,300 @@
+/*
+ * dns.c
+ *
+ * Copyright (C) 2012-15 - ntop.org
+ *
+ * This file is part of nDPI, an open source deep packet inspection
+ * library based on the OpenDPI and PACE technology by ipoque GmbH
+ *
+ * nDPI is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * nDPI is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with nDPI. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "ndpi_protocols.h"
+
+#ifdef NDPI_PROTOCOL_DNS
+
+static u_int getNameLength(u_int i, const u_int8_t *payload, u_int payloadLen) {
+ if(payload[i] == 0x00)
+ return(1);
+ else if(payload[i] == 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));
+ }
+}
+
+/* *********************************************** */
+
+static char* ndpi_intoa_v4(unsigned int addr, char* buf, u_short bufLen) {
+ char *cp, *retStr;
+ uint byte;
+ int n;
+
+ cp = &buf[bufLen];
+ *--cp = '\0';
+
+ n = 4;
+ do {
+ byte = addr & 0xff;
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if(byte > 0) {
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if(byte > 0)
+ *--cp = byte + '0';
+ }
+ *--cp = '.';
+ addr >>= 8;
+ } while (--n > 0);
+
+ /* Convert the string to lowercase */
+ retStr = (char*)(cp+1);
+
+ return(retStr);
+}
+
+/* *********************************************** */
+
+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));
+}
+
+/* *********************************************** */
+
+struct dns_packet_header {
+ u_int16_t transaction_id, flags, num_queries, answer_rrs, authority_rrs, additional_rrs;
+} __attribute__((packed));
+
+void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow)
+{
+ struct ndpi_packet_struct *packet = &flow->packet;
+ u_int16_t dport = 0, sport = 0;
+
+#define NDPI_MAX_DNS_REQUESTS 16
+
+ NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "search DNS.\n");
+
+ if (packet->udp != NULL) {
+ sport = ntohs(packet->udp->source), dport = ntohs(packet->udp->dest);
+ NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "calculated dport over UDP.\n");
+ } else if(packet->tcp != NULL) {
+ sport = ntohs(packet->tcp->source), dport = ntohs(packet->tcp->dest);
+ NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "calculated dport over tcp.\n");
+ }
+
+ if(((dport == 53) || (sport == 53) || (dport == 5355))
+ && (packet->payload_packet_len > sizeof(struct dns_packet_header))) {
+ int i = packet->tcp ? 2 : 0;
+ struct dns_packet_header header, *dns = (struct dns_packet_header*)&packet->payload[i];
+ u_int8_t is_query, ret_code, is_dns = 0;
+ u_int32_t a_record[NDPI_MAX_DNS_REQUESTS] = { 0 }, query_offset, num_a_records = 0;
+
+ header.flags = ntohs(dns->flags);
+ header.transaction_id = ntohs(dns->transaction_id);
+ header.num_queries = ntohs(dns->num_queries);
+ header.answer_rrs = ntohs(dns->answer_rrs);
+ header.authority_rrs = ntohs(dns->authority_rrs);
+ header.additional_rrs = ntohs(dns->additional_rrs);
+ is_query = (header.flags & 0x8000) ? 0 : 1;
+ ret_code = is_query ? 0 : (header.flags & 0x0F);
+ i += sizeof(struct dns_packet_header);
+ query_offset = i;
+
+ if(is_query) {
+ /* DNS Request */
+ if((header.num_queries > 0) && (header.num_queries <= NDPI_MAX_DNS_REQUESTS)
+ && (((header.flags & 0x2800) == 0x2800 /* Dynamic DNS Update */)
+ || ((header.answer_rrs == 0) && (header.authority_rrs == 0)))) {
+ /* This is a good query */
+ is_dns = 1;
+
+ if(header.num_queries > 0) {
+ while(i < packet->payload_packet_len) {
+ if(packet->payload[i] == '\0') {
+ i++;
+ flow->protos.dns.query_type = get16(&i, packet->payload);
+ break;
+ } else
+ i++;
+ }
+ }
+ }
+ } else {
+ /* DNS Reply */
+
+ flow->server_id = flow->dst;
+
+ if((header.num_queries <= NDPI_MAX_DNS_REQUESTS) /* Don't assume that num_queries must be zero */
+ && (((header.answer_rrs > 0) && (header.answer_rrs <= NDPI_MAX_DNS_REQUESTS))
+ || ((header.authority_rrs > 0) && (header.authority_rrs <= NDPI_MAX_DNS_REQUESTS))
+ || ((header.additional_rrs > 0) && (header.additional_rrs <= NDPI_MAX_DNS_REQUESTS)))
+ ) {
+ /* This is a good reply */
+ is_dns = 1;
+
+ i++;
+
+ if(packet->payload[i] != '\0') {
+ while((i < packet->payload_packet_len)
+ && (packet->payload[i] != '\0')) {
+ i++;
+ }
+
+ i++;
+ }
+
+ i += 4;
+
+ if(header.answer_rrs > 0) {
+ u_int16_t rsp_type /*, rsp_class */;
+ u_int16_t num;
+
+ for(num = 0; num < header.answer_rrs; num++) {
+ u_int16_t data_len;
+
+ if((i+6) >= packet->payload_packet_len) {
+ break;
+ }
+
+ if((data_len = getNameLength(i, packet->payload, packet->payload_packet_len)) == 0) {
+ break;
+ } else
+ i += data_len;
+
+ rsp_type = get16(&i, packet->payload);
+ // rsp_class = get16(&i, packet->payload);
+
+ i += 4;
+ data_len = get16(&i, packet->payload);
+
+ if((data_len <= 1) || (data_len > (packet->payload_packet_len-i))) {
+ break;
+ }
+
+ flow->protos.dns.rsp_type = rsp_type;
+
+ if(rsp_type == 1 /* A */) {
+ if(data_len == 4) {
+ u_int32_t v = ntohl(*((u_int32_t*)&packet->payload[i]));
+
+ if(num_a_records < (NDPI_MAX_DNS_REQUESTS-1))
+ a_record[num_a_records++] = v;
+ else
+ break; /* One record is enough */
+ }
+ }
+
+ if(data_len == 0) {
+ break;
+ }
+
+ i += data_len;
+ } /* for */
+ }
+ }
+
+ if((header.num_queries <= NDPI_MAX_DNS_REQUESTS)
+ && ((header.answer_rrs == 0)
+ || (header.authority_rrs == 0)
+ || (header.additional_rrs == 0))
+ && (ret_code != 0 /* 0 == OK */)
+ ) {
+ /* This is a good reply */
+ is_dns = 1;
+ }
+ }
+
+ if(is_dns) {
+ int j = 0;
+
+ flow->protos.dns.num_queries = (u_int8_t)header.num_queries,
+ flow->protos.dns.num_answers = (u_int8_t)(header.answer_rrs+header.authority_rrs+header.additional_rrs),
+ flow->protos.dns.ret_code = ret_code;
+
+ i = query_offset+1;
+
+ while((i < packet->payload_packet_len)
+ && (j < (sizeof(flow->host_server_name)-1))
+ && (packet->payload[i] != '\0')) {
+ flow->host_server_name[j] = tolower(packet->payload[i]);
+ if(flow->host_server_name[j] < ' ')
+ flow->host_server_name[j] = '.';
+ j++, i++;
+ }
+
+ if(a_record != 0) {
+ char a_buf[32];
+ int i;
+
+ for(i=0; i<num_a_records; i++) {
+ j += snprintf((char*)&flow->host_server_name[j], sizeof(flow->host_server_name)-1-j, "%s%s",
+ (i == 0) ? "@" : ";",
+ ndpi_intoa_v4(a_record[i], a_buf, sizeof(a_buf)));
+ }
+ }
+
+ flow->host_server_name[j] = '\0';
+
+ if(j > 0) {
+#ifdef DEBUG
+ printf("==> %s\n", flow->host_server_name);
+#endif
+
+ if(ndpi_struct->match_dns_host_names)
+ ndpi_match_string_subprotocol(ndpi_struct, flow,
+ (char *)flow->host_server_name,
+ strlen((const char*)flow->host_server_name));
+ }
+
+ i++;
+
+ memcpy(&flow->protos.dns.query_type, &packet->payload[i], 2);
+ flow->protos.dns.query_type = ntohs(flow->protos.dns.query_type), i += 2;
+
+ memcpy(&flow->protos.dns.query_class, &packet->payload[i], 2);
+ flow->protos.dns.query_class = ntohs(flow->protos.dns.query_class), i += 2;
+
+#ifdef DEBUG
+ printf("%s [type=%04X][class=%04X]\n", flow->host_server_name, flow->protos.dns.query_type, flow->protos.dns.query_class);
+#endif
+
+ if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) {
+ /*
+ Do not set the protocol with DNS if ndpi_match_string_subprotocol() has
+ matched a subprotocol
+ */
+ NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "found DNS.\n");
+ ndpi_int_add_connection(ndpi_struct, flow, (dport == 5355) ? NDPI_PROTOCOL_LLMNR : NDPI_PROTOCOL_DNS, NDPI_REAL_PROTOCOL);
+ }
+ } else {
+ flow->protos.dns.bad_packet = 1;
+ NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "exclude DNS.\n");
+ NDPI_ADD_PROTOCOL_TO_BITMASK(flow->excluded_protocol_bitmask, NDPI_PROTOCOL_DNS);
+ }
+ }
+}
+#endif