Skip to content

Commit c1e78ff

Browse files
author
leoparente
authored
Feature/adjust qname aggregation (#242)
* Threat static sufix on DNS Qnames * compare without casting * add only_qname_suffix validation on unit tests * replace string_view with size_t for suffix * Fix aggregation tests
1 parent ccdfb18 commit c1e78ff

File tree

6 files changed

+67
-12
lines changed

6 files changed

+67
-12
lines changed

src/handlers/dns/DnsStreamHandler.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ void DnsStreamHandler::process_udp_packet_cb(pcpp::Packet &payload, PacketDirect
163163
if (metric_port) {
164164
DnsLayer dnsLayer(udpLayer, &payload);
165165
if (!_filtering(dnsLayer, dir, l3, pcpp::UDP, metric_port, stamp)) {
166-
_metrics->process_dns_layer(dnsLayer, dir, l3, pcpp::UDP, flowkey, metric_port, stamp);
166+
_metrics->process_dns_layer(dnsLayer, dir, l3, pcpp::UDP, flowkey, metric_port, _static_suffix_size, stamp);
167+
_static_suffix_size = 0;
167168
// signal for chained stream handlers, if we have any
168169
udp_signal(payload, dir, l3, flowkey, stamp);
169170
}
@@ -243,7 +244,8 @@ void DnsStreamHandler::tcp_message_ready_cb(int8_t side, const pcpp::TcpStreamDa
243244
pcpp::Packet dummy_packet;
244245
DnsLayer dnsLayer(data.get(), size, nullptr, &dummy_packet);
245246
if (!_filtering(dnsLayer, dir, l3Type, pcpp::UDP, port, stamp)) {
246-
_metrics->process_dns_layer(dnsLayer, dir, l3Type, pcpp::TCP, flowKey, port, stamp);
247+
_metrics->process_dns_layer(dnsLayer, dir, l3Type, pcpp::TCP, flowKey, port, _static_suffix_size, stamp);
248+
_static_suffix_size = 0;
247249
}
248250
// data is freed upon return
249251
};
@@ -319,9 +321,10 @@ bool DnsStreamHandler::_filtering(DnsLayer &payload, [[maybe_unused]] PacketDire
319321
std::string qname_ci{payload.getFirstQuery()->getName()};
320322
std::transform(qname_ci.begin(), qname_ci.end(), qname_ci.begin(),
321323
[](unsigned char c) { return std::tolower(c); });
322-
for (auto fqn : _f_qnames) {
324+
for (const auto &fqn : _f_qnames) {
323325
// if it matched, we know we are not filtering
324326
if (endsWith(qname_ci, fqn)) {
327+
_static_suffix_size = fqn.size();
325328
goto will_not_filter;
326329
}
327330
}
@@ -532,7 +535,7 @@ void DnsMetricsBucket::process_dnstap(bool deep, const dnstap::Dnstap &payload)
532535
process_dns_layer(deep, dpayload, l3, l4, port);
533536
}
534537
}
535-
void DnsMetricsBucket::process_dns_layer(bool deep, DnsLayer &payload, pcpp::ProtocolType l3, Protocol l4, uint16_t port)
538+
void DnsMetricsBucket::process_dns_layer(bool deep, DnsLayer &payload, pcpp::ProtocolType l3, Protocol l4, uint16_t port, size_t suffix_size)
536539
{
537540
std::unique_lock lock(_mutex);
538541

@@ -626,7 +629,7 @@ void DnsMetricsBucket::process_dns_layer(bool deep, DnsLayer &payload, pcpp::Pro
626629
}
627630
}
628631

629-
auto aggDomain = aggregateDomain(name);
632+
auto aggDomain = aggregateDomain(name, suffix_size);
630633
_dns_topQname2.update(std::string(aggDomain.first));
631634
if (aggDomain.second.size()) {
632635
_dns_topQname3.update(std::string(aggDomain.second));
@@ -788,12 +791,12 @@ void DnsMetricsBucket::process_filtered()
788791
}
789792

790793
// the general metrics manager entry point (both UDP and TCP)
791-
void DnsMetricsManager::process_dns_layer(DnsLayer &payload, PacketDirection dir, pcpp::ProtocolType l3, pcpp::ProtocolType l4, uint32_t flowkey, uint16_t port, timespec stamp)
794+
void DnsMetricsManager::process_dns_layer(DnsLayer &payload, PacketDirection dir, pcpp::ProtocolType l3, pcpp::ProtocolType l4, uint32_t flowkey, uint16_t port, size_t suffix_size, timespec stamp)
792795
{
793796
// base event
794797
new_event(stamp);
795798
// process in the "live" bucket. this will parse the resources if we are deep sampling
796-
live_bucket()->process_dns_layer(_deep_sampling_now, payload, l3, static_cast<Protocol>(l4), port);
799+
live_bucket()->process_dns_layer(_deep_sampling_now, payload, l3, static_cast<Protocol>(l4), port, suffix_size);
797800

798801
if (group_enabled(group::DnsMetrics::DnsTransactions)) {
799802
// handle dns transactions (query/response pairs)

src/handlers/dns/DnsStreamHandler.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class DnsMetricsBucket final : public visor::AbstractMetricsBucket
158158
void to_prometheus(std::stringstream &out, Metric::LabelMap add_labels = {}) const override;
159159

160160
void process_filtered();
161-
void process_dns_layer(bool deep, DnsLayer &payload, pcpp::ProtocolType l3, Protocol l4, uint16_t port);
161+
void process_dns_layer(bool deep, DnsLayer &payload, pcpp::ProtocolType l3, Protocol l4, uint16_t port, size_t suffix_size = 0);
162162
void process_dns_layer(pcpp::ProtocolType l3, Protocol l4, QR side, uint16_t port);
163163
void process_dnstap(bool deep, const dnstap::Dnstap &payload);
164164

@@ -200,7 +200,7 @@ class DnsMetricsManager final : public visor::AbstractMetricsManager<DnsMetricsB
200200
}
201201

202202
void process_filtered(timespec stamp);
203-
void process_dns_layer(DnsLayer &payload, PacketDirection dir, pcpp::ProtocolType l3, pcpp::ProtocolType l4, uint32_t flowkey, uint16_t port, timespec stamp);
203+
void process_dns_layer(DnsLayer &payload, PacketDirection dir, pcpp::ProtocolType l3, pcpp::ProtocolType l4, uint32_t flowkey, uint16_t port, size_t suffix_size, timespec stamp);
204204
void process_dnstap(const dnstap::Dnstap &payload, bool filtered);
205205
};
206206

@@ -287,6 +287,7 @@ class DnsStreamHandler final : public visor::StreamMetricsHandler<DnsMetricsMana
287287
std::bitset<Filters::FiltersMAX> _f_enabled;
288288
uint16_t _f_rcode{0};
289289
std::vector<std::string> _f_qnames;
290+
size_t _static_suffix_size{0};
290291
std::bitset<DNSTAP_TYPE_SIZE> _f_dnstap_types;
291292

292293
static const inline StreamMetricsHandler::GroupDefType _group_defs = {

src/handlers/dns/dns.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace visor::handler::dns {
88

9-
AggDomainResult aggregateDomain(const std::string &domain)
9+
AggDomainResult aggregateDomain(const std::string &domain, size_t suffix_size)
1010
{
1111

1212
std::string_view qname2(domain);
@@ -18,7 +18,9 @@ AggDomainResult aggregateDomain(const std::string &domain)
1818
return AggDomainResult(qname2, qname3);
1919
}
2020
std::size_t endDot = std::string::npos;
21-
if (domain.back() == '.') {
21+
if (suffix_size > 0 && domain.size() > suffix_size) {
22+
endDot = domain.size() - suffix_size;
23+
} else if (domain.back() == '.') {
2224
endDot = domain.size() - 2;
2325
}
2426
auto first_dot = domain.rfind('.', endDot);

src/handlers/dns/dns.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
namespace visor::handler::dns {
1414

1515
typedef std::pair<std::string_view, std::string_view> AggDomainResult;
16-
AggDomainResult aggregateDomain(const std::string &domain);
16+
AggDomainResult aggregateDomain(const std::string &domain, size_t suffix_size = 0);
1717

1818
enum QR {
1919
query = 0,

src/handlers/dns/tests/test_dns.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,47 @@ TEST_CASE("DNS Utilities", "[dns]")
7272
CHECK(result.first == ".b.c");
7373
CHECK(result.second == "");
7474
}
75+
76+
SECTION("aggregateDomain with static suffix")
77+
{
78+
AggDomainResult result;
79+
std::string domain;
80+
std::string static_suffix;
81+
82+
domain = "biz.foo.bar.com";
83+
static_suffix = ".bar.com";
84+
result = aggregateDomain(domain, static_suffix.size());
85+
CHECK(result.first == ".foo.bar.com");
86+
CHECK(result.second == "biz.foo.bar.com");
87+
88+
domain = "biz.foo.bar.com";
89+
static_suffix = "bar.com";
90+
result = aggregateDomain(domain, static_suffix.size());
91+
CHECK(result.first == ".foo.bar.com");
92+
CHECK(result.second == "biz.foo.bar.com");
93+
94+
domain = "biz.foo.bar.com";
95+
static_suffix = "foo.bar.com";
96+
result = aggregateDomain(domain, static_suffix.size());
97+
CHECK(result.first == "biz.foo.bar.com");
98+
CHECK(result.second == "");
99+
100+
domain = "foo.bar.com.";
101+
static_suffix = "biz.foo.bar.com";
102+
result = aggregateDomain(domain, static_suffix.size());
103+
CHECK(result.first == ".bar.com.");
104+
CHECK(result.second == "foo.bar.com.");
105+
106+
domain = "www.google.co.uk";
107+
static_suffix = ".co.uk";
108+
result = aggregateDomain(domain, static_suffix.size());
109+
CHECK(result.first == ".google.co.uk");
110+
CHECK(result.second == "www.google.co.uk");
111+
112+
domain = "www.google.co.uk";
113+
static_suffix = "google.co.uk";
114+
result = aggregateDomain(domain, static_suffix.size());
115+
CHECK(result.first == "www.google.co.uk");
116+
CHECK(result.second == "");
117+
}
75118
}

src/handlers/dns/tests/test_dns_layer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,12 @@ TEST_CASE("DNS Filters: only_qname_suffix", "[pcap][dns]")
374374
CHECK(counters.REFUSED.value() == 0);
375375
CHECK(counters.NX.value() == 1);
376376
CHECK(counters.filtered.value() == 14);
377+
378+
nlohmann::json j;
379+
dns_handler.metrics()->bucket(0)->to_json(j);
380+
381+
CHECK(j["top_qname2"][0]["name"].get<std::string>().find("google.com") != std::string::npos);
382+
CHECK(j["top_qname3"][0]["name"] == nullptr);
377383
}
378384

379385
TEST_CASE("DNS groups", "[pcap][dns]")

0 commit comments

Comments
 (0)