]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add cookies support for sdig
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 11 Feb 2025 10:33:41 +0000 (11:33 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 11 Feb 2025 12:00:13 +0000 (13:00 +0100)
docs/manpages/sdig.1.rst
pdns/Makefile.am
pdns/ednscookies.cc
pdns/ednscookies.hh
pdns/misc.cc
pdns/misc.hh
pdns/recursordist/Makefile.am
pdns/sdig.cc

index 73e5a9e2d787e52c5b51e312c4cbfc9a00f13c6d..996d5ca6ad07f206cc6e5f494fe70be4fea4f82a 100644 (file)
@@ -57,6 +57,8 @@ tlsProvider *name*
     when using DoT, use TLS provider *name*. Currently supported (if compiled in): `openssl` and `gnutls`. Default is `openssl` if available.
 opcode *OPNUM*
     Use opcode *OPNUM* instead of 0 (Query). For example, ``sdig 192.0.2.1 53 example.com SOA opcode 4`` sends a ``NOTIFY``.
+cookie *COOKIE*
+    if *COOKIE* is -d send a random client cookie. Otherwise send the given cookie, which should be a hex string received from a server earlier.
 
 Examples
 --------
index a5e9cbfe706c295a3586b4364e288556bb2d04b8..d824d24f2caeda2ae60b72e9e9bc33edc622893c 100644 (file)
@@ -540,6 +540,7 @@ sdig_SOURCES = \
        dnsrecords.cc \
        dnswriter.cc dnswriter.hh \
        dolog.hh \
+       ednscookies.cc ednscookies.hh \
        ednsextendederror.cc ednsextendederror.hh \
        ednssubnet.cc iputils.cc \
        libssl.cc libssl.hh \
index 593d9e728cb3ed0d3eaeaebe86df0e3ff56c159d..253d9bab8ca076552c904a27b55d1296d4b2b48b 100644 (file)
@@ -23,6 +23,7 @@
 #include "config.h"
 
 #include "ednscookies.hh"
+#include "dns_random.hh"
 #include "iputils.hh"
 
 #ifdef HAVE_CRYPTO_SHORTHASH
@@ -145,6 +146,16 @@ bool EDNSCookiesOpt::shouldRefresh() const
   return rfc1982LessThan(timestamp + 1800, now);
 }
 
+void EDNSCookiesOpt::makeClientCookie()
+{
+  uint32_t lower = dns_random_uint32();
+  uint32_t upper = dns_random_uint32();
+  client = string();
+  client.resize(8);
+  memcpy(client.data(), &lower, sizeof(lower));
+  memcpy(&client.at(4), &upper, sizeof(upper));
+}
+
 bool EDNSCookiesOpt::makeServerCookie([[maybe_unused]] const string& secret, [[maybe_unused]] const ComboAddress& source)
 {
 #ifdef HAVE_CRYPTO_SHORTHASH
index 03c5bb2ca0c486e3ba6404273e23cdeeee6308d3..204ef235622da4a8a0792e501e96a4dc05ac8ce8 100644 (file)
@@ -52,6 +52,7 @@ struct EDNSCookiesOpt
   }
 
   [[nodiscard]] bool isValid(const std::string& secret, const ComboAddress& source) const;
+  void makeClientCookie();
   bool makeServerCookie(const std::string& secret, const ComboAddress& source);
   [[nodiscard]] std::string makeOptString() const;
   [[nodiscard]] std::string getServer() const
index 8f0a7bd45333be160b5e45f791c9b66860e350db..5e662a8dbf571f13dd6c42bc83debe2269c310d0 100644 (file)
@@ -630,15 +630,16 @@ string U32ToIP(uint32_t val)
 }
 
 
-string makeHexDump(const string& str)
+string makeHexDump(const string& str, const string& sep)
 {
   std::array<char, 5> tmp;
   string ret;
-  ret.reserve(static_cast<size_t>(str.size()*2.2));
+  ret.reserve(static_cast<size_t>(str.size() * (2 + sep.size())));
 
   for (char n : str) {
-    snprintf(tmp.data(), tmp.size(), "%02x ", static_cast<unsigned char>(n));
+    snprintf(tmp.data(), tmp.size(), "%02x", static_cast<unsigned char>(n));
     ret += tmp.data();
+    ret += sep;
   }
   return ret;
 }
index 1082b47c5ad6101372e726c43d3c1313ed142526..efc5e8bf208968038551c9e3352718e3669703b9 100644 (file)
@@ -336,7 +336,7 @@ inline double getTime()
   throw runtime_error(why + ": " + stringerror(errno));
 }
 
-string makeHexDump(const string& str);
+string makeHexDump(const string& str, const string& sep = " ");
 //! Convert the hexstring in to a byte string
 string makeBytesFromHex(const string &in);
 
index 03beee782b8fdbe0a850dd4db3c4f697c4307d36..da669c9c934342fb7b9a5f7075b286ff7c3af320 100644 (file)
@@ -140,8 +140,8 @@ pdns_recursor_SOURCES = \
        dnsseckeeper.hh \
        dnswriter.cc dnswriter.hh \
        dolog.hh \
-       ednsextendederror.cc ednsextendederror.hh \
        ednscookies.cc ednscookies.hh \
+       ednsextendederror.cc ednsextendederror.hh \
        ednsoptions.cc ednsoptions.hh \
        ednspadding.cc ednspadding.hh \
        ednssubnet.cc ednssubnet.hh \
index 9f3a3fc6a16c569e4bdced72e885f7314ac6243f..ca956a0c34c01931570a0b373d31771405e7c594 100644 (file)
@@ -6,6 +6,7 @@
 #include "dnswriter.hh"
 #include "ednsoptions.hh"
 #include "ednssubnet.hh"
+#include "ednscookies.hh"
 #include "ednsextendederror.hh"
 #include "misc.hh"
 #include "proxy-protocol.hh"
@@ -41,6 +42,7 @@ static void usage()
           "[dnssec] [ednssubnet SUBNET/MASK] [hidesoadetails] [hidettl] [recurse] [showflags] "
           "[tcp] [dot] [insecure] [fastOpen] [subjectName name] [caStore file] [tlsProvider openssl|gnutls] "
           "[proxy UDP(0)/TCP(1) SOURCE-IP-ADDRESS-AND-PORT DESTINATION-IP-ADDRESS-AND-PORT] "
+          "[cookie -/HEX] "
           "[dumpluaraw] [opcode OPNUM]"
        << endl;
 }
@@ -57,11 +59,11 @@ static std::unordered_set<uint16_t> s_expectedIDs;
 
 static void fillPacket(vector<uint8_t>& packet, const string& q, const string& t,
                        bool dnssec, const std::optional<Netmask>& ednsnm,
-                       bool recurse, QClass qclass, uint8_t opcode, uint16_t qid)
+                       bool recurse, QClass qclass, uint8_t opcode, uint16_t qid, const std::optional<string>& cookie)
 {
   DNSPacketWriter pw(packet, DNSName(q), DNSRecordContent::TypeToNumber(t), qclass, opcode);
 
-  if (dnssec || ednsnm || getenv("SDIGBUFSIZE")) {
+  if (dnssec || ednsnm || getenv("SDIGBUFSIZE") || cookie) {
     char* sbuf = getenv("SDIGBUFSIZE");
     int bufsize;
     if (sbuf)
@@ -74,7 +76,19 @@ static void fillPacket(vector<uint8_t>& packet, const string& q, const string& t
       eo.setSource(*ednsnm);
       opts.emplace_back(EDNSOptionCode::ECS, eo.makeOptString());
     }
-
+    if (cookie) {
+      EDNSCookiesOpt cookieOpt;
+      if (*cookie == "-") {
+        cookieOpt.makeClientCookie();
+      }
+      else {
+        string unhex = makeBytesFromHex(*cookie);
+        if (!cookieOpt.makeFromString(unhex)) {
+          cerr << "Malformed cookie in argument list, adding anyway" << endl;
+        }
+      }
+      opts.emplace_back(EDNSOptionCode::COOKIE, cookieOpt.makeOptString());
+    }
     pw.addOpt(bufsize, 0, dnssec ? EDNSOpts::DNSSECOK : 0, opts);
     pw.commit();
   }
@@ -104,8 +118,18 @@ static void printReply(const string& reply, bool showflags, bool hidesoadetails,
   }
 
   cout << endl;
-  cout << "Rcode: " << mdp.d_header.rcode << " ("
-       << RCode::to_s(mdp.d_header.rcode) << "), RD: " << mdp.d_header.rd
+  EDNSOpts edo{};
+  bool hasEDNS = getEDNSOpts(mdp, &edo);
+
+  if (hasEDNS) {
+    uint16_t ercode = edo.d_extRCode << 4 | mdp.d_header.rcode;
+    cout << "Rcode: " << ercode << " (" << ERCode::to_s(ercode);
+  }
+  else {
+    cout << "Rcode: " << mdp.d_header.rcode << " (" << RCode::to_s(mdp.d_header.rcode);
+  }
+
+  cout << "), RD: " << mdp.d_header.rd
        << ", QR: " << mdp.d_header.qr;
   cout << ", TC: " << mdp.d_header.tc << ", AA: " << mdp.d_header.aa
        << ", opcode: " << mdp.d_header.opcode << endl;
@@ -162,28 +186,37 @@ static void printReply(const string& reply, bool showflags, bool hidesoadetails,
     cout << "\t" << i->getContent()->getZoneRepresentation() << "\n";
   }
 
-  EDNSOpts edo;
-  if (getEDNSOpts(mdp, &edo)) {
-    //    cerr<<"Have "<<edo.d_options.size()<<" options!"<<endl;
-    for (vector<pair<uint16_t, string>>::const_iterator iter = edo.d_options.begin();
-         iter != edo.d_options.end(); ++iter) {
-      if (iter->first == EDNSOptionCode::ECS) { // 'EDNS subnet'
+  if (hasEDNS) {
+    for (const auto& iter : edo.d_options) {
+      if (iter.first == EDNSOptionCode::ECS) { // 'EDNS subnet'
         EDNSSubnetOpts reso;
-        if (EDNSSubnetOpts::getFromString(iter->second, &reso)) {
+        if (EDNSSubnetOpts::getFromString(iter.second, &reso)) {
           cerr << "EDNS Subnet response: " << reso.getSource().toString()
                << ", scope: " << reso.getScope().toString()
                << ", family = " << std::to_string(reso.getFamily())
                << endl;
         }
-      } else if (iter->first == EDNSOptionCode::PADDING) {
-        cerr << "EDNS Padding size: " << (iter->second.size()) << endl;
-      } else if (iter->first == EDNSOptionCode::EXTENDEDERROR) {
+      }
+      else if (iter.first == EDNSOptionCode::COOKIE) {
+        EDNSCookiesOpt cookie(iter.second);
+        auto client = cookie.getClient();
+        auto server = cookie.getServer();
+        auto dump = makeHexDump(client, "") + makeHexDump(server, "");
+        if (cookie.isWellFormed()) {
+          cerr << "EDNS Cookie response: " << dump << endl;
+        }
+        else {
+          cerr << "EDNS Cookie response malformed: " << dump << endl;
+        }
+      } else if (iter.first == EDNSOptionCode::PADDING) {
+        cerr << "EDNS Padding size: " << iter.second.size() << endl;
+      } else if (iter.first == EDNSOptionCode::EXTENDEDERROR) {
         EDNSExtendedError eee;
-        if (getEDNSExtendedErrorOptFromString(iter->second, eee)) {
+        if (getEDNSExtendedErrorOptFromString(iter.second, eee)) {
           cerr << "EDNS Extended Error response: " << eee.infoCode << "/" << eee.extraText << endl;
         }
       } else {
-        cerr << "Have unknown option " << (int)iter->first << endl;
+        cerr << "Have unknown option " << (int)iter.first << endl;
       }
     }
   }
@@ -211,6 +244,7 @@ try {
   string caStore;
   string tlsProvider = "openssl";
   bool dumpluaraw = false;
+  std::optional<string> cookie;
 
   for (int i = 1; i < argc; i++) {
     if ((string)argv[i] == "--help") {
@@ -303,6 +337,13 @@ try {
         ComboAddress dest(argv[++i]);
         proxyheader = makeProxyHeader(ptcp, src, dest, {});
       }
+      else if (strcmp(argv[i], "cookie") == 0) {
+        if (argc < i + 2) {
+          cerr << "cookie needs an argument"<<endl;
+          exit(EXIT_FAILURE);
+        }
+        cookie = argv[++i];
+      }
       else if (strcmp(argv[i], "dumpluaraw") == 0) {
         dumpluaraw = true;
       }
@@ -356,7 +397,7 @@ try {
 #ifdef HAVE_LIBCURL
     vector<uint8_t> packet;
     s_expectedIDs.insert(0);
-    fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0);
+    fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0, cookie);
     MiniCurl mc;
     MiniCurl::MiniCurlHeaders mch;
     mch.emplace("Content-Type", "application/dns-message");
@@ -410,7 +451,7 @@ try {
     for (const auto& it : questions) {
       vector<uint8_t> packet;
       s_expectedIDs.insert(counter);
-      fillPacket(packet, it.first, it.second, dnssec, ednsnm, recurse, qclass, opcode, counter);
+      fillPacket(packet, it.first, it.second, dnssec, ednsnm, recurse, qclass, opcode, counter, cookie);
       counter++;
 
       // Prefer to do a single write, so that fastopen can send all the data on SYN
@@ -440,7 +481,7 @@ try {
   {
     vector<uint8_t> packet;
     s_expectedIDs.insert(0);
-    fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0);
+    fillPacket(packet, name, type, dnssec, ednsnm, recurse, qclass, opcode, 0, cookie);
     string question(packet.begin(), packet.end());
     Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
     question = proxyheader + question;