This commit introduces a new method to compare a `DNSName`
against a view of raw, wire-format bytes, skipping the
allocation and copy that is usually required to get a
second `DNSName` object to compare against.
This signifitcantly reduces the amount of time matching
a DNS response received from a backend against the content
we expect to find.
Signed-off-by: Remi Gacogne <remi.gacogne@powerdns.com>
(cherry picked from commit
67eb73850f3141c44963d95ef815fe6a0586d2a8)
Signed-off-by: Remi Gacogne <remi.gacogne@powerdns.com>
return false;
}
- uint16_t rqtype{};
- uint16_t rqclass{};
- DNSName rqname;
try {
+ uint16_t rqtype{};
+ uint16_t rqclass{};
+ if (response.size() < (sizeof(dnsheader) + qname.wirelength() + sizeof(rqtype) + sizeof(rqclass))) {
+ return false;
+ }
+
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- rqname = DNSName(reinterpret_cast<const char*>(response.data()), response.size(), sizeof(dnsheader), false, &rqtype, &rqclass);
+ const std::string_view packetView(reinterpret_cast<const char*>(response.data() + sizeof(dnsheader)), response.size() - sizeof(dnsheader));
+ if (qname.matches(packetView)) {
+ size_t pos = sizeof(dnsheader) + qname.wirelength();
+ rqtype = response.at(pos) * 256 + response.at(pos + 1);
+ rqclass = response.at(pos + 2) * 256 + response.at(pos + 3);
+ return rqtype == qtype && rqclass == qclass;
+ }
+ return false;
}
catch (const std::exception& e) {
if (remote && !response.empty() && static_cast<size_t>(response.size()) > sizeof(dnsheader)) {
}
return false;
}
-
- return rqtype == qtype && rqclass == qclass && rqname == qname;
}
static void restoreFlags(struct dnsheader* dnsHeader, uint16_t origFlags)
return d_position == 0;
}
+bool DNSName::matches(const std::string_view& wire_uncompressed) const
+{
+ if (wire_uncompressed.empty() != empty() || wire_uncompressed.size() < d_storage.size()) {
+ return false;
+ }
+
+ const auto* us = d_storage.cbegin();
+ const auto* p = wire_uncompressed.cbegin();
+ for (; us != d_storage.cend() && p != wire_uncompressed.cend(); ++us, ++p) {
+ if (dns_tolower(*p) != dns_tolower(*us)) {
+ return false;
+ }
+ }
+
+ return us == d_storage.cend();
+}
+
#if defined(PDNS_AUTH) // [
std::ostream & operator<<(std::ostream &ostr, const ZoneName& zone)
{
bool isPartOf(const DNSName& rhs) const; //!< Are we part of the rhs name? Note that name.isPartOf(name).
inline bool operator==(const DNSName& rhs) const; //!< DNS-native comparison (case insensitive) - empty compares to empty
bool operator!=(const DNSName& other) const { return !(*this == other); }
+ bool matches(const std::string_view& wire_uncompressed) const; // DNS-native (case insensitive) comparison against raw data in wire format
std::string toString(const std::string& separator=".", const bool trailing=true) const; //!< Our human-friendly, escaped, representation
void toString(std::string& output, const std::string& separator=".", const bool trailing=true) const;
BOOST_CHECK_EQUAL(name5.getCommonLabels(name1), DNSName());
}
+BOOST_AUTO_TEST_CASE(test_raw_data_comparison) {
+ const DNSName aroot("a.root-servers.net");
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(query, aroot, QType::A, QClass::IN, 0);
+
+ {
+ const std::string_view raw(reinterpret_cast<const char*>(query.data()) + sizeof(dnsheader), query.size() - sizeof(dnsheader));
+ BOOST_CHECK(aroot.matches(raw));
+
+ DNSName differentCase("A.RooT-Servers.NET");
+ BOOST_CHECK(differentCase.matches(raw));
+
+ const DNSName broot("b.root-servers.net");
+ BOOST_CHECK(!(broot.matches(raw)));
+
+ /* last character differs */
+ const DNSName notaroot("a.root-servers.nes");
+ BOOST_CHECK(!(notaroot.matches(raw)));
+ }
+
+ {
+ /* too short */
+ const std::string_view raw(reinterpret_cast<const char*>(query.data() + sizeof(dnsheader)), aroot.wirelength() - 1);
+ BOOST_CHECK(!(aroot.matches(raw)));
+ }
+}
+
#if defined(PDNS_AUTH)
BOOST_AUTO_TEST_CASE(test_variantnames) {
ZoneName zone1("..variant");