From: Adrian Mamolea (admamole) Date: Mon, 28 Apr 2025 18:39:39 +0000 (+0000) Subject: Pull request #4705: extractor: extend dns support X-Git-Tag: 3.7.4.0~8 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=8c0d0ca05e152ff93c93507e62a7c667c223f4cf;p=thirdparty%2Fsnort3.git Pull request #4705: extractor: extend dns support Merge in SNORT/snort3 from ~ADMAMOLE/snort3:dns3 to master Squashed commit of the following: commit a66400442cc0567df4607d23f5a070e670b6d76a Author: Adrian Mamolea Date: Tue Apr 15 13:55:28 2025 -0400 extractor: extend dns support --- diff --git a/doc/user/extractor.txt b/doc/user/extractor.txt index 08f6326a2..8bb10c2b5 100644 --- a/doc/user/extractor.txt +++ b/doc/user/extractor.txt @@ -130,8 +130,13 @@ Fields supported for DNS: * `auth` - The list of authoritative responses * `addl` - The list of additional responses -In the answers, auth, and addl lists the decoding of the following RR types is supported: -A, AAAA, CNAME, DS, MX, NS, NSEC, PTR, RRSIG, SOA, TXT +The answers, auth, and addl lists contain all the RRs found in the corresponding message sections. Each RR is +represented by a summary of its decoding. For these RR types the decoding contains type specific information +(ip addresses, domain names, etc.): A, AAAA, BIND9 signing, CNAME, DNSKEY, DS, LOC, MX, NS, NSEC, OPT, PTR, RRSIG, +SOA, SPF, SRV, SSHFP, TXT. For these RR types: CAA, HINFO, HTTPS, NSEC3, NSEC3PARAM, SVCB, TKEY, TSIG, the decoding +contains only the name of the RR type. This is also the default decoding applied to all RR types that don't have +a type specific decoder. When the name of the type is not known it is decoded as UNKNOWN-N, where N is RR type +numeric value. Fields supported for connection: diff --git a/src/pub_sub/dns_events.cc b/src/pub_sub/dns_events.cc index 85990b59c..47e8c4f5b 100644 --- a/src/pub_sub/dns_events.cc +++ b/src/pub_sub/dns_events.cc @@ -50,112 +50,9 @@ static const std::string& class_name(uint16_t query_class) return it != class_names.end() ? it->second : unknown; } -static const std::string& qtype_name(uint16_t query_type) -{ - static const std::map qtype_names = - { - {1, "A"}, // RFC 1035 - {2, "NS"}, // RFC 1035 - {3, "MD"}, // RFC 1035 - {4, "MF"}, // RFC 1035 - {5, "CNAME"}, // RFC 1035 - {6, "SOA"}, // RFC 1035 - {7, "MB"}, // RFC 1035 - {8, "MG"}, // RFC 1035 - {9, "MR"}, // RFC 1035 - {10, "NULL"}, // RFC 1035 - {11, "WKS"}, // RFC 1035 - {12, "PTR"}, // RFC 1035 - {13, "HINFO"}, // RFC 1035 - {14, "MINFO"}, // RFC 1035 - {15, "MX"}, // RFC 1035 - {16, "TXT"}, // RFC 1035 - {17, "RP"}, // RFC 1183 - {18, "AFSDB"}, // RFC 1183 - {19, "X25"}, // RFC 1183 - {20, "ISDN"}, // RFC 1183 - {21, "RT"}, // RFC 1183 - {22, "NSAP"}, // RFC 1706 - {23, "NSAP_PTR"}, // RFC 1348 - {24, "SIG"}, // RFC 2536 - {25, "KEY"}, // RFC 2536 - {26, "PX"}, // RFC 2163 - {27, "GPOS"}, // RFC 1712 - {28, "AAAA"}, // RFC 3596 - {29, "LOC"}, // RFC 1876 - {30, "NXT"}, // RFC 2535 - {31, "EID"}, - {32, "NIMLOC"}, - {33, "SRV"}, // RFC 2782 - {34, "ATMA"}, - {35, "NAPTR"}, // RFC 3403 - {36, "KX"}, // RFC 2230 - {37, "CERT"}, // RFC 4398 - {38, "A6"}, // RFC 2874 - {39, "DNAME"}, // RFC 6672 - {40, "SINK"}, - {41, "OPT"}, // RFC 6891 - {42, "APL"}, // RFC 3123 - {43, "DS"}, // RFC 4034 - {44, "SSHFP"}, // RFC 4255 - {45, "IPSECKEY"}, // RFC 4025 - {46, "RRSIG"}, // RFC 4034 - {47, "NSEC"}, // RFC 4034 - {48, "DNSKEY"}, // RFC 4034 - {49, "DHCID"}, // RFC 4701 - {50, "NSEC3"}, // RFC 5155 - {51, "NSEC3PARAM"}, // RFC 5155 - {52, "TLSA"}, // RFC 6698 - {53, "SMIMEA"}, // RFC 8162 - {55, "HIP"}, // RFC 8005 - {56, "NINFO"}, - {57, "RKEY"}, - {58, "TALINK"}, - {59, "CDS"}, // RFC 7344 - {60, "CDNSKEY"}, // RFC 7344 - {61, "OPENPGPKEY"}, // RFC 7929 - {62, "CSYNC"}, // RFC 7477 - {63, "ZONEMD"}, // RFC 8976 - {64, "SVCB"}, // RFC 9460 - {65, "HTTPS"}, // RFC 9460 - {66, "DSYNC"}, - {99, "SPF"}, // RFC 7208 - {100, "UINFO"}, - {101, "UID"}, - {102, "GID"}, - {103, "UNSPEC"}, - {104, "NID"}, // RFC 6742 - {105, "L32"}, // RFC 6742 - {106, "L64"}, // RFC 6742 - {107, "LP"}, // RFC 6742 - {108, "EUI48"}, // RFC 7043 - {109, "EUI64"}, // RFC 7043 - {249, "TKEY"}, // RFC 2930 - {250, "TSIG"}, // RFC 8945 - {251, "IXFR"}, // RFC 1995 - {252, "AXFR"}, // RFC 1035 - {253, "MAILB"}, // RFC 1035 - {254, "MAILA"}, // RFC 1035 - {255, "*"}, // RFC 1035, also known as ANY - {256, "URI"}, // RFC 7553 - {257, "CAA"}, // RFC 8659 - {32768, "TA"}, - {32769, "DLV"}, // RFC 4431 - {65281, "WINS"}, // Microsoft - {65282, "WINS-R"}, // Microsoft - {65521, "INTEGRITY"} // Chromium Design Doc: Querying HTTPSSVC - // Add more QTYPEs as needed - }; - - static const std::string unknown = "UNKNOWN"; - - auto it = qtype_names.find(query_type); - return it != qtype_names.end() ? it->second : unknown; -} - static const std::string& rcode_name(uint16_t rcode) { - static const std::map rcode_names = + static const std::map rcode_names = { {0, "NOERROR"}, // RFC 1035 {1, "FORMERR"}, // RFC 1035 @@ -272,7 +169,7 @@ const std::string& DnsResponseEvent::get_query_type_name() const static const std::string empty = ""; if (session.hdr.questions == 0) return empty; - return qtype_name(get_query_type()); + return DNSData::qtype_name(get_query_type()); } uint8_t DnsResponseEvent::get_rcode() const @@ -332,9 +229,14 @@ const std::string& DnsResponseEvent::get_TTLs() const bool DnsResponseEvent::get_rejected() const { - return session.hdr.flags & DNS_HDR_FLAG_RESPONSE && - session.hdr.flags & DNS_HDR_FLAG_REPLY_CODE_MASK && - !session.hdr.questions; + if (session.hdr.flags & DNS_HDR_FLAG_RESPONSE) + { + if (session.hdr.flags & DNS_HDR_FLAG_REPLY_CODE_MASK && !session.hdr.questions) + return true; + if (session.hdr.answers == 0 && session.hdr.authorities == 0 && session.hdr.additionals == 0) + return true; + } + return false; } const std::string& DnsResponseEvent::get_auth() const diff --git a/src/service_inspectors/dns/dns.cc b/src/service_inspectors/dns/dns.cc index a4fa54ce6..8e92d47e8 100644 --- a/src/service_inspectors/dns/dns.cc +++ b/src/service_inspectors/dns/dns.cc @@ -794,23 +794,11 @@ static uint16_t ParseDNSRData( if (dnsSessionData->publish_response()) dnsSessionData->dns_events.add_fqdn(dnsSessionData->cur_fqdn_event, dnsSessionData->curr_rr.ttl); - bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData); - break; - case DNS_RR_TYPE_NS: - 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: - case DNS_RR_TYPE_RRSIG: - case DNS_RR_TYPE_NSEC: - case DNS_RR_TYPE_DS: 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; + /* An unknown RR type or one w/o special handling, skip */ + bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData); break; } @@ -900,6 +888,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData, bool& ne switch (dnsSessionData->state) { case DNS_RESP_STATE_ANS_RR: /* ANSWERS section */ + dnsSessionData->answer_tabs.emplace_back(data - p->data); for (i=dnsSessionData->curr_rec; ihdr.answers; i++) { bytes_unused = ParseDNSAnswer(data, bytes_unused, dnsSessionData, p, dnsSessionData->answer_tabs); @@ -945,6 +934,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData, bool& ne dnsSessionData->curr_rec = 0; /* Fall through */ case DNS_RESP_STATE_AUTH_RR: /* AUTHORITIES section */ + dnsSessionData->auth_tabs.emplace_back(data - p->data); for (i=dnsSessionData->curr_rec; ihdr.authorities; i++) { bytes_unused = ParseDNSAnswer(data, bytes_unused, dnsSessionData, p, dnsSessionData->auth_tabs); @@ -990,6 +980,7 @@ static void ParseDNSResponseMessage(Packet* p, DNSData* dnsSessionData, bool& ne dnsSessionData->curr_rec = 0; /* Fall through */ case DNS_RESP_STATE_ADD_RR: /* ADDITIONALS section */ + dnsSessionData->addl_tabs.emplace_back(data - p->data); for (i=dnsSessionData->curr_rec; ihdr.additionals; i++) { bytes_unused = ParseDNSAnswer(data, bytes_unused, dnsSessionData, p, dnsSessionData->addl_tabs); diff --git a/src/service_inspectors/dns/dns.h b/src/service_inspectors/dns/dns.h index 07de79843..5a954607f 100644 --- a/src/service_inspectors/dns/dns.h +++ b/src/service_inspectors/dns/dns.h @@ -94,7 +94,6 @@ struct DNSNameState } }; -// FIXIT-L remove obsolete flags? #define DNS_RR_TYPE_A 0x0001 #define DNS_RR_TYPE_NS 0x0002 #define DNS_RR_TYPE_MD 0x0003 // obsolete @@ -112,9 +111,23 @@ struct DNSNameState #define DNS_RR_TYPE_MX 0x000f #define DNS_RR_TYPE_TXT 0x0010 #define DNS_RR_TYPE_AAAA 0x001c +#define DNS_RR_TYPE_LOC 0x001d +#define DNS_RR_TYPE_SRV 0x0021 +#define DNS_RR_TYPE_OPT 0x0029 #define DNS_RR_TYPE_RRSIG 0x002e #define DNS_RR_TYPE_NSEC 0x002f #define DNS_RR_TYPE_DS 0x002b +#define DNS_RR_TYPE_SSHFP 0x002c +#define DNS_RR_TYPE_DNSKEY 0x0030 +#define DNS_RR_TYPE_NSEC3 0x0032 +#define DNS_RR_TYPE_NSEC3PARAM 0x0033 +#define DNS_RR_TYPE_SVCB 0x0040 +#define DNS_RR_TYPE_HTTPS 0x0041 +#define DNS_RR_TYPE_SPF 0x0063 +#define DNS_RR_TYPE_TKEY 0x00f9 +#define DNS_RR_TYPE_TSIG 0x00fa +#define DNS_RR_TYPE_CAA 0x0101 +#define DNS_RR_TYPE_BIND9_SIGNING 0xfffe #define DNS_RR_PTR 0xC0 @@ -221,7 +234,7 @@ struct DNSData bool has_events() const; bool valid_dns(const DNSHdr&) const; - void decode_rdata(const snort::Packet* p, const uint8_t* rdata, uint16_t rdlength, + void decode_rdata(const snort::Packet* p, const uint8_t* rr, const uint8_t* rdata, uint16_t rdlength, uint16_t type, std::string& rdata_str) const; void get_rr_data(const snort::Packet *p, const std::vector& tabs, std::string& rrs, std::string* ttls = nullptr) const; @@ -229,6 +242,8 @@ struct DNSData { get_rr_data(p, answer_tabs, answers, &ttls); } void get_auth(const snort::Packet *p, std::string& auth) const { get_rr_data(p, auth_tabs, auth); } void get_addl(const snort::Packet *p, std::string& addl) const { get_rr_data(p, addl_tabs, addl); } + + static const std::string& qtype_name(uint16_t query_type, bool* is_unknown = nullptr); }; DNSData* get_dns_session_data(snort::Packet* p, bool from_server, DNSData& udpSessionData); diff --git a/src/service_inspectors/dns/dns_rr_decoder.cc b/src/service_inspectors/dns/dns_rr_decoder.cc index daaa2bc04..1d029206e 100644 --- a/src/service_inspectors/dns/dns_rr_decoder.cc +++ b/src/service_inspectors/dns/dns_rr_decoder.cc @@ -23,11 +23,126 @@ #include "dns.h" +#include +#include #include #include "sfip/sf_ip.h" using namespace snort; +const std::string& DNSData::qtype_name(uint16_t query_type, bool* is_unknown) +{ + static const std::map qtype_names = + { + {1, "A"}, // RFC 1035 + {2, "NS"}, // RFC 1035 + {3, "MD"}, // RFC 1035 + {4, "MF"}, // RFC 1035 + {5, "CNAME"}, // RFC 1035 + {6, "SOA"}, // RFC 1035 + {7, "MB"}, // RFC 1035 + {8, "MG"}, // RFC 1035 + {9, "MR"}, // RFC 1035 + {10, "NULL"}, // RFC 1035 + {11, "WKS"}, // RFC 1035 + {12, "PTR"}, // RFC 1035 + {13, "HINFO"}, // RFC 1035 + {14, "MINFO"}, // RFC 1035 + {15, "MX"}, // RFC 1035 + {16, "TXT"}, // RFC 1035 + {17, "RP"}, // RFC 1183 + {18, "AFSDB"}, // RFC 1183 + {19, "X25"}, // RFC 1183 + {20, "ISDN"}, // RFC 1183 + {21, "RT"}, // RFC 1183 + {22, "NSAP"}, // RFC 1706 + {23, "NSAP_PTR"}, // RFC 1348 + {24, "SIG"}, // RFC 2536 + {25, "KEY"}, // RFC 2536 + {26, "PX"}, // RFC 2163 + {27, "GPOS"}, // RFC 1712 + {28, "AAAA"}, // RFC 3596 + {29, "LOC"}, // RFC 1876 + {30, "NXT"}, // RFC 2535 + {31, "EID"}, + {32, "NIMLOC"}, + {33, "SRV"}, // RFC 2782 + {34, "ATMA"}, + {35, "NAPTR"}, // RFC 3403 + {36, "KX"}, // RFC 2230 + {37, "CERT"}, // RFC 4398 + {38, "A6"}, // RFC 2874 + {39, "DNAME"}, // RFC 6672 + {40, "SINK"}, + {41, "OPT"}, // RFC 6891 + {42, "APL"}, // RFC 3123 + {43, "DS"}, // RFC 4034 + {44, "SSHFP"}, // RFC 4255 + {45, "IPSECKEY"}, // RFC 4025 + {46, "RRSIG"}, // RFC 4034 + {47, "NSEC"}, // RFC 4034 + {48, "DNSKEY"}, // RFC 4034 + {49, "DHCID"}, // RFC 4701 + {50, "NSEC3"}, // RFC 5155 + {51, "NSEC3PARAM"}, // RFC 5155 + {52, "TLSA"}, // RFC 6698 + {53, "SMIMEA"}, // RFC 8162 + {55, "HIP"}, // RFC 8005 + {56, "NINFO"}, + {57, "RKEY"}, + {58, "TALINK"}, + {59, "CDS"}, // RFC 7344 + {60, "CDNSKEY"}, // RFC 7344 + {61, "OPENPGPKEY"}, // RFC 7929 + {62, "CSYNC"}, // RFC 7477 + {63, "ZONEMD"}, // RFC 8976 + {64, "SVCB"}, // RFC 9460 + {65, "HTTPS"}, // RFC 9460 + {66, "DSYNC"}, + {99, "SPF"}, // RFC 7208 + {100, "UINFO"}, + {101, "UID"}, + {102, "GID"}, + {103, "UNSPEC"}, + {104, "NID"}, // RFC 6742 + {105, "L32"}, // RFC 6742 + {106, "L64"}, // RFC 6742 + {107, "LP"}, // RFC 6742 + {108, "EUI48"}, // RFC 7043 + {109, "EUI64"}, // RFC 7043 + {249, "TKEY"}, // RFC 2930 + {250, "TSIG"}, // RFC 8945 + {251, "IXFR"}, // RFC 1995 + {252, "AXFR"}, // RFC 1035 + {253, "MAILB"}, // RFC 1035 + {254, "MAILA"}, // RFC 1035 + {255, "*"}, // RFC 1035, also known as ANY + {256, "URI"}, // RFC 7553 + {257, "CAA"}, // RFC 8659 + {32768, "TA"}, + {32769, "DLV"}, // RFC 4431 + {65281, "WINS"}, // Microsoft + {65282, "WINS-R"}, // Microsoft + {65521, "INTEGRITY"}, // Chromium Design Doc: Querying HTTPSSVC + {65534, "BIND9S"} // BIND9 signing signal + // Add more QTYPEs as needed + }; + + static const std::string unknown = "UNKNOWN"; + + auto it = qtype_names.find(query_type); + if (it == qtype_names.end()) + { + if (is_unknown) + *is_unknown = true; + return unknown; + } + + if (is_unknown) + *is_unknown = false; + return it->second; +} + static void decode_ip(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str, uint16_t type) { int family = (type == DNS_RR_TYPE_A) ? AF_INET : AF_INET6; @@ -116,6 +231,60 @@ static void decode_domain_name(const uint8_t* rdata, uint16_t rdlength, } } +static void decode_bind9_signing(std::string& rdata_str) +{ + static const std::string bind9_signing = "BIND9" + part_sep + "signing" + part_sep + "signal"; + rdata_str.append(bind9_signing); +} + +static void decode_dnskey(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str) +{ + static const std::string dnskey_prefix = "DNSKEY" + part_sep; + static const unsigned ALGORITHM_OFFSET = 3; // size is one byte + static const unsigned PUBLIC_KEY_OFFSET = 4; + + if (rdlength <= PUBLIC_KEY_OFFSET) + return; // incomplete DNSKEY record + + rdata_str.append(dnskey_prefix); + rdata_str.append(std::to_string(rdata[ALGORITHM_OFFSET])); +} + +static void decode_ds(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str) +{ + static const std::string ds_prefix = "DS" + part_sep; + static const unsigned ALGORITHM_OFFSET = 2; // size is one byte + static const unsigned DIGEST_TYPE_OFFSET = 3; // size is one byte + static const unsigned DIGEST_OFFSET = 4; + + if (rdlength <= DIGEST_OFFSET) + return; // incomplete DS record + + rdata_str.append(ds_prefix); + rdata_str.append(std::to_string(rdata[ALGORITHM_OFFSET])); + rdata_str.append(part_sep); + rdata_str.append(std::to_string(rdata[DIGEST_TYPE_OFFSET])); +} + +static void decode_loc(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str) +{ + static const std::string loc_prefix = "LOC" + part_sep; + static const unsigned SIZE_OFFSET = 1; // size is one byte + static const unsigned HORIZ_PRE_OFFSET = 2; // size is one byte + static const unsigned VERT_PRE_OFFSET = 3; // size is one byte + static const unsigned LATITUDE_OFFSET = 4; + + if (rdlength <= LATITUDE_OFFSET) + return; // incomplete LOC record + + rdata_str.append(loc_prefix); + rdata_str.append(std::to_string(rdata[SIZE_OFFSET])); + rdata_str.append(part_sep); + rdata_str.append(std::to_string(rdata[HORIZ_PRE_OFFSET])); + rdata_str.append(part_sep); + rdata_str.append(std::to_string(rdata[VERT_PRE_OFFSET])); +} + static void decode_mx(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str, const Packet* p) { static const unsigned EXCHANGE_OFFSET = 2; @@ -127,6 +296,37 @@ static void decode_mx(const uint8_t* rdata, uint16_t rdlength, std::string& rdat decode_domain_name(rdata, rdlength, rdata_str, p); } +static void decode_nsec(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str, + const Packet* p, const uint8_t* rr_domain_name) +{ + static const std::string nsec_prefix = "NSEC" + part_sep; + static const unsigned RDATA_OFFSET = 10; + const uint8_t* rr_domain_name_end = rdata - RDATA_OFFSET; + uint16_t rr_domain_name_len = rr_domain_name_end - rr_domain_name; + + rdata_str.append(nsec_prefix); + decode_domain_name(rr_domain_name, rr_domain_name_len, rdata_str, p); + rdata_str.append(part_sep); + decode_domain_name(rdata, rdlength, rdata_str); +} + +static void decode_opt(const uint8_t* rdata, std::string& rdata_str) +{ + static const std::string opt_prefix = "OPT" + part_sep; + static const unsigned RDATA_OFFSET = 10; + static const unsigned EXTENDED_RCODE_OFFSET = 4; // size is one byte + static const unsigned DO_Z_OFFSET = 6; // size is two bytes + static const unsigned DO_MASK = 0x8000; + + const uint8_t* rr_type = rdata - RDATA_OFFSET; + auto do_z_flags = (uint16_t)rr_type[DO_Z_OFFSET] << 8 | rr_type[DO_Z_OFFSET + 1]; + + rdata_str.append(opt_prefix); + rdata_str.append(std::to_string(rr_type[EXTENDED_RCODE_OFFSET])); + rdata_str.append(part_sep); + rdata_str.append(std::to_string((do_z_flags & DO_MASK) >> 15)); +} + static void decode_rrsig(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str) { static const std::string rrsig_prefix = "RRSIG" + part_sep; @@ -145,35 +345,73 @@ static void decode_rrsig(const uint8_t* rdata, uint16_t rdlength, std::string& r decode_domain_name(rdata, rdlength, rdata_str); } -static void decode_nsec(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str, - const std::string& resp_query) +static void decode_spf(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str) { - static const std::string nsec_prefix = "NSEC" + part_sep; + static const std::string spf_prefix = "SPF" + part_sep; - rdata_str.append(nsec_prefix); - rdata_str.append(resp_query); + if (rdlength <= 1) + return; // incomplete SPF record + + rdata_str.append(spf_prefix); + + uint8_t txt_len = *rdata; + rdata_str.append(std::to_string(txt_len)); rdata_str.append(part_sep); - decode_domain_name(rdata, rdlength, rdata_str); + rdata++; + rdlength--; + + uint8_t actual_len = rdlength > txt_len ? txt_len : rdlength; + rdata_str.append((const char*)rdata, actual_len); } -static void decode_ds(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str) +static void decode_srv(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str, const Packet* p) { - static const std::string ds_prefix = "DS" + part_sep; - static const unsigned DIGEST_OFFSET = 4; + static const unsigned TARGET_OFFSET = 6; - if (rdlength <= DIGEST_OFFSET) - return; // incomplete DS record + if (rdlength <= TARGET_OFFSET) + return; // incomplete SRV record - rdata_str.append(ds_prefix); - rdata_str.append(std::to_string(rdata[2])); // algorithm - rdata_str.append(part_sep); - rdata_str.append(std::to_string(rdata[3])); // digest type + rdata += TARGET_OFFSET; + rdlength -= TARGET_OFFSET; + decode_domain_name(rdata, rdlength, rdata_str, p); +} + +static void decode_sshfp(const uint8_t* rdata, uint16_t rdlength, std::string& rdata_str) +{ + static const std::string sshfp_prefix = "SSHFP" + part_sep; + static const unsigned FINGERPRINT_OFFSET = 2; + + if (rdlength <= FINGERPRINT_OFFSET) + return; // incomplete SSHFP record + + const uint8_t* rdata_end = rdata + rdlength; + rdata += FINGERPRINT_OFFSET; + std::ostringstream oss; + while (rdata < rdata_end) + { + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(*rdata); + rdata++; + } + + rdata_str.append(sshfp_prefix); + rdata_str.append(oss.str()); +} + +static void decode_default_rr(std::string& rdata_str, uint16_t type) +{ + bool is_unknown = false; + rdata_str.append(DNSData::qtype_name(type, &is_unknown)); + if (is_unknown) + { + rdata_str.append("-"); + rdata_str.append(std::to_string(type)); + } } -void DNSData::decode_rdata(const Packet* p, const uint8_t* rdata, uint16_t rdlength, uint16_t type, - std::string& rdata_str) const +void DNSData::decode_rdata(const Packet* p, const uint8_t* rr_domain_name, const uint8_t* rdata, + uint16_t rdlength, uint16_t type, std::string& rdata_str) const { - assert(rdata < p->data + p->dsize); + assert(rdata <= p->data + p->dsize); if (rdata + rdlength > p->data + p->dsize) rdlength = p->dsize - (rdata - p->data); @@ -185,14 +423,14 @@ void DNSData::decode_rdata(const Packet* p, const uint8_t* rdata, uint16_t rdlen decode_ip(rdata, rdlength, rdata_str, type); break; - case DNS_RR_TYPE_TXT: - decode_txt(rdata, rdlength, rdata_str); + case DNS_RR_TYPE_BIND9_SIGNING: + decode_bind9_signing(rdata_str); break; case DNS_RR_TYPE_CNAME: + case DNS_RR_TYPE_MB: case DNS_RR_TYPE_MD: case DNS_RR_TYPE_MF: - case DNS_RR_TYPE_MB: case DNS_RR_TYPE_MG: case DNS_RR_TYPE_MR: case DNS_RR_TYPE_NS: @@ -201,23 +439,60 @@ void DNSData::decode_rdata(const Packet* p, const uint8_t* rdata, uint16_t rdlen decode_domain_name(rdata, rdlength, rdata_str, p); break; + case DNS_RR_TYPE_DNSKEY: + decode_dnskey(rdata, rdlength, rdata_str); + break; + + case DNS_RR_TYPE_DS: + decode_ds(rdata, rdlength, rdata_str); + break; + + case DNS_RR_TYPE_LOC: + decode_loc(rdata, rdlength, rdata_str); + break; + case DNS_RR_TYPE_MX: decode_mx(rdata, rdlength, rdata_str, p); break; + case DNS_RR_TYPE_NSEC: + decode_nsec(rdata, rdlength, rdata_str, p, rr_domain_name); + break; + + case DNS_RR_TYPE_OPT: + decode_opt(rdata, rdata_str); + break; + case DNS_RR_TYPE_RRSIG: decode_rrsig(rdata, rdlength, rdata_str); break; - case DNS_RR_TYPE_NSEC: - decode_nsec(rdata, rdlength, rdata_str, resp_query); + case DNS_RR_TYPE_SPF: + decode_spf(rdata, rdlength, rdata_str); break; - case DNS_RR_TYPE_DS: - decode_ds(rdata, rdlength, rdata_str); + case DNS_RR_TYPE_SRV: + decode_srv(rdata, rdlength, rdata_str, p); + break; + + case DNS_RR_TYPE_SSHFP: + decode_sshfp(rdata, rdlength, rdata_str); + break; + + case DNS_RR_TYPE_TXT: + decode_txt(rdata, rdlength, rdata_str); break; + case DNS_RR_TYPE_CAA: + case DNS_RR_TYPE_HINFO: + case DNS_RR_TYPE_HTTPS: + case DNS_RR_TYPE_NSEC3: + case DNS_RR_TYPE_NSEC3PARAM: + case DNS_RR_TYPE_SVCB: + case DNS_RR_TYPE_TKEY: + case DNS_RR_TYPE_TSIG: default: + decode_default_rr(rdata_str, type); break; } } @@ -225,22 +500,33 @@ void DNSData::decode_rdata(const Packet* p, const uint8_t* rdata, uint16_t rdlen void DNSData::get_rr_data(const Packet *p, const std::vector& tabs, std::string& rrs, std::string* ttls) const { + assert(p != nullptr); + assert(p->data != nullptr); + static const std::string item_sep = " "; // list item separator static const unsigned RDATA_OFFSET = 10; + const uint8_t* rr_domain_name = nullptr; + for (auto tab : tabs) { - if (tab + RDATA_OFFSET >= p->dsize) + if (tab + RDATA_OFFSET > p->dsize) continue; - auto rdata = p->data + tab; - - uint16_t type = (rdata[0] << 8) | rdata[1]; - uint32_t ttl = (rdata[4] << 24) | (rdata[5] << 16) | (rdata[6] << 8) | rdata[7]; + auto rr_type = p->data + tab; + if (rr_domain_name == nullptr) + { + rr_domain_name = rr_type; + continue; + } - uint16_t rdlength = (rdata[8] << 8) | rdata[9]; + uint16_t type = (rr_type[0] << 8) | rr_type[1]; + uint32_t ttl = (rr_type[4] << 24) | (rr_type[5] << 16) | (rr_type[6] << 8) | rr_type[7]; + uint16_t rdlength = (rr_type[8] << 8) | rr_type[9]; + auto rdata = rr_type + RDATA_OFFSET; std::string rdata_str; - decode_rdata(p, rdata + RDATA_OFFSET, rdlength, type, rdata_str); + decode_rdata(p, rr_domain_name, rdata, rdlength, type, rdata_str); + rr_domain_name = rdata + rdlength; if (rdata_str.empty()) continue;