summaryrefslogtreecommitdiffstats
path: root/src/dns.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns.h')
-rw-r--r--src/dns.h541
1 files changed, 541 insertions, 0 deletions
diff --git a/src/dns.h b/src/dns.h
new file mode 100644
index 0000000..0afd281
--- /dev/null
+++ b/src/dns.h
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2017-2024 OARC, Inc.
+ * Copyright (c) 2011-2017, IIS - The Internet Foundation in Sweden
+ * All rights reserved.
+ *
+ * This file is part of PacketQ.
+ *
+ * PacketQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PacketQ 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 PacketQ. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __packetq_dns_h
+#define __packetq_dns_h
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "output.h"
+#include "packet_handler.h"
+#include "tcp.h"
+
+namespace packetq {
+
+extern char visible_char_map[256];
+
+class DNSMessage {
+public:
+ class Header {
+ public:
+ int id;
+ int z;
+ bool qr;
+ int opcode;
+ bool aa;
+ bool tc;
+ bool rd;
+ bool ra;
+ bool ad;
+ bool cd;
+ int rcode;
+ int qdcount;
+ int ancount;
+ int nscount;
+ int arcount;
+ Header()
+ : z(0)
+ {
+ id = 0;
+ qr = 0;
+ opcode = 0;
+ aa = 0;
+ tc = 0;
+ rd = 0;
+ ra = 0;
+ ad = 0;
+ cd = 0;
+ rcode = 0;
+ qdcount = 0;
+ ancount = 0;
+ nscount = 0;
+ arcount = 0;
+ }
+ void parse(DNSMessage& p)
+ {
+ /*
+ From rfc 2929
+ 1 1 1 1 1 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ID |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | QDCOUNT/ZOCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ANCOUNT/PRCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | NSCOUNT/UPCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | ARCOUNT |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ */
+
+ id = p.get_ushort(0);
+ qr = p.get_bit(2, 0);
+ opcode = p.get_bits(2, 1, 4);
+ aa = p.get_bit(2, 5);
+ tc = p.get_bit(2, 6);
+ rd = p.get_bit(2, 7);
+ ra = p.get_bit(2, 8);
+ ad = p.get_bit(2, 10);
+ cd = p.get_bit(2, 11);
+ rcode = p.get_bits(2, 12, 4);
+ qdcount = p.get_ushort(4);
+ ancount = p.get_ushort(6);
+ nscount = p.get_ushort(8);
+ arcount = p.get_ushort(10);
+ }
+ };
+
+ class Name {
+ public:
+ char fqdn[2048]; // escaping needs *4 the space
+ int labels;
+
+ Name()
+ : fqdn { 0 }
+ , labels(0)
+ {
+ }
+
+ void reset(void)
+ {
+ fqdn[0] = 0;
+ labels = 0;
+ }
+ };
+
+ class Question {
+ public:
+ Name qname;
+ int qtype;
+ int qclass;
+
+ Question()
+ {
+ qtype = 0;
+ qclass = 0;
+ }
+
+ int parse(DNSMessage& m, int offs)
+ {
+ offs = m.parse_dname(qname, offs);
+ qtype = m.get_ushort(offs);
+ offs += 2;
+ qclass = m.get_ushort(offs);
+ offs += 2;
+ return offs;
+ }
+ };
+
+ class RR {
+ public:
+ Name name;
+ int type;
+ int rr_class;
+ unsigned int ttl;
+ int rdlength;
+ int doffs;
+
+ RR()
+ {
+ type = 0;
+ rr_class = 0;
+ ttl = 0;
+ rdlength = 0;
+ doffs = 0;
+ }
+
+ int parse(DNSMessage& m, int offs)
+ {
+ offs = m.parse_dname(name, offs);
+ type = m.get_ushort(offs);
+ if (type == 41) {
+ m.m_opt_rr = this;
+ m.m_new_opt_rr = true;
+ }
+ offs += 2;
+ rr_class = m.get_ushort(offs);
+ offs += 2;
+ ttl = m.get_ushort(offs) << 16;
+ ttl |= m.get_ushort(offs + 2);
+ offs += 4;
+ rdlength = m.get_ushort(offs);
+ offs += 2;
+ doffs = offs;
+ offs += rdlength;
+ return offs;
+ }
+ };
+
+ IP_header& m_ip_header;
+ unsigned char* m_data;
+ int m_length;
+ Header m_header;
+ Question m_questions[2];
+ RR m_answer[2];
+ RR m_authority[2];
+ RR m_additional[2];
+ RR* m_opt_rr;
+ bool m_new_opt_rr;
+ int m_error;
+ bool m_edns0;
+ bool m_do;
+ int m_extended_rcode;
+ int m_edns_version;
+ int m_z;
+ int m_udp_size;
+ bool m_edns0_ecs;
+ int m_edns0_ecs_family;
+ int m_edns0_ecs_source;
+ int m_edns0_ecs_scope;
+ in6addr_t m_edns0_ecs_addr;
+ bool m_edns0_ecs_addr_set;
+
+ DNSMessage(unsigned char* data, int len, IP_header& head)
+ : m_ip_header(head)
+ {
+ m_opt_rr = 0;
+ m_new_opt_rr = false;
+ m_error = 0;
+ m_data = data;
+ m_length = len;
+ m_edns0 = false;
+ m_do = false;
+ m_extended_rcode = 0;
+ m_edns_version = 0;
+ m_z = 0;
+ m_udp_size = 0;
+ m_edns0_ecs = false;
+ m_edns0_ecs_family = 0;
+ m_edns0_ecs_source = 0;
+ m_edns0_ecs_scope = 0;
+ m_edns0_ecs_addr_set = false;
+
+ parse();
+ }
+ int parse_dname(Name& name, int offs)
+ {
+ int p = 0;
+ int savedoffs = 0;
+ int n = get_ubyte(offs++);
+ char* out = &name.fqdn[0];
+ if (n == 0)
+ out[p++] = '.';
+
+ while (n > 0) {
+ name.labels++;
+ while (n >= 192) {
+ if (savedoffs) {
+ out[p++] = 0;
+ return savedoffs;
+ }
+ savedoffs = offs + 1;
+ int n2 = get_ubyte(offs++);
+ int ptr = (n & 63) * 0x100 + n2;
+ offs = ptr;
+ n = get_ubyte(offs++);
+ }
+
+ // if the string is too long restart and mess it up
+ // check if we can fit a fully escaped label + . and reserve for zeroing it later
+ if (p + (n * 4) + 1 > sizeof(name.fqdn) - 1)
+ p = 0;
+
+ while (n-- > 0) {
+ const unsigned int byte = get_ubyte(offs++);
+ if (visible_char_map[byte]) {
+ out[p++] = visible_char_map[byte];
+ } else {
+ out[p++] = '\\';
+ out[p++] = '0' + byte / 100;
+ out[p++] = '0' + byte / 10 % 10;
+ out[p++] = '0' + byte % 10;
+ }
+ }
+ out[p++] = '.';
+ n = get_ubyte(offs++);
+ }
+ if (savedoffs)
+ offs = savedoffs;
+ out[p++] = 0;
+ return offs;
+ }
+ void parse_opt_rr()
+ {
+ if (m_opt_rr) {
+ if (!m_edns0) {
+ m_edns0 = true;
+ unsigned long ttl = m_opt_rr->ttl;
+ m_do = (ttl >> 15) & 1;
+ m_extended_rcode = ttl >> 24;
+ m_edns_version = (ttl >> 16) & 0xff;
+ m_z = ttl & 0x7fff;
+ m_udp_size = m_opt_rr->rr_class;
+ }
+ if (((m_opt_rr->ttl >> 16) & 0xff) == 0 && m_opt_rr->rdlength > 0) {
+ // Parse this OPT RR that is EDNS0
+ int rdlen = m_opt_rr->rdlength,
+ offs = m_opt_rr->doffs,
+ opcode = 0,
+ oplen = 0;
+
+ while (rdlen > 3) {
+ // Minimum op code and length
+ opcode = get_ushort(offs);
+ oplen = get_ushort(offs + 2);
+ offs += 4;
+ rdlen -= 4;
+
+ if (rdlen < oplen)
+ break;
+
+ if (opcode == 8 && !m_edns0_ecs && oplen > 3) {
+ // ECS - Client Subnet - RFC7871
+ m_edns0_ecs = true;
+ m_edns0_ecs_family = get_ushort(offs);
+ m_edns0_ecs_source = get_ubyte(offs + 2);
+ m_edns0_ecs_scope = get_ubyte(offs + 3);
+
+ int addrlen = (m_edns0_ecs_source / 8) + (m_edns0_ecs_source % 8 ? 1 : 0);
+ int fill = 0;
+
+ if (addrlen <= (oplen - 4)) {
+ switch (m_edns0_ecs_family) {
+ case 1:
+ fill = 4;
+ m_edns0_ecs_addr_set = true;
+ break;
+ case 2:
+ fill = 16;
+ m_edns0_ecs_addr_set = true;
+ break;
+ }
+
+ int a = 0, b;
+ for (b = 15; fill && b > -1; fill--, b--) {
+ if (a < addrlen) {
+ m_edns0_ecs_addr.__in6_u.__u6_addr8[b] = get_ubyte(offs + 4 + a);
+ a++;
+ } else {
+ m_edns0_ecs_addr.__in6_u.__u6_addr8[b] = 0;
+ }
+ }
+ }
+ }
+
+ rdlen -= oplen;
+ offs += oplen;
+ }
+ }
+ }
+ }
+ void parse()
+ {
+ m_header.parse(*this);
+ int offs = 12;
+ int q = 0;
+ int cnt = m_header.qdcount;
+ while (cnt-- > 0) {
+ offs = m_questions[q].parse(*this, offs);
+ if (offs > m_length) {
+ m_questions[q].qname.reset();
+ m_error = offs;
+ return;
+ }
+ q = 1; // not ++ ignore further Q's
+ }
+ q = 0;
+ cnt = m_header.ancount;
+ while (cnt-- > 0) {
+ offs = m_answer[q].parse(*this, offs);
+ q = 1; // not ++ ignore further Q's
+ if (offs > m_length) {
+ m_error = offs;
+ return;
+ }
+ }
+ q = 0;
+ cnt = m_header.nscount;
+ while (cnt-- > 0) {
+ offs = m_authority[q].parse(*this, offs);
+ q = 1; // not ++ ignore further Q's
+ if (offs > m_length) {
+ m_error = offs;
+ return;
+ }
+ }
+ q = 0;
+ cnt = m_header.arcount;
+ while (cnt-- > 0) {
+ offs = m_additional[q].parse(*this, offs);
+ q = 1; // not ++ ignore further Q's
+ if (offs > m_length) {
+ m_error = offs;
+ return;
+ }
+ if (m_new_opt_rr) {
+ parse_opt_rr();
+ m_new_opt_rr = false;
+ }
+ }
+ if (offs > m_length)
+ m_error = offs;
+ }
+
+ unsigned int get_ubyte(int offs)
+ {
+ if (offs >= m_length)
+ return 0;
+ return int(m_data[offs]);
+ }
+ // returns 16 bit number at byte offset offs
+ unsigned int get_ushort(int offs)
+ {
+ if ((offs + 1) >= m_length)
+ return 0;
+ return (int(m_data[offs]) << 8) | int(m_data[offs + 1]);
+ }
+ uint32_t get_uint32(int offs)
+ {
+ if ((offs + 3) >= m_length)
+ return 0;
+ return (uint32_t(m_data[offs]) << 24) | (uint32_t(m_data[offs + 1]) << 16) | (uint32_t(m_data[offs + 2]) << 8) | uint32_t(m_data[offs + 3]);
+ }
+ bool get_bit(int offs, int bit)
+ {
+ if (offs >= m_length)
+ return 0;
+ return ((get_ushort(offs) << bit) & 0x8000) == 0x8000;
+ }
+ unsigned int get_bits(int offs, int bit, int bits)
+ {
+ if (offs >= m_length)
+ return 0;
+ return ((get_ushort(offs) << bit) & 0xffff) >> (16 - bits);
+ }
+};
+
+class Parse_dns : public Packet_handler {
+public:
+ enum {
+ COLUMN_QNAME = IP_header_to_table::COLUMN_FRAGMENTS + 1,
+ COLUMN_ANAME,
+ COLUMN_MSG_ID,
+ COLUMN_MSG_SIZE,
+ COLUMN_OPCODE,
+ COLUMN_RCODE,
+ COLUMN_EXTENDED_RCODE,
+ COLUMN_EDNS_VERSION,
+ COLUMN_Z,
+ COLUMN_UDP_SIZE,
+ COLUMN_QD_COUNT,
+ COLUMN_AN_COUNT,
+ COLUMN_NS_COUNT,
+ COLUMN_AR_COUNT,
+ COLUMN_QTYPE,
+ COLUMN_QCLASS,
+ COLUMN_QLABELS,
+ COLUMN_ATYPE,
+ COLUMN_ACLASS,
+ COLUMN_ALABELS,
+ COLUMN_ATTL,
+ COLUMN_AA,
+ COLUMN_TC,
+ COLUMN_RD,
+ COLUMN_CD,
+ COLUMN_RA,
+ COLUMN_AD,
+ COLUMN_DO,
+ COLUMN_EDNS0,
+ COLUMN_QR,
+ COLUMN_EDNS0_ECS,
+ COLUMN_EDNS0_ECS_FAMILY,
+ COLUMN_EDNS0_ECS_SOURCE,
+ COLUMN_EDNS0_ECS_SCOPE,
+ COLUMN_EDNS0_ECS_ADDRESS,
+ };
+
+ Parse_dns(bool escape_dnsnames);
+
+ virtual void on_table_created(Table* table, const std::vector<int>& columns);
+ virtual Packet::ParseResult parse(Packet& packet, const std::vector<int>& columns, Row& destination_row, bool sample);
+
+ void add_packet_columns();
+ void add_lookup_tables();
+
+private:
+ Str_conv converter;
+
+ IP_header_to_table m_ip_helper;
+
+ Int_accessor acc_s;
+ Int_accessor acc_us;
+ Int_accessor acc_ether_type;
+ Int_accessor acc_protocol;
+ Int_accessor acc_src_port;
+ Int_accessor acc_msg_id;
+ Int_accessor acc_msg_size;
+ Int_accessor acc_opcode;
+ Int_accessor acc_rcode;
+ Int_accessor acc_extended_rcode;
+ Int_accessor acc_edns_version;
+ Int_accessor acc_z;
+ Int_accessor acc_udp_size;
+ Int_accessor acc_qd_count;
+ Int_accessor acc_an_count;
+ Int_accessor acc_ns_count;
+ Int_accessor acc_ar_count;
+ Int_accessor acc_qtype;
+ Int_accessor acc_qclass;
+ Int_accessor acc_qlabels;
+ Int_accessor acc_atype;
+ Int_accessor acc_aclass;
+ Int_accessor acc_attl;
+ Int_accessor acc_alabels;
+ Int_accessor acc_edns0_ecs_family;
+ Int_accessor acc_edns0_ecs_source;
+ Int_accessor acc_edns0_ecs_scope;
+ Bool_accessor acc_qr;
+ Bool_accessor acc_aa;
+ Bool_accessor acc_tc;
+ Bool_accessor acc_rd;
+ Bool_accessor acc_cd;
+ Bool_accessor acc_ra;
+ Bool_accessor acc_ad;
+ Bool_accessor acc_do;
+ Bool_accessor acc_edns0;
+ Bool_accessor acc_edns0_ecs;
+ Text_accessor acc_qname;
+ Text_accessor acc_aname;
+ Text_accessor acc_src_addr;
+ Text_accessor acc_dst_addr;
+ Text_accessor acc_edns0_ecs_address;
+};
+
+} // namespace packetq
+
+#endif // __packetq_dns_h