--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2004-2013 Sourcefire, Inc.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program 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
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+//
+
+/*
+ * DNS preprocessor
+ * Author: Chris Sherwin
+ * Contributors: Adam Keeton, Ryan Jordan
+ *
+ *
+ * Alert for Gobbles, CRC32, protocol mismatch (Cisco catalyst vulnerability),
+ * and a SecureCRT vulnerability. Will also alert if the client or server
+ * traffic appears to flow the wrong direction, or if packets appear
+ * malformed/spoofed.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "snort_types.h"
+#include "snort_debug.h"
+
+#include "dns.h"
+#include "dns_module.h"
+#include "profiler.h"
+#include "stream/stream_api.h"
+#include "parser.h"
+#include "framework/inspector.h"
+#include "utils/sfsnprintfappend.h"
+
+THREAD_LOCAL ProfileStats dnsPerfStats;
+THREAD_LOCAL SimpleStats dnsstats;
+
+#define MIN_UDP_PAYLOAD 0x1FFF
+#define DNS_RR_PTR 0xC0
+
+/*
+ * Function prototype(s)
+ */
+static void snort_dns(Packet* p);
+
+unsigned DnsFlowData::flow_id = 0;
+
+DNSData udpSessionData;
+
+DNSData* SetNewDNSData(Packet* p)
+{
+ DnsFlowData* fd;
+
+ if (p->is_udp())
+ return NULL;
+
+ fd = new DnsFlowData;
+
+ p->flow->set_application_data(fd);
+ return &fd->session;
+}
+
+static DNSData* get_dns_session_data(Packet* p)
+{
+ DnsFlowData* fd;
+
+ if (p->is_udp())
+ {
+ if (p->dsize < (sizeof(DNSHdr) + sizeof(DNSRR) + MIN_UDP_PAYLOAD))
+ return NULL;
+
+ memset(&udpSessionData, 0, sizeof(udpSessionData));
+ return &udpSessionData;
+ }
+
+ fd = (DnsFlowData*)((p->flow)->get_application_data(
+ DnsFlowData::flow_id));
+
+ return fd ? &fd->session : NULL;
+}
+
+static uint16_t ParseDNSHeader(const unsigned char* data,
+ uint16_t bytes_unused,
+ DNSData* dnsSessionData)
+{
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+
+ switch (dnsSessionData->state)
+ {
+ case DNS_RESP_STATE_LENGTH:
+ /* First two bytes are length in TCP */
+ dnsSessionData->length = ((uint8_t)*data) << 8;
+ dnsSessionData->state = DNS_RESP_STATE_LENGTH_PART;
+ data++;
+ bytes_unused--;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_LENGTH_PART:
+ dnsSessionData->length |= ((uint8_t)*data);
+ dnsSessionData->state = DNS_RESP_STATE_HDR_ID;
+ data++;
+ bytes_unused--;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_ID:
+ dnsSessionData->hdr.id = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_ID_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_ID_PART:
+ dnsSessionData->hdr.id |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_FLAGS;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_FLAGS:
+ dnsSessionData->hdr.flags = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_FLAGS_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_FLAGS_PART:
+ dnsSessionData->hdr.flags |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_QS;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_QS:
+ dnsSessionData->hdr.questions = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_QS_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_QS_PART:
+ dnsSessionData->hdr.questions |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_ANSS;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_ANSS:
+ dnsSessionData->hdr.answers = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_ANSS_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_ANSS_PART:
+ dnsSessionData->hdr.answers |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_AUTHS;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_AUTHS:
+ dnsSessionData->hdr.authorities = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_AUTHS_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_AUTHS_PART:
+ dnsSessionData->hdr.authorities |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_ADDS;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_ADDS:
+ dnsSessionData->hdr.additionals = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_HDR_ADDS_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_HDR_ADDS_PART:
+ dnsSessionData->hdr.additionals |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->state = DNS_RESP_STATE_QUESTION;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ default:
+ /* Continue -- we're beyond the header */
+ break;
+ }
+
+ return bytes_unused;
+}
+
+uint16_t ParseDNSName(const unsigned char* data,
+ uint16_t bytes_unused,
+ DNSData* dnsSessionData)
+{
+ uint16_t bytes_required = dnsSessionData->curr_txt.txt_len -
+ dnsSessionData->curr_txt.txt_bytes_seen;
+
+ while (dnsSessionData->curr_txt.name_state != DNS_RESP_STATE_NAME_COMPLETE)
+ {
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+
+ switch (dnsSessionData->curr_txt.name_state)
+ {
+ case DNS_RESP_STATE_NAME_SIZE:
+ dnsSessionData->curr_txt.txt_len = (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->bytes_seen_curr_rec++;
+ if (dnsSessionData->curr_txt.txt_len == 0)
+ {
+ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME_COMPLETE;
+ return bytes_unused;
+ }
+
+ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME;
+ dnsSessionData->curr_txt.txt_bytes_seen = 0;
+
+ if ((dnsSessionData->curr_txt.txt_len & DNS_RR_PTR) == DNS_RR_PTR)
+ {
+ /* A reference to another location...
+ This is an offset */
+ dnsSessionData->curr_txt.offset = (dnsSessionData->curr_txt.txt_len & ~0xC0) << 8;
+ bytes_required = dnsSessionData->curr_txt.txt_len = 1;
+ dnsSessionData->curr_txt.relative = 1;
+ /* Setup to read 2nd Byte of Location */
+ }
+ else
+ {
+ bytes_required = dnsSessionData->curr_txt.txt_len;
+ dnsSessionData->curr_txt.offset = 0;
+ dnsSessionData->curr_txt.relative = 0;
+ }
+
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+
+ /* Fall through */
+ case DNS_RESP_STATE_NAME:
+ if (bytes_required <= bytes_unused)
+ {
+ bytes_unused -= bytes_required;
+ if (dnsSessionData->curr_txt.relative)
+ {
+ /* If this one is a relative offset, read that extra byte */
+ dnsSessionData->curr_txt.offset |= *data;
+ }
+ data += bytes_required;
+ dnsSessionData->bytes_seen_curr_rec += bytes_required;
+ dnsSessionData->curr_txt.txt_bytes_seen += bytes_required;
+
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ }
+ else
+ {
+ dnsSessionData->bytes_seen_curr_rec+= bytes_unused;
+ dnsSessionData->curr_txt.txt_bytes_seen += bytes_unused;
+ return 0;
+ }
+ if (dnsSessionData->curr_txt.relative)
+ {
+ /* And since its relative, we're done */
+ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME_COMPLETE;
+ return bytes_unused;
+ }
+ break;
+ }
+
+ /* Go to the next portion of the name */
+ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME_SIZE;
+ }
+
+ return bytes_unused;
+}
+
+static uint16_t ParseDNSQuestion(const unsigned char* data,
+ uint16_t /*data_size*/,
+ uint16_t bytes_unused,
+ DNSData* dnsSessionData)
+{
+ uint16_t bytes_used = 0;
+ uint16_t new_bytes_unused = 0;
+
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+
+ if (dnsSessionData->curr_rec_state < DNS_RESP_STATE_Q_NAME_COMPLETE)
+ {
+ new_bytes_unused = ParseDNSName(data, bytes_unused, dnsSessionData);
+ bytes_used = bytes_unused - new_bytes_unused;
+
+ if (dnsSessionData->curr_txt.name_state == DNS_RESP_STATE_NAME_COMPLETE)
+ {
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_TYPE;
+ memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+ data = data + bytes_used;
+ bytes_unused = new_bytes_unused;
+
+ if (bytes_unused == 0)
+ {
+ /* ran out of data */
+ return bytes_unused;
+ }
+ }
+ else
+ {
+ /* Should be 0 -- ran out of data */
+ return new_bytes_unused;
+ }
+ }
+
+ switch (dnsSessionData->curr_rec_state)
+ {
+ case DNS_RESP_STATE_Q_TYPE:
+ dnsSessionData->curr_q.type = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_TYPE_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_Q_TYPE_PART:
+ dnsSessionData->curr_q.type |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_CLASS;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_Q_CLASS:
+ dnsSessionData->curr_q.dns_class = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_CLASS_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_Q_CLASS_PART:
+ dnsSessionData->curr_q.dns_class |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_COMPLETE;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ default:
+ /* Continue -- we're beyond this question */
+ break;
+ }
+
+ return bytes_unused;
+}
+
+uint16_t ParseDNSAnswer(const unsigned char* data,
+ uint16_t /*data_size*/,
+ uint16_t bytes_unused,
+ DNSData* dnsSessionData)
+{
+ uint16_t bytes_used = 0;
+ uint16_t new_bytes_unused = 0;
+
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+
+ if (dnsSessionData->curr_rec_state < DNS_RESP_STATE_RR_NAME_COMPLETE)
+ {
+ new_bytes_unused = ParseDNSName(data, bytes_unused, dnsSessionData);
+ bytes_used = bytes_unused - new_bytes_unused;
+
+ if (dnsSessionData->curr_txt.name_state == DNS_RESP_STATE_NAME_COMPLETE)
+ {
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TYPE;
+ memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+ data = data + bytes_used;
+ }
+ bytes_unused = new_bytes_unused;
+
+ if (bytes_unused == 0)
+ {
+ /* ran out of data */
+ return bytes_unused;
+ }
+ }
+
+ switch (dnsSessionData->curr_rec_state)
+ {
+ case DNS_RESP_STATE_RR_TYPE:
+ dnsSessionData->curr_rr.type = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TYPE_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_RR_TYPE_PART:
+ dnsSessionData->curr_rr.type |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_CLASS;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_RR_CLASS:
+ dnsSessionData->curr_rr.dns_class = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_CLASS_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_RR_CLASS_PART:
+ dnsSessionData->curr_rr.dns_class |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TTL;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_RR_TTL:
+ dnsSessionData->curr_rr.ttl = (uint8_t)*data << 24;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TTL_PART;
+ dnsSessionData->bytes_seen_curr_rec = 1;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_RR_TTL_PART:
+ while (dnsSessionData->bytes_seen_curr_rec < 4)
+ {
+ dnsSessionData->bytes_seen_curr_rec++;
+ dnsSessionData->curr_rr.ttl |=
+ (uint8_t)*data << (4-dnsSessionData->bytes_seen_curr_rec)*8;
+ data++;
+ bytes_unused--;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ }
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDLENGTH;
+ /* Fall through */
+ case DNS_RESP_STATE_RR_RDLENGTH:
+ dnsSessionData->curr_rr.length = (uint8_t)*data << 8;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDLENGTH_PART;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_RR_RDLENGTH_PART:
+ dnsSessionData->curr_rr.length |= (uint8_t)*data;
+ data++;
+ bytes_unused--;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDATA_START;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ default:
+ /* Continue -- we're beyond this answer */
+ break;
+ }
+
+ return bytes_unused;
+}
+
+/* The following check is to look for an attempt to exploit
+ * a vulnerability in the DNS client, per MS 06-041.
+ *
+ * For details, see:
+ * http://www.microsoft.com/technet/security/bulletin/ms06-007.mspx
+ * http://cve.mitre.org/cgi-bin/cvename.cgi?name=2006-3441
+ *
+ * Vulnerability Research by Lurene Grenier, Judy Novak,
+ * and Brian Caswell.
+ */
+uint16_t CheckRRTypeTXTVuln(const unsigned char* data,
+ uint16_t bytes_unused,
+ DNSData* dnsSessionData)
+{
+ uint16_t bytes_required = dnsSessionData->curr_txt.txt_len -
+ dnsSessionData->curr_txt.txt_bytes_seen;
+
+ while (dnsSessionData->curr_txt.name_state != DNS_RESP_STATE_RR_NAME_COMPLETE)
+ {
+ if (dnsSessionData->bytes_seen_curr_rec == dnsSessionData->curr_rr.length)
+ {
+ /* Done with the name */
+ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_RR_NAME_COMPLETE;
+ /* Got to the end of the rdata in this packet! */
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_COMPLETE;
+ return bytes_unused;
+ }
+
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+
+ switch (dnsSessionData->curr_txt.name_state)
+ {
+ case DNS_RESP_STATE_RR_NAME_SIZE:
+ dnsSessionData->curr_txt.txt_len = (uint8_t)*data;
+ dnsSessionData->curr_txt.txt_count++;
+
+ /* include the NULL */
+ dnsSessionData->curr_txt.total_txt_len += dnsSessionData->curr_txt.txt_len + 1;
+
+ if (!dnsSessionData->curr_txt.alerted)
+ {
+ uint32_t overflow_check = (dnsSessionData->curr_txt.txt_count * 4) +
+ (dnsSessionData->curr_txt.total_txt_len * 2) + 4;
+ /* if txt_count * 4 + total_txt_len * 2 + 4 > FFFF, vulnerability! */
+ if (overflow_check > 0xFFFF)
+ {
+ /* Alert on obsolete DNS RR types */
+ SnortEventqAdd(GID_DNS, DNS_EVENT_RDATA_OVERFLOW);
+
+ dnsSessionData->curr_txt.alerted = 1;
+ }
+ }
+
+ data++;
+ bytes_unused--;
+ dnsSessionData->bytes_seen_curr_rec++;
+ if (dnsSessionData->curr_txt.txt_len > 0)
+ {
+ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_RR_NAME;
+ dnsSessionData->curr_txt.txt_bytes_seen = 0;
+ bytes_required = dnsSessionData->curr_txt.txt_len;
+ }
+ else
+ {
+ continue;
+ }
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ /* Fall through */
+ case DNS_RESP_STATE_RR_NAME:
+ if (bytes_required <= bytes_unused)
+ {
+ bytes_unused -= bytes_required;
+ dnsSessionData->bytes_seen_curr_rec += bytes_required;
+ data += bytes_required;
+ dnsSessionData->curr_txt.txt_bytes_seen += bytes_required;
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+ }
+ else
+ {
+ dnsSessionData->curr_txt.txt_bytes_seen += bytes_unused;
+ dnsSessionData->bytes_seen_curr_rec += bytes_unused;
+ return 0;
+ }
+ break;
+ }
+
+ /* Go to the next portion of the name */
+ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_RR_NAME_SIZE;
+ }
+
+ return bytes_unused;
+}
+
+uint16_t SkipDNSRData(const unsigned char* data,
+ uint16_t bytes_unused,
+ DNSData* dnsSessionData)
+{
+ uint16_t bytes_required = dnsSessionData->curr_rr.length - dnsSessionData->bytes_seen_curr_rec;
+
+ if (bytes_required <= bytes_unused)
+ {
+ bytes_unused -= bytes_required;
+ data += bytes_required;
+ dnsSessionData->bytes_seen_curr_rec += bytes_required;
+ }
+ else
+ {
+ dnsSessionData->bytes_seen_curr_rec += bytes_unused;
+ return 0;
+ }
+
+ /* Got to the end of the rdata in this packet! */
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_COMPLETE;
+ return bytes_unused;
+}
+
+uint16_t ParseDNSRData(Packet*,
+ const unsigned char* data,
+ uint16_t bytes_unused,
+ DNSData* dnsSessionData)
+{
+ if (bytes_unused == 0)
+ {
+ return bytes_unused;
+ }
+
+ switch (dnsSessionData->curr_rr.type)
+ {
+ case DNS_RR_TYPE_TXT:
+ /* Check for RData Overflow */
+ bytes_unused = CheckRRTypeTXTVuln(data, bytes_unused, dnsSessionData);
+ break;
+
+ case DNS_RR_TYPE_MD:
+ case DNS_RR_TYPE_MF:
+ /* Alert on obsolete DNS RR types */
+ SnortEventqAdd(GID_DNS, DNS_EVENT_OBSOLETE_TYPES);
+ bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData);
+ break;
+
+ case DNS_RR_TYPE_MB:
+ case DNS_RR_TYPE_MG:
+ case DNS_RR_TYPE_MR:
+ case DNS_RR_TYPE_NULL:
+ case DNS_RR_TYPE_MINFO:
+ /* Alert on experimental DNS RR types */
+ SnortEventqAdd(GID_DNS, DNS_EVENT_EXPERIMENTAL_TYPES);
+ bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData);
+ break;
+ case DNS_RR_TYPE_A:
+ case DNS_RR_TYPE_NS:
+ case DNS_RR_TYPE_CNAME:
+ case DNS_RR_TYPE_SOA:
+ case DNS_RR_TYPE_WKS:
+ case DNS_RR_TYPE_PTR:
+ case DNS_RR_TYPE_HINFO:
+ case DNS_RR_TYPE_MX:
+ bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData);
+ break;
+ default:
+ /* Not one of the known types. Stop looking at this session
+ * as DNS. */
+ dnsSessionData->flags |= DNS_FLAG_NOT_DNS;
+ break;
+ }
+
+ return bytes_unused;
+}
+
+void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData)
+{
+ uint16_t bytes_unused = p->dsize;
+ int i;
+ const unsigned char* data = p->data;
+
+ while (bytes_unused)
+ {
+ /* Parse through the DNS Header */
+ if (dnsSessionData->state < DNS_RESP_STATE_QUESTION)
+ {
+ /* Length only applies on a TCP packet, skip to header ID
+ * if at beginning of a UDP Response.
+ */
+ if ((dnsSessionData->state == DNS_RESP_STATE_LENGTH) &&
+ p->is_udp())
+ {
+ dnsSessionData->state = DNS_RESP_STATE_HDR_ID;
+ }
+
+ bytes_unused = ParseDNSHeader(data, bytes_unused, dnsSessionData);
+ if (bytes_unused > 0)
+ {
+ data = p->data + (p->dsize - bytes_unused);
+ }
+ else
+ {
+ /* No more data */
+ return;
+ }
+
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_NAME;
+ dnsSessionData->curr_rec = 0;
+ }
+
+ /* Print out the header (but only once -- when we're ready to parse the Questions */
+ if ((dnsSessionData->curr_rec_state == DNS_RESP_STATE_Q_NAME) &&
+ (dnsSessionData->curr_rec == 0))
+ {
+ DEBUG_WRAP(
+ DebugMessage(DEBUG_DNS,
+ "DNS Header: length %d, id 0x%x, flags 0x%x, "
+ "questions %d, answers %d, authorities %d, additionals %d\n",
+ dnsSessionData->length, dnsSessionData->hdr.id,
+ dnsSessionData->hdr.flags, dnsSessionData->hdr.questions,
+ dnsSessionData->hdr.answers,
+ dnsSessionData->hdr.authorities,
+ dnsSessionData->hdr.additionals);
+ );
+ }
+
+ if (!(dnsSessionData->hdr.flags & DNS_HDR_FLAG_RESPONSE))
+ {
+ /* Not a response */
+ return;
+ }
+
+ /* Handle the DNS Queries */
+ if (dnsSessionData->state == DNS_RESP_STATE_QUESTION)
+ {
+ /* Skip over the 4 byte question records... */
+ for (i=dnsSessionData->curr_rec; i< dnsSessionData->hdr.questions; i++)
+ {
+ bytes_unused = ParseDNSQuestion(data, p->dsize, bytes_unused, dnsSessionData);
+
+ if (dnsSessionData->curr_rec_state == DNS_RESP_STATE_Q_COMPLETE)
+ {
+ DEBUG_WRAP(
+ DebugMessage(DEBUG_DNS,
+ "DNS Question %d: type %d, class %d\n",
+ i, dnsSessionData->curr_q.type,
+ dnsSessionData->curr_q.dns_class);
+ );
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_NAME;
+ dnsSessionData->curr_rec++;
+ }
+ if (bytes_unused > 0)
+ {
+ data = p->data + (p->dsize - bytes_unused);
+ }
+ else
+ {
+ /* No more data */
+ return;
+ }
+ }
+ dnsSessionData->state = DNS_RESP_STATE_ANS_RR;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE;
+ dnsSessionData->curr_rec = 0;
+ }
+
+ /* Handle the RRs */
+ switch (dnsSessionData->state)
+ {
+ case DNS_RESP_STATE_ANS_RR: /* ANSWERS section */
+ for (i=dnsSessionData->curr_rec; i<dnsSessionData->hdr.answers; i++)
+ {
+ bytes_unused = ParseDNSAnswer(data, p->dsize,
+ bytes_unused, dnsSessionData);
+
+ if (bytes_unused == 0)
+ {
+ /* No more data */
+ return;
+ }
+
+ switch (dnsSessionData->curr_rec_state)
+ {
+ case DNS_RESP_STATE_RR_RDATA_START:
+ DEBUG_WRAP(
+ DebugMessage(DEBUG_DNS,
+ "DNS ANSWER RR %d: type %d, class %d, "
+ "ttl %d rdlength %d\n", i,
+ dnsSessionData->curr_rr.type,
+ dnsSessionData->curr_rr.dns_class,
+ dnsSessionData->curr_rr.ttl,
+ dnsSessionData->curr_rr.length);
+ );
+
+ dnsSessionData->bytes_seen_curr_rec = 0;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDATA_MID;
+ /* Fall through */
+ case DNS_RESP_STATE_RR_RDATA_MID:
+ /* Data now points to the beginning of the RDATA */
+ data = p->data + (p->dsize - bytes_unused);
+ bytes_unused = ParseDNSRData(p, data, bytes_unused, dnsSessionData);
+ if (dnsSessionData->curr_rec_state != DNS_RESP_STATE_RR_COMPLETE)
+ {
+ /* Out of data, pick up on the next packet */
+ return;
+ }
+ else
+ {
+ /* Go to the next record */
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE;
+ dnsSessionData->curr_rec++;
+
+ if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT)
+ {
+ /* Reset the state tracking for this record */
+ memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+ }
+ data = p->data + (p->dsize - bytes_unused);
+ }
+ }
+ }
+ dnsSessionData->state = DNS_RESP_STATE_AUTH_RR;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE;
+ dnsSessionData->curr_rec = 0;
+ /* Fall through */
+ case DNS_RESP_STATE_AUTH_RR: /* AUTHORITIES section */
+ for (i=dnsSessionData->curr_rec; i<dnsSessionData->hdr.authorities; i++)
+ {
+ bytes_unused = ParseDNSAnswer(data, p->dsize,
+ bytes_unused, dnsSessionData);
+
+ if (bytes_unused == 0)
+ {
+ /* No more data */
+ return;
+ }
+
+ switch (dnsSessionData->curr_rec_state)
+ {
+ case DNS_RESP_STATE_RR_RDATA_START:
+ DEBUG_WRAP(
+ DebugMessage(DEBUG_DNS,
+ "DNS AUTH RR %d: type %d, class %d, "
+ "ttl %d rdlength %d\n", i,
+ dnsSessionData->curr_rr.type,
+ dnsSessionData->curr_rr.dns_class,
+ dnsSessionData->curr_rr.ttl,
+ dnsSessionData->curr_rr.length);
+ );
+
+ dnsSessionData->bytes_seen_curr_rec = 0;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDATA_MID;
+ /* Fall through */
+ case DNS_RESP_STATE_RR_RDATA_MID:
+ /* Data now points to the beginning of the RDATA */
+ data = p->data + (p->dsize - bytes_unused);
+ bytes_unused = ParseDNSRData(p, data, bytes_unused, dnsSessionData);
+ if (dnsSessionData->curr_rec_state != DNS_RESP_STATE_RR_COMPLETE)
+ {
+ /* Out of data, pick up on the next packet */
+ return;
+ }
+ else
+ {
+ /* Go to the next record */
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE;
+ dnsSessionData->curr_rec++;
+
+ if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT)
+ {
+ /* Reset the state tracking for this record */
+ memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+ }
+ data = p->data + (p->dsize - bytes_unused);
+ }
+ }
+ }
+ dnsSessionData->state = DNS_RESP_STATE_ADD_RR;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE;
+ dnsSessionData->curr_rec = 0;
+ /* Fall through */
+ case DNS_RESP_STATE_ADD_RR: /* ADDITIONALS section */
+ for (i=dnsSessionData->curr_rec; i<dnsSessionData->hdr.authorities; i++)
+ {
+ bytes_unused = ParseDNSAnswer(data, p->dsize,
+ bytes_unused, dnsSessionData);
+
+ if (bytes_unused == 0)
+ {
+ /* No more data */
+ return;
+ }
+
+ switch (dnsSessionData->curr_rec_state)
+ {
+ case DNS_RESP_STATE_RR_RDATA_START:
+ DEBUG_WRAP(
+ DebugMessage(DEBUG_DNS,
+ "DNS ADDITONAL RR %d: type %d, class %d, "
+ "ttl %d rdlength %d\n", i,
+ dnsSessionData->curr_rr.type,
+ dnsSessionData->curr_rr.dns_class,
+ dnsSessionData->curr_rr.ttl,
+ dnsSessionData->curr_rr.length);
+ );
+
+ dnsSessionData->bytes_seen_curr_rec = 0;
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDATA_MID;
+ /* Fall through */
+ case DNS_RESP_STATE_RR_RDATA_MID:
+ /* Data now points to the beginning of the RDATA */
+ data = p->data + (p->dsize - bytes_unused);
+ bytes_unused = ParseDNSRData(p, data, bytes_unused, dnsSessionData);
+ if (dnsSessionData->curr_rec_state != DNS_RESP_STATE_RR_COMPLETE)
+ {
+ /* Out of data, pick up on the next packet */
+ return;
+ }
+ else
+ {
+ /* Go to the next record */
+ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE;
+ dnsSessionData->curr_rec++;
+
+ if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT)
+ {
+ /* Reset the state tracking for this record */
+ memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState));
+ }
+ data = p->data + (p->dsize - bytes_unused);
+ }
+ }
+ }
+ /* Done with this one, onto the next -- may also be in this packet */
+ dnsSessionData->state = DNS_RESP_STATE_LENGTH;
+ dnsSessionData->curr_rec_state = 0;
+ dnsSessionData->curr_rec = 0;
+ }
+ }
+}
+
+static void snort_dns(Packet* p)
+{
+ DNSData* dnsSessionData = NULL;
+ uint8_t direction = 0;
+ PROFILE_VARS;
+
+ /* For TCP, do a few extra checks... */
+ if (p->is_udp())
+ {
+ /* If session picked up mid-stream, do not process further.
+ * Would be almost impossible to tell where we are in the
+ * data stream. */
+ if ( stream.get_session_flags(p->flow) & SSNFLAG_MIDSTREAM )
+ {
+ return;
+ }
+
+ if ( stream.is_stream_sequenced(p->flow, SSN_DIR_FROM_CLIENT) )
+ {
+ return;
+ }
+
+ /* If we're waiting on stream reassembly, don't process this packet. */
+ if ( p->packet_flags & PKT_STREAM_INSERT )
+ {
+ return;
+ }
+ }
+
+ /* Get the direction of the packet. */
+ direction = ( (p->packet_flags & PKT_FROM_SERVER ) ?
+ DNS_DIR_FROM_SERVER : DNS_DIR_FROM_CLIENT );
+
+ MODULE_PROFILE_START(dnsPerfStats);
+
+ /* Attempt to get a previously allocated DNS block. */
+ dnsSessionData = get_dns_session_data(p);
+
+ if (dnsSessionData == NULL)
+ {
+ /* Check the stream session. If it does not currently
+ * have our DNS data-block attached, create one.
+ */
+ dnsSessionData = SetNewDNSData(p);
+
+ if ( !dnsSessionData )
+ {
+ /* Could not get/create the session data for this packet. */
+ MODULE_PROFILE_END(dnsPerfStats);
+ return;
+ }
+ }
+
+ if (dnsSessionData->flags & DNS_FLAG_NOT_DNS)
+ {
+ MODULE_PROFILE_END(dnsPerfStats);
+ return;
+ }
+
+ if (direction == DNS_DIR_FROM_SERVER)
+ {
+ ParseDNSResponseMessage(p, dnsSessionData);
+ }
+
+ MODULE_PROFILE_END(dnsPerfStats);
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+class Dns : public Inspector
+{
+public:
+ Dns(DnsModule*);
+
+ void show(SnortConfig*) override;
+ void eval(Packet*) override;
+};
+
+Dns::Dns(DnsModule*)
+{ }
+
+void Dns::show(SnortConfig*)
+{
+ LogMessage("DNS\n");
+}
+
+void Dns::eval(Packet* p)
+{
+ // precondition - what we registered for
+ assert(p->is_udp() && p->is_tcp() && p->dsize && p->data);
+
+ ++dnsstats.total_packets;
+ snort_dns(p);
+}
+
+//-------------------------------------------------------------------------
+// api stuff
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{ return new DnsModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
+static void dns_init()
+{
+ DnsFlowData::init();
+}
+
+static Inspector* dns_ctor(Module* m)
+{
+ DnsModule* mod = (DnsModule*)m;
+ return new Dns(mod);
+}
+
+static void dns_dtor(Inspector* p)
+{
+ delete p;
+}
+
+const InspectApi dns_api =
+{
+ {
+ PT_INSPECTOR,
+ sizeof(InspectApi),
+ INSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ DNS_NAME,
+ DNS_HELP,
+ mod_ctor,
+ mod_dtor
+ },
+ IT_SERVICE,
+ (uint16_t)PktType::TCP | (uint16_t)PktType::UDP,
+ nullptr, // buffers
+ "dns",
+ dns_init,
+ nullptr, // pterm
+ nullptr, // tinit
+ nullptr, // tterm
+ dns_ctor,
+ dns_dtor,
+ nullptr, // ssn
+ nullptr // reset
+};
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+{
+ &dns_api.base,
+ nullptr
+};
+#else
+const BaseApi* sin_dns = &dns_api.base;
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2004-2013 Sourcefire, Inc.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program 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
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+//
+
+/*
+ * dns.h: Definitions, structs, function prototype(s) for
+ * the DNS service inspectors.
+ * Author: Chris Sherwin
+ */
+
+#ifndef DNS_H
+#define DNS_H
+
+#include "protocols/packet.h"
+#include "stream/stream_api.h"
+#include "profiler.h"
+
+/*
+ * Directional defines
+ */
+#define DNS_DIR_FROM_SERVER 1
+#define DNS_DIR_FROM_CLIENT 2
+
+/****** A few data structures ******/
+typedef struct _DNSHdr
+{
+ uint16_t id;
+ uint16_t flags;
+ uint16_t questions;
+ uint16_t answers;
+ uint16_t authorities;
+ uint16_t additionals;
+} DNSHdr;
+
+#define DNS_HDR_FLAG_REPLY_CODE_MASK 0x000F
+#define DNS_HDR_FLAG_NON_AUTHENTICATED_OK 0x0010
+#define DNS_HDR_FLAG_ANS_AUTHENTICATED 0x0020
+#define DNS_HDR_FLAG_RESERVED 0x0040
+#define DNS_HDR_FLAG_RECURSION_AVAIL 0x0080
+#define DNS_HDR_FLAG_RECURSION_DESIRED 0x0100
+#define DNS_HDR_FLAG_TRUNCATED 0x0200
+#define DNS_HDR_FLAG_AUTHORITATIVE 0x0400
+#define DNS_HDR_FLAG_OPCODE_MASK 0x7800
+#define DNS_HDR_FLAG_RESPONSE 0x8000
+
+typedef struct _DNSQuestion
+{
+ uint16_t type;
+ uint16_t dns_class;
+} DNSQuestion;
+
+typedef struct _DNSRR
+{
+ uint16_t type;
+ uint16_t dns_class;
+ uint32_t ttl;
+ uint16_t length;
+} DNSRR;
+
+typedef struct _DNSNameState
+{
+ uint32_t txt_count;
+ uint32_t total_txt_len;
+ uint8_t txt_len;
+ uint8_t txt_bytes_seen;
+ uint8_t name_state;
+ uint8_t alerted;
+ uint16_t offset;
+ uint8_t relative;
+} DNSNameState;
+
+#define DNS_RR_TYPE_A 0x0001
+#define DNS_RR_TYPE_NS 0x0002
+#define DNS_RR_TYPE_MD 0x0003 /* obsolete */
+#define DNS_RR_TYPE_MF 0x0004 /* obsolete */
+#define DNS_RR_TYPE_CNAME 0x0005
+#define DNS_RR_TYPE_SOA 0x0006
+#define DNS_RR_TYPE_MB 0x0007 /* experimental */
+#define DNS_RR_TYPE_MG 0x0008 /* experimental */
+#define DNS_RR_TYPE_MR 0x0009 /* experimental */
+#define DNS_RR_TYPE_NULL 0x000a /* experimental */
+#define DNS_RR_TYPE_WKS 0x000b
+#define DNS_RR_TYPE_PTR 0x000c
+#define DNS_RR_TYPE_HINFO 0x000d
+#define DNS_RR_TYPE_MINFO 0x000e /* experimental */
+#define DNS_RR_TYPE_MX 0x000f
+#define DNS_RR_TYPE_TXT 0x0010
+
+#define DNS_FLAG_NOT_DNS 0x01
+
+/* DNSSessionData States */
+#define DNS_RESP_STATE_LENGTH 0x00 /* 2 bytes - TCP only*/
+#define DNS_RESP_STATE_LENGTH_PART 0x01 /* Partial length */
+
+#define DNS_RESP_STATE_HDR 0x10 /* 12 bytes */
+#define DNS_RESP_STATE_HDR_ID 0x11 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_ID_PART 0x12 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_FLAGS 0x13 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_FLAGS_PART 0x14 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_QS 0x15 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_QS_PART 0x16 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_ANSS 0x17 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_ANSS_PART 0x18 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_AUTHS 0x19 /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_AUTHS_PART 0x1a /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_ADDS 0x1b /* (2 bytes) */
+#define DNS_RESP_STATE_HDR_ADDS_PART 0x1c /* (2 bytes) */
+
+#define DNS_RESP_STATE_QUESTION 0x20 /* 4 bytes */
+#define DNS_RESP_STATE_Q_NAME 0x21 /* (size depends on data) */
+#define DNS_RESP_STATE_Q_NAME_COMPLETE 0x22 /* (size depends on data) */
+#define DNS_RESP_STATE_Q_TYPE 0x23 /* (2 bytes) */
+#define DNS_RESP_STATE_Q_TYPE_PART 0x24 /* (2 bytes) */
+#define DNS_RESP_STATE_Q_CLASS 0x25 /* (2 bytes) */
+#define DNS_RESP_STATE_Q_CLASS_PART 0x26 /* (2 bytes) */
+#define DNS_RESP_STATE_Q_COMPLETE 0x27
+
+#define DNS_RESP_STATE_NAME_SIZE 0x31 /* (1 byte) */
+#define DNS_RESP_STATE_NAME 0x32 /* (size depends on field) */
+#define DNS_RESP_STATE_NAME_COMPLETE 0x33
+
+#define DNS_RESP_STATE_ANS_RR 0x40 /* (size depends on field) */
+#define DNS_RESP_STATE_RR_NAME_SIZE 0x41 /* (1 byte) */
+#define DNS_RESP_STATE_RR_NAME 0x42 /* (size depends on field) */
+#define DNS_RESP_STATE_RR_NAME_COMPLETE 0x43
+#define DNS_RESP_STATE_RR_TYPE 0x44 /* (2 bytes) */
+#define DNS_RESP_STATE_RR_TYPE_PART 0x45 /* (2 bytes) */
+#define DNS_RESP_STATE_RR_CLASS 0x46 /* (2 bytes) */
+#define DNS_RESP_STATE_RR_CLASS_PART 0x47 /* (2 bytes) */
+#define DNS_RESP_STATE_RR_TTL 0x48 /* (4 bytes) */
+#define DNS_RESP_STATE_RR_TTL_PART 0x49 /* (4 bytes) */
+#define DNS_RESP_STATE_RR_RDLENGTH 0x4a /* (2 bytes) */
+#define DNS_RESP_STATE_RR_RDLENGTH_PART 0x4b /* (2 bytes) */
+#define DNS_RESP_STATE_RR_RDATA_START 0x4c /* (size depends on RDLENGTH) */
+#define DNS_RESP_STATE_RR_RDATA_MID 0x4d /* (size depends on RDLENGTH) */
+#define DNS_RESP_STATE_RR_COMPLETE 0x4e
+
+#define DNS_RESP_STATE_AUTH_RR 0x50
+#define DNS_RESP_STATE_ADD_RR 0x60
+
+/*
+ * Per-session data block containing current state
+ * of the DNS preprocessor for the session.
+ *
+ * state: The current state of the session.
+ * num_records: Number of records in the session.
+ * curr_record: Record number for the current record
+ * curr_record_length: Current record length.
+ * total_record_length: Total data length of records.
+ * length: Total length of DNS response (TCP only)
+ * hdr: Copy of the data from the DNS Header
+ */
+struct DNSData
+{
+ uint32_t state;
+ uint16_t curr_rec;
+ uint16_t curr_rec_length;
+ uint16_t bytes_seen_curr_rec;
+ uint16_t length;
+ uint8_t curr_rec_state;
+ DNSHdr hdr;
+ DNSQuestion curr_q;
+ DNSRR curr_rr;
+ DNSNameState curr_txt;
+ uint8_t flags;
+};
+
+class DnsFlowData : public FlowData
+{
+public:
+ DnsFlowData() : FlowData(flow_id)
+ { memset(&session, 0, sizeof(session)); }
+
+ ~DnsFlowData() { }
+
+ static void init()
+ { flow_id = FlowData::get_flow_id(); }
+
+public:
+ static unsigned flow_id;
+ DNSData session;
+};
+
+#endif /* DNS_H */
+