]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add Lua FFI helpers for parsing a DNS packet
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 4 Jan 2022 17:10:01 +0000 (18:10 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 10 Oct 2022 07:56:59 +0000 (09:56 +0200)
pdns/dnsdistdist/Makefile.am
pdns/dnsdistdist/dnsdist-dnsparser.cc [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-dnsparser.hh [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-lua-ffi-interface.h
pdns/dnsdistdist/dnsdist-lua-ffi.cc

index b04bf37d470f7c2e95669feffe8a8848e75eff8a..a6c08bc813d2d4a4219cd63d4f94cf2b80ce4c72 100644 (file)
@@ -140,6 +140,7 @@ dnsdist_SOURCES = \
        dnsdist-console.cc dnsdist-console.hh \
        dnsdist-discovery.cc dnsdist-discovery.hh \
        dnsdist-dnscrypt.cc \
+       dnsdist-dnsparser.cc dnsdist-dnsparser.hh \
        dnsdist-downstream-connection.hh \
        dnsdist-dynblocks.cc dnsdist-dynblocks.hh \
        dnsdist-dynbpf.cc dnsdist-dynbpf.hh \
@@ -242,6 +243,7 @@ testrunner_SOURCES = \
        dnscrypt.cc dnscrypt.hh \
        dnsdist-backend.cc \
        dnsdist-cache.cc dnsdist-cache.hh \
+       dnsdist-dnsparser.cc dnsdist-dnsparser.hh \
        dnsdist-downstream-connection.hh \
        dnsdist-dynblocks.cc dnsdist-dynblocks.hh \
        dnsdist-dynbpf.cc dnsdist-dynbpf.hh \
diff --git a/pdns/dnsdistdist/dnsdist-dnsparser.cc b/pdns/dnsdistdist/dnsdist-dnsparser.cc
new file mode 100644 (file)
index 0000000..19e9e82
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "dnsdist-dnsparser.hh"
+#include "dnsparser.hh"
+
+namespace dnsdist
+{
+DNSPacketOverlay::DNSPacketOverlay(const std::string_view& packet)
+{
+  if (packet.size() < sizeof(dnsheader)) {
+    throw std::runtime_error("Packet is too small for a DNS packet");
+  }
+
+  memcpy(&d_header, packet.data(), sizeof(dnsheader));
+  uint64_t numRecords = ntohs(d_header.ancount) + ntohs(d_header.nscount) + ntohs(d_header.arcount);
+  d_records.reserve(numRecords);
+
+  try
+  {
+    PacketReader reader(pdns_string_view(reinterpret_cast<const char*>(packet.data()), packet.size()));
+
+    for (uint16_t n = 0; n < ntohs(d_header.qdcount) ; ++n) {
+      reader.xfrName(d_qname);
+      reader.xfrType(d_qtype);
+      reader.xfrType(d_qclass);
+    }
+
+    for (uint64_t n = 0; n < numRecords; ++n) {
+      Record rec;
+      reader.xfrName(rec.d_name);
+      rec.d_place = n < ntohs(d_header.ancount) ? DNSResourceRecord::ANSWER : (n < (ntohs(d_header.ancount) + ntohs(d_header.nscount)) ? DNSResourceRecord::AUTHORITY : DNSResourceRecord::ADDITIONAL);
+      reader.xfrType(rec.d_type);
+      reader.xfrType(rec.d_class);
+      reader.xfr32BitInt(rec.d_ttl);
+      reader.xfr16BitInt(rec.d_contentLength);
+      rec.d_contentOffset = reader.getPosition();
+      reader.skip(rec.d_contentLength);
+      d_records.push_back(std::move(rec));
+    }
+  }
+  catch (const std::exception& e) {
+    throw std::runtime_error("Unable to parse DNS packet: " + std::string(e.what()));
+  }
+  catch (const PDNSException& e) {
+    throw std::runtime_error("Unable to parse DNS packet: " + e.reason);
+  }
+  catch (...) {
+    throw std::runtime_error("Unable to parse DNS packet");
+  }
+}
+}
+
diff --git a/pdns/dnsdistdist/dnsdist-dnsparser.hh b/pdns/dnsdistdist/dnsdist-dnsparser.hh
new file mode 100644 (file)
index 0000000..e6a4c00
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "dnsparser.hh"
+
+namespace dnsdist
+{
+class DNSPacketOverlay
+{
+public:
+  DNSPacketOverlay(const std::string_view& packet);
+
+  struct Record
+  {
+    DNSName d_name;
+    uint32_t d_ttl;
+    uint16_t d_type;
+    uint16_t d_class;
+    uint16_t d_contentLength;
+    uint16_t d_contentOffset;
+    DNSResourceRecord::Place d_place;
+  };
+
+  DNSName d_qname;
+  std::vector<Record> d_records;
+  uint16_t d_qtype;
+  uint16_t d_qclass;
+  dnsheader d_header;
+};
+}
index 528d45cd40f80ebad423b08fae55c5862846a212..7f2cc601b186e1fcbbd8b31955de378550f9169f 100644 (file)
@@ -187,3 +187,18 @@ bool dnsdist_ffi_network_endpoint_new(const char* path, size_t pathSize, dnsdist
 bool dnsdist_ffi_network_endpoint_is_valid(const dnsdist_ffi_network_endpoint_t* endpoint) __attribute__ ((visibility ("default")));
 bool dnsdist_ffi_network_endpoint_send(const dnsdist_ffi_network_endpoint_t* endpoint, const char* payload, size_t payloadSize) __attribute__ ((visibility ("default")));
 void dnsdist_ffi_network_endpoint_free(dnsdist_ffi_network_endpoint_t* endpoint) __attribute__ ((visibility ("default")));
+
+typedef struct dnsdist_ffi_dnspacket_t dnsdist_ffi_dnspacket_t;
+
+bool dnsdist_ffi_dnspacket_parse(const char* packet, size_t packetSize, dnsdist_ffi_dnspacket_t** out) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_dnspacket_get_qname_raw(const dnsdist_ffi_dnspacket_t* packet, const char** qname, size_t* qnameSize) __attribute__ ((visibility ("default")));
+uint16_t dnsdist_ffi_dnspacket_get_qtype(const dnsdist_ffi_dnspacket_t* packet) __attribute__ ((visibility ("default")));
+uint16_t dnsdist_ffi_dnspacket_get_qclass(const dnsdist_ffi_dnspacket_t* packet) __attribute__ ((visibility ("default")));
+uint16_t dnsdist_ffi_dnspacket_get_records_count_in_section(const dnsdist_ffi_dnspacket_t* packet, uint8_t section) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_dnspacket_get_record_name_raw(const dnsdist_ffi_dnspacket_t* packet, size_t idx, const char** name, size_t* nameSize) __attribute__ ((visibility ("default")));
+uint16_t dnsdist_ffi_dnspacket_get_record_type(const dnsdist_ffi_dnspacket_t* packet, size_t idx) __attribute__ ((visibility ("default")));
+uint16_t dnsdist_ffi_dnspacket_get_record_class(const dnsdist_ffi_dnspacket_t* packet, size_t idx) __attribute__ ((visibility ("default")));
+uint32_t dnsdist_ffi_dnspacket_get_record_ttl(const dnsdist_ffi_dnspacket_t* packet, size_t idx) __attribute__ ((visibility ("default")));
+uint16_t dnsdist_ffi_dnspacket_get_record_content_length(const dnsdist_ffi_dnspacket_t* packet, size_t idx) __attribute__ ((visibility ("default")));
+uint16_t dnsdist_ffi_dnspacket_get_record_content_offset(const dnsdist_ffi_dnspacket_t* packet, size_t idx) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_dnspacket_free(dnsdist_ffi_dnspacket_t*) __attribute__ ((visibility ("default")));
index c1a07ac2ea0cb494da54faf4c92601d8a769c787..74e726f9930645188ba31666f3dc00e27ada7c7b 100644 (file)
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#include "dnsdist-dnsparser.hh"
 #include "dnsdist-lua-ffi.hh"
 #include "dnsdist-mac-address.hh"
 #include "dnsdist-lua-network.hh"
@@ -1124,3 +1125,127 @@ void dnsdist_ffi_network_endpoint_free(dnsdist_ffi_network_endpoint_t* endpoint)
 {
   delete endpoint;
 }
+
+struct dnsdist_ffi_dnspacket_t
+{
+  dnsdist::DNSPacketOverlay overlay;
+};
+
+bool dnsdist_ffi_dnspacket_parse(const char* packet, size_t packetSize, dnsdist_ffi_dnspacket_t** out)
+{
+  if (out == nullptr || packetSize < sizeof(dnsheader)) {
+    return false;
+  }
+
+  try {
+    dnsdist::DNSPacketOverlay overlay(std::string_view(packet, packetSize));
+    *out = new dnsdist_ffi_dnspacket_t{std::move(overlay)};
+    return true;
+  }
+  catch (const std::exception& e) {
+    vinfolog("Error in dnsdist_ffi_dnspacket_parse: %s", e.what());
+  }
+  catch (...) {
+    vinfolog("Error in dnsdist_ffi_dnspacket_parse");
+  }
+  return false;
+}
+
+void dnsdist_ffi_dnspacket_get_qname_raw(const dnsdist_ffi_dnspacket_t* packet, const char** qname, size_t* qnameSize)
+{
+  if (packet == nullptr || qname == nullptr || qnameSize == nullptr) {
+    return;
+  }
+  const auto& storage = packet->overlay.d_qname.getStorage();
+  *qname = storage.data();
+  *qnameSize = storage.size();
+}
+
+uint16_t dnsdist_ffi_dnspacket_get_qtype(const dnsdist_ffi_dnspacket_t* packet)
+{
+  if (packet != nullptr) {
+    return packet->overlay.d_qtype;
+  }
+  return 0;
+}
+
+uint16_t dnsdist_ffi_dnspacket_get_qclass(const dnsdist_ffi_dnspacket_t* packet)
+{
+  if (packet != nullptr) {
+    return packet->overlay.d_qclass;
+  }
+  return 0;
+}
+
+uint16_t dnsdist_ffi_dnspacket_get_records_count_in_section(const dnsdist_ffi_dnspacket_t* packet, uint8_t section)
+{
+  if (packet == nullptr || section > 3) {
+    return 0;
+  }
+
+  uint16_t count = 0;
+  for (const auto& record : packet->overlay.d_records) {
+    if (record.d_place == section) {
+      count++;
+    }
+  }
+
+  return count;
+}
+
+void dnsdist_ffi_dnspacket_get_record_name_raw(const dnsdist_ffi_dnspacket_t* packet, size_t idx, const char** name, size_t* nameSize)
+{
+  if (packet == nullptr || name == nullptr || nameSize == nullptr || idx > packet->overlay.d_records.size()) {
+    return;
+  }
+  const auto& storage = packet->overlay.d_records.at(idx).d_name.getStorage();
+  *name = storage.data();
+  *nameSize = storage.size();
+}
+
+uint16_t dnsdist_ffi_dnspacket_get_record_type(const dnsdist_ffi_dnspacket_t* packet, size_t idx)
+{
+  if (packet == nullptr || idx > packet->overlay.d_records.size()) {
+    return 0;
+  }
+  return packet->overlay.d_records.at(idx).d_type;
+}
+
+uint16_t dnsdist_ffi_dnspacket_get_record_class(const dnsdist_ffi_dnspacket_t* packet, size_t idx)
+{
+  if (packet == nullptr || idx > packet->overlay.d_records.size()) {
+    return 0;
+  }
+  return packet->overlay.d_records.at(idx).d_class;
+}
+
+uint32_t dnsdist_ffi_dnspacket_get_record_ttl(const dnsdist_ffi_dnspacket_t* packet, size_t idx)
+{
+  if (packet == nullptr || idx > packet->overlay.d_records.size()) {
+    return 0;
+  }
+  return packet->overlay.d_records.at(idx).d_ttl;
+}
+
+uint16_t dnsdist_ffi_dnspacket_get_record_content_length(const dnsdist_ffi_dnspacket_t* packet, size_t idx)
+{
+  if (packet == nullptr || idx > packet->overlay.d_records.size()) {
+    return 0;
+  }
+  return packet->overlay.d_records.at(idx).d_contentLength;
+}
+
+uint16_t dnsdist_ffi_dnspacket_get_record_content_offset(const dnsdist_ffi_dnspacket_t* packet, size_t idx)
+{
+  if (packet == nullptr || idx > packet->overlay.d_records.size()) {
+    return 0;
+  }
+  return packet->overlay.d_records.at(idx).d_contentOffset;
+}
+
+void dnsdist_ffi_dnspacket_free(dnsdist_ffi_dnspacket_t* packet)
+{
+  if (packet != nullptr) {
+    delete packet;
+  }
+}