/*
* 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 .
*/
#include "dns.h"
#include "output.h"
#include "packet_handler.h"
#include "packetq.h"
#include "tcp.h"
#include
#include
#include
namespace packetq {
char visible_char_map[256];
void fill_in_visible_char_map()
{
for (int i = 0; i < 256; ++i) {
visible_char_map[i] = isgraph(i) ? i : '$';
}
}
void fill_in_visible_char_map_rfc1035()
{
for (int i = 0; i < 256; ++i) {
if ((i >= 'a' && i <= 'z')
|| (i >= 'A' && i <= 'Z')
|| (i >= '0' && i <= '9')
|| (i == '-' || i == '_')) {
visible_char_map[i] = i;
} else { // espaping needed
visible_char_map[i] = 0;
}
}
}
Parse_dns::Parse_dns(bool escape_dnsnames)
{
if (escape_dnsnames) {
fill_in_visible_char_map_rfc1035();
} else {
fill_in_visible_char_map();
}
table_name = "dns";
add_packet_columns();
add_lookup_tables();
}
void Parse_dns::add_packet_columns()
{
m_ip_helper.add_packet_columns(*this);
add_packet_column("qname", "", Coltype::_text, COLUMN_QNAME);
add_packet_column("aname", "", Coltype::_text, COLUMN_ANAME);
add_packet_column("msg_id", "", Coltype::_int, COLUMN_MSG_ID);
add_packet_column("msg_size", "", Coltype::_int, COLUMN_MSG_SIZE);
add_packet_column("opcode", "", Coltype::_int, COLUMN_OPCODE);
add_packet_column("rcode", "", Coltype::_int, COLUMN_RCODE);
add_packet_column("extended_rcode", "", Coltype::_int, COLUMN_EXTENDED_RCODE);
add_packet_column("edns_version", "", Coltype::_int, COLUMN_EDNS_VERSION);
add_packet_column("z", "", Coltype::_int, COLUMN_Z);
add_packet_column("udp_size", "", Coltype::_int, COLUMN_UDP_SIZE);
add_packet_column("qd_count", "", Coltype::_int, COLUMN_QD_COUNT);
add_packet_column("an_count", "", Coltype::_int, COLUMN_AN_COUNT);
add_packet_column("ns_count", "", Coltype::_int, COLUMN_NS_COUNT);
add_packet_column("ar_count", "", Coltype::_int, COLUMN_AR_COUNT);
add_packet_column("qtype", "", Coltype::_int, COLUMN_QTYPE);
add_packet_column("qclass", "", Coltype::_int, COLUMN_QCLASS);
add_packet_column("qlabels", "", Coltype::_int, COLUMN_QLABELS);
add_packet_column("atype", "", Coltype::_int, COLUMN_ATYPE);
add_packet_column("aclass", "", Coltype::_int, COLUMN_ACLASS);
add_packet_column("attl", "", Coltype::_int, COLUMN_ATTL);
add_packet_column("alabels", "", Coltype::_int, COLUMN_ALABELS);
add_packet_column("aa", "", Coltype::_bool, COLUMN_AA);
add_packet_column("tc", "", Coltype::_bool, COLUMN_TC);
add_packet_column("rd", "", Coltype::_bool, COLUMN_RD);
add_packet_column("cd", "", Coltype::_bool, COLUMN_CD);
add_packet_column("ra", "", Coltype::_bool, COLUMN_RA);
add_packet_column("ad", "", Coltype::_bool, COLUMN_AD);
add_packet_column("do", "", Coltype::_bool, COLUMN_DO);
add_packet_column("edns0", "", Coltype::_bool, COLUMN_EDNS0);
add_packet_column("qr", "", Coltype::_bool, COLUMN_QR);
add_packet_column("edns0_ecs", "", Coltype::_bool, COLUMN_EDNS0_ECS);
add_packet_column("edns0_ecs_family", "", Coltype::_int, COLUMN_EDNS0_ECS_FAMILY);
add_packet_column("edns0_ecs_source", "", Coltype::_int, COLUMN_EDNS0_ECS_SOURCE);
add_packet_column("edns0_ecs_scope", "", Coltype::_int, COLUMN_EDNS0_ECS_SCOPE);
add_packet_column("edns0_ecs_address", "", Coltype::_text, COLUMN_EDNS0_ECS_ADDRESS);
}
void Parse_dns::add_lookup_tables()
{
g_db.add_lut("qtype", 1, "A");
g_db.add_lut("qtype", 2, "NS");
g_db.add_lut("qtype", 3, "MD");
g_db.add_lut("qtype", 4, "MF");
g_db.add_lut("qtype", 5, "CNAME");
g_db.add_lut("qtype", 6, "SOA");
g_db.add_lut("qtype", 7, "MB");
g_db.add_lut("qtype", 8, "MG");
g_db.add_lut("qtype", 9, "MR");
g_db.add_lut("qtype", 10, "NULL");
g_db.add_lut("qtype", 11, "WKS");
g_db.add_lut("qtype", 12, "PTR");
g_db.add_lut("qtype", 13, "HINFO");
g_db.add_lut("qtype", 14, "MINFO");
g_db.add_lut("qtype", 15, "MX");
g_db.add_lut("qtype", 16, "TXT");
g_db.add_lut("qtype", 17, "RP");
g_db.add_lut("qtype", 18, "AFSDB");
g_db.add_lut("qtype", 19, "X25");
g_db.add_lut("qtype", 20, "ISDN");
g_db.add_lut("qtype", 21, "RT");
g_db.add_lut("qtype", 22, "NSAP");
g_db.add_lut("qtype", 23, "NSAP-PTR");
g_db.add_lut("qtype", 24, "SIG");
g_db.add_lut("qtype", 25, "KEY");
g_db.add_lut("qtype", 26, "PX");
g_db.add_lut("qtype", 27, "GPOS");
g_db.add_lut("qtype", 28, "AAAA");
g_db.add_lut("qtype", 29, "LOC");
g_db.add_lut("qtype", 30, "NXT");
g_db.add_lut("qtype", 31, "EID");
g_db.add_lut("qtype", 32, "NIMLOC");
g_db.add_lut("qtype", 33, "SRV");
g_db.add_lut("qtype", 34, "ATMA");
g_db.add_lut("qtype", 35, "NAPTR");
g_db.add_lut("qtype", 36, "KX");
g_db.add_lut("qtype", 37, "CERT");
g_db.add_lut("qtype", 38, "A6");
g_db.add_lut("qtype", 39, "DNAME");
g_db.add_lut("qtype", 40, "SINK");
g_db.add_lut("qtype", 41, "OPT");
g_db.add_lut("qtype", 42, "APL");
g_db.add_lut("qtype", 43, "DS");
g_db.add_lut("qtype", 44, "SSHFP");
g_db.add_lut("qtype", 45, "IPSECKEY");
g_db.add_lut("qtype", 46, "RRSIG");
g_db.add_lut("qtype", 47, "NSEC");
g_db.add_lut("qtype", 48, "DNSKEY");
g_db.add_lut("qtype", 49, "DHCID");
g_db.add_lut("qtype", 50, "NSEC3");
g_db.add_lut("qtype", 51, "NSEC3PARAM");
g_db.add_lut("qtype", 52, "TLSA");
g_db.add_lut("qtype", 53, "SMIMEA");
g_db.add_lut("qtype", 55, "HIP");
g_db.add_lut("qtype", 56, "NINFO");
g_db.add_lut("qtype", 57, "RKEY");
g_db.add_lut("qtype", 58, "TALINK");
g_db.add_lut("qtype", 59, "CDS");
g_db.add_lut("qtype", 60, "CDNSKEY");
g_db.add_lut("qtype", 61, "OPENPGPKEY");
g_db.add_lut("qtype", 62, "CSYNC");
g_db.add_lut("qtype", 63, "ZONEMD");
g_db.add_lut("qtype", 64, "SVCB");
g_db.add_lut("qtype", 65, "HTTPS");
g_db.add_lut("qtype", 99, "SPF");
g_db.add_lut("qtype", 100, "UINFO");
g_db.add_lut("qtype", 101, "UID");
g_db.add_lut("qtype", 102, "GID");
g_db.add_lut("qtype", 103, "UNSPEC");
g_db.add_lut("qtype", 104, "NID");
g_db.add_lut("qtype", 105, "L32");
g_db.add_lut("qtype", 106, "L64");
g_db.add_lut("qtype", 107, "LP");
g_db.add_lut("qtype", 108, "EUI48");
g_db.add_lut("qtype", 109, "EUI64");
g_db.add_lut("qtype", 249, "TKEY");
g_db.add_lut("qtype", 250, "TSIG");
g_db.add_lut("qtype", 251, "IXFR");
g_db.add_lut("qtype", 252, "AXFR");
g_db.add_lut("qtype", 253, "MAILB");
g_db.add_lut("qtype", 254, "MAILA");
g_db.add_lut("qtype", 255, "*");
g_db.add_lut("qtype", 256, "URI");
g_db.add_lut("qtype", 257, "CAA");
g_db.add_lut("qtype", 258, "AVC");
g_db.add_lut("qtype", 259, "DOA");
g_db.add_lut("qtype", 260, "AMTRELAY");
g_db.add_lut("qtype", 32768, "TA");
g_db.add_lut("qtype", 32769, "DLV");
g_db.add_lut("rcode", 0, "NoError");
g_db.add_lut("rcode", 1, "FormErr");
g_db.add_lut("rcode", 2, "ServFail");
g_db.add_lut("rcode", 3, "NXDomain");
g_db.add_lut("rcode", 4, "NotImp");
g_db.add_lut("rcode", 5, "Refused");
g_db.add_lut("rcode", 6, "YXDomain");
g_db.add_lut("rcode", 7, "YXRRSet");
g_db.add_lut("rcode", 8, "NXRRSet");
g_db.add_lut("rcode", 9, "NotAuth");
g_db.add_lut("rcode", 10, "NotZone");
g_db.add_lut("rcode", 16, "BADVERS");
g_db.add_lut("rcode", 16, "BADSIG");
g_db.add_lut("rcode", 17, "BADKEY");
g_db.add_lut("rcode", 18, "BADTIME");
g_db.add_lut("rcode", 19, "BADMODE");
g_db.add_lut("rcode", 20, "BADNAME");
g_db.add_lut("rcode", 21, "BADALG");
g_db.add_lut("rcode", 22, "BADTRUNC");
}
void Parse_dns::on_table_created(Table* table, const std::vector& columns)
{
m_ip_helper.on_table_created(table, columns);
acc_msg_id = table->get_accessor("msg_id");
acc_msg_size = table->get_accessor("msg_size");
acc_opcode = table->get_accessor("opcode");
acc_rcode = table->get_accessor("rcode");
acc_extended_rcode = table->get_accessor("extended_rcode");
acc_edns_version = table->get_accessor("edns_version");
acc_z = table->get_accessor("z");
acc_udp_size = table->get_accessor("udp_size");
acc_qd_count = table->get_accessor("qd_count");
acc_an_count = table->get_accessor("an_count");
acc_ns_count = table->get_accessor("ns_count");
acc_ar_count = table->get_accessor("ar_count");
acc_qtype = table->get_accessor("qtype");
acc_qclass = table->get_accessor("qclass");
acc_qlabels = table->get_accessor("qlabels");
acc_atype = table->get_accessor("atype");
acc_aclass = table->get_accessor("aclass");
acc_attl = table->get_accessor("attl");
acc_alabels = table->get_accessor("alabels");
acc_qr = table->get_accessor("qr");
acc_aa = table->get_accessor("aa");
acc_tc = table->get_accessor("tc");
acc_rd = table->get_accessor("rd");
acc_cd = table->get_accessor("cd");
acc_ra = table->get_accessor("ra");
acc_ad = table->get_accessor("ad");
acc_do = table->get_accessor("do");
acc_edns0 = table->get_accessor("edns0");
acc_qname = table->get_accessor("qname");
acc_aname = table->get_accessor("aname");
acc_edns0_ecs = table->get_accessor("edns0_ecs");
acc_edns0_ecs_family = table->get_accessor("edns0_ecs_family");
acc_edns0_ecs_source = table->get_accessor("edns0_ecs_source");
acc_edns0_ecs_scope = table->get_accessor("edns0_ecs_scope");
acc_edns0_ecs_address = table->get_accessor("edns0_ecs_address");
}
Packet::ParseResult Parse_dns::parse(Packet& packet, const std::vector& columns, Row& destination_row, bool sample)
{
if (not(packet.m_len >= 12 && (packet.m_ip_header.proto == IPPROTO_UDP || packet.m_ip_header.proto == IPPROTO_TCP)))
return Packet::ERROR;
if (!sample)
return Packet::NOT_SAMPLED;
unsigned char* ddata = packet.m_data;
int dlength = packet.m_len;
if (packet.m_ip_header.proto == IPPROTO_TCP) {
int dns_size = (int(ddata[0]) << 8) | ddata[1];
ddata += 2;
dlength -= 2;
if (dns_size != dlength)
return Packet::ERROR;
}
DNSMessage message(ddata, dlength, packet.m_ip_header);
DNSMessage::Header& header = message.m_header;
IP_header& ip_header = message.m_ip_header;
if (message.m_error != 0)
return Packet::ERROR;
if (!header.qr and header.qdcount == 0)
return Packet::ERROR;
Row* r = &destination_row;
m_ip_helper.assign(r, &ip_header, columns);
for (auto i = columns.begin(), end = columns.end(); i != end; ++i) {
switch (*i) {
case COLUMN_MSG_ID:
acc_msg_id.value(r) = header.id;
break;
case COLUMN_MSG_SIZE:
acc_msg_size.value(r) = message.m_length;
break;
case COLUMN_QR:
acc_qr.value(r) = header.qr;
break;
case COLUMN_AA:
acc_aa.value(r) = header.aa;
break;
case COLUMN_TC:
acc_tc.value(r) = header.tc;
break;
case COLUMN_RD:
acc_rd.value(r) = header.rd;
break;
case COLUMN_CD:
acc_cd.value(r) = header.cd;
break;
case COLUMN_RA:
acc_ra.value(r) = header.ra;
break;
case COLUMN_AD:
acc_ad.value(r) = header.ad;
break;
case COLUMN_OPCODE:
acc_opcode.value(r) = header.opcode;
break;
case COLUMN_RCODE:
acc_rcode.value(r) = header.rcode;
break;
case COLUMN_QD_COUNT:
acc_qd_count.value(r) = header.qdcount;
break;
case COLUMN_AN_COUNT:
acc_an_count.value(r) = header.ancount;
break;
case COLUMN_NS_COUNT:
acc_ns_count.value(r) = header.nscount;
break;
case COLUMN_AR_COUNT:
acc_ar_count.value(r) = header.arcount;
break;
case COLUMN_QTYPE:
acc_qtype.value(r) = message.m_questions[0].qtype;
break;
case COLUMN_QCLASS:
acc_qclass.value(r) = message.m_questions[0].qclass;
break;
case COLUMN_QLABELS:
acc_qlabels.value(r) = message.m_questions[0].qname.labels;
break;
case COLUMN_QNAME:
acc_qname.value(r) = RefCountString::construct(message.m_questions[0].qname.fqdn);
break;
case COLUMN_EDNS0:
acc_edns0.value(r) = message.m_edns0 ? 1 : 0;
break;
case COLUMN_DO:
acc_do.value(r) = message.m_edns0 ? message.m_do : 0;
break;
case COLUMN_EXTENDED_RCODE:
acc_extended_rcode.value(r) = message.m_edns0 ? message.m_extended_rcode : 0;
break;
case COLUMN_EDNS_VERSION:
acc_edns_version.value(r) = message.m_edns0 ? message.m_edns_version : 0;
break;
case COLUMN_Z:
acc_z.value(r) = message.m_edns0 ? message.m_z : 0;
break;
case COLUMN_UDP_SIZE:
acc_udp_size.value(r) = message.m_edns0 ? message.m_udp_size : 0;
break;
case COLUMN_ANAME:
acc_aname.value(r) = header.ancount ? RefCountString::construct(message.m_answer[0].name.fqdn) : RefCountString::construct("");
break;
case COLUMN_ATYPE:
acc_atype.value(r) = header.ancount ? message.m_answer[0].type : 0;
break;
case COLUMN_ACLASS:
acc_aclass.value(r) = header.ancount ? message.m_answer[0].rr_class : 0;
break;
case COLUMN_ATTL:
acc_attl.value(r) = header.ancount ? message.m_answer[0].ttl : 0;
break;
case COLUMN_ALABELS:
acc_alabels.value(r) = header.ancount ? message.m_answer[0].name.labels : 0;
break;
case COLUMN_EDNS0_ECS:
acc_edns0_ecs.value(r) = message.m_edns0_ecs ? 1 : 0;
break;
case COLUMN_EDNS0_ECS_FAMILY:
acc_edns0_ecs_family.value(r) = message.m_edns0_ecs_family;
break;
case COLUMN_EDNS0_ECS_SOURCE:
acc_edns0_ecs_source.value(r) = message.m_edns0_ecs_source;
break;
case COLUMN_EDNS0_ECS_SCOPE:
acc_edns0_ecs_scope.value(r) = message.m_edns0_ecs_scope;
break;
case COLUMN_EDNS0_ECS_ADDRESS:
if (message.m_edns0_ecs_addr_set && message.m_edns0_ecs_family == 1)
acc_edns0_ecs_address.value(r) = v4_addr2str(message.m_edns0_ecs_addr);
else if (message.m_edns0_ecs_addr_set && message.m_edns0_ecs_family == 2)
acc_edns0_ecs_address.value(r) = v6_addr2str(message.m_edns0_ecs_addr);
else
acc_edns0_ecs_address.value(r) = RefCountString::construct("");
break;
}
}
return Packet::OK;
}
} // namespace packetq