]> granicus.if.org Git - pdns/commitdiff
replace RCODE=BADVERS response on version >0 with a rule to match on the
authorDmitry Alenichev <mitya@vk.com>
Wed, 23 Jan 2019 22:05:56 +0000 (01:05 +0300)
committerDmitry Alenichev <mitya@vk.com>
Wed, 23 Jan 2019 22:05:56 +0000 (01:05 +0300)
highest implementation level of EDNS version and action to return the
ERCode.

pdns/dnsdist-console.cc
pdns/dnsdist-ecs.cc
pdns/dnsdist-ecs.hh
pdns/dnsdist-lua-actions.cc
pdns/dnsdist-lua-rules.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
pdns/dnsdistdist/dnsdist-rules.hh

index 01458399d6f946a76c0c0be8e09f496739aeb986..0ca1d581dd48a3f6b1722de7f118ca74c0f9ca0e 100644 (file)
@@ -374,6 +374,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "DSTPortRule", true, "port", "matches questions received to the destination port specified" },
   { "dumpStats", true, "", "print all statistics we gather" },
   { "dynBlockRulesGroup", true, "", "return a new DynBlockRulesGroup object" },
+  { "EDNSVersionRule", true, "version", "matches queries with the specified EDNS version" },
   { "EDNSOptionRule", true, "optcode", "matches queries with the specified EDNS0 option present" },
   { "ERCodeRule", true, "rcode", "matches responses with the specified extended rcode (EDNS0)" },
   { "exceedNXDOMAINs", true, "rate, seconds", "get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds" },
index cfd8f084c088eff63a14b351d6f47857c28ef92e..56422e48a8a992ca5cceb60be62598c072924144 100644 (file)
@@ -244,7 +244,7 @@ void generateECSOption(const ComboAddress& source, string& res, uint16_t ECSPref
   generateEDNSOption(EDNSOptionCode::ECS, payload, res);
 }
 
-void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint16_t ednsrcode, bool dnssecOK)
+void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint8_t ednsrcode, bool dnssecOK)
 {
   const uint8_t name = 0;
   dnsrecordheader dh;
@@ -626,7 +626,7 @@ int rewriteResponseWithoutEDNSOption(const std::string& initialPacket, const uin
   return 0;
 }
 
-bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint16_t ednsrcode)
+bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint8_t ednsrcode)
 {
   if (dh->arcount != 0) {
     return false;
@@ -741,45 +741,3 @@ bool queryHasEDNS(const DNSQuestion& dq)
 
   return false;
 }
-
-int getEDNSVersion(const DNSQuestion& dq)
-try
-{
-  if (ntohs(dq.dh->qdcount) != 1 || dq.dh->ancount != 0 || ntohs(dq.dh->arcount) != 1 || dq.dh->nscount != 0) {
-    return 0;
-  }
-
-  if (dq.len <= sizeof(dnsheader)) {
-    return 0;
-  }
-
-  size_t pos = sizeof(dnsheader) + dq.consumed + DNS_TYPE_SIZE + DNS_CLASS_SIZE;
-
-  if (dq.len <= (pos + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE)) {
-    return 0;
-  }
-
-  const char* packet = reinterpret_cast<const char*>(dq.dh);
-
-  if (packet[pos] != 0) {
-    /* not root, so not a valid OPT record */
-    return 0;
-  }
-
-  pos++;
-
-  uint16_t qtype = (reinterpret_cast<const unsigned char*>(packet)[pos])*256 + reinterpret_cast<const unsigned char*>(packet)[pos+1];
-  pos += DNS_TYPE_SIZE;
-  pos += DNS_CLASS_SIZE;
-
-  if (qtype != QType::OPT || (pos + EDNS_EXTENDED_RCODE_SIZE + 1) >= dq.len) {
-    return 0;
-  }
-
-  const uint8_t* z = reinterpret_cast<const uint8_t*>(packet) + pos + EDNS_EXTENDED_RCODE_SIZE;
-  return 0x100 * (*z) + *(z+1);
-}
-catch(...)
-{
-  return 0;
-}
index 48c298b8641a09cd6bb3258cb5e9f11ffb7106d9..7c3739f443c2546c46101c4935169c9207c48496 100644 (file)
@@ -26,13 +26,13 @@ extern uint16_t g_PayloadSizeSelfGenAnswers;
 
 int rewriteResponseWithoutEDNS(const std::string& initialPacket, vector<uint8_t>& newContent);
 int locateEDNSOptRR(const std::string& packet, uint16_t * optStart, size_t * optLen, bool * last);
-void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint16_t ednsrcode, bool dnssecOK);
+void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint8_t ednsrcode, bool dnssecOK);
 void generateECSOption(const ComboAddress& source, string& res, uint16_t ECSPrefixLength);
 int removeEDNSOptionFromOPT(char* optStart, size_t* optLen, const uint16_t optionCodeToRemove);
 int rewriteResponseWithoutEDNSOption(const std::string& initialPacket, const uint16_t optionCodeToSkip, vector<uint8_t>& newContent);
 int getEDNSOptionsStart(const char* packet, const size_t offset, const size_t len, uint16_t* optRDPosition, size_t * remaining);
 bool isEDNSOptionInOpt(const std::string& packet, const size_t optStart, const size_t optLen, const uint16_t optionCodeToFind, size_t* optContentStart = nullptr, uint16_t* optContentLen = nullptr);
-bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint16_t ednsrcode);
+bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint8_t ednsrcode);
 bool addEDNSToQueryTurnedResponse(DNSQuestion& dq);
 
 bool handleEDNSClientSubnet(DNSQuestion& dq, bool* ednsAdded, bool* ecsAdded, bool preserveTrailingData);
@@ -42,4 +42,3 @@ bool parseEDNSOptions(DNSQuestion& dq);
 
 int getEDNSZ(const DNSQuestion& dq);
 bool queryHasEDNS(const DNSQuestion& dq);
-int getEDNSVersion(const DNSQuestion& dq);
index db70a8d4af7149afe77d76df99f5f7036c3d0dbf..55c7251e0aaca0866ec83c78e1ffc17cdf89c5bf 100644 (file)
@@ -310,6 +310,26 @@ private:
   uint8_t d_rcode;
 };
 
+class ERCodeAction : public DNSAction
+{
+public:
+  ERCodeAction(uint8_t rcode) : d_rcode(rcode) {}
+  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  {
+    dq->dh->rcode = (d_rcode & 0xF);
+    dq->ednsRCode = ((d_rcode & 0xFFF0) >> 4);
+    dq->dh->qr = true; // for good measure
+    return Action::HeaderModify;
+  }
+  string toString() const override
+  {
+    return "set ercode "+ERCode::to_s(d_rcode);
+  }
+
+private:
+  uint8_t d_rcode;
+};
+
 class TCAction : public DNSAction
 {
 public:
@@ -1163,6 +1183,10 @@ void setupLuaActions()
       return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
     });
 
+  g_lua.writeFunction("ERCodeAction", [](uint8_t rcode) {
+      return std::shared_ptr<DNSAction>(new ERCodeAction(rcode));
+    });
+
   g_lua.writeFunction("SkipCacheAction", []() {
       return std::shared_ptr<DNSAction>(new SkipCacheAction);
     });
index 9d83032bf95a95ee5dbca728de4bcfd3f6ba6dd9..b27a4a3d5c29bfe83331bbeb92995eeafdd879ec 100644 (file)
@@ -418,6 +418,10 @@ void setupLuaRules()
       return std::shared_ptr<DNSRule>(new ERCodeRule(rcode));
     });
 
+  g_lua.writeFunction("EDNSVersionRule", [](uint8_t version) {
+      return std::shared_ptr<DNSRule>(new EDNSVersionRule(version));
+    });
+
   g_lua.writeFunction("EDNSOptionRule", [](uint16_t optcode) {
       return std::shared_ptr<DNSRule>(new EDNSOptionRule(optcode));
     });
index 70a5ddb8ba460b3cd2e7926624cc9de2280da8ef..67750aefb542a5b114d473ee39c214f1f7654aa6 100644 (file)
@@ -1403,12 +1403,6 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct
       return;
     }
 
-    if (queryHasEDNS(dq) && getEDNSVersion(dq) > 0) {
-        dq.dh->qr = true;
-        dq.dh->rcode = (16 & 0xF);
-        dq.ednsRCode = ((16 & 0xFFF0)>>4); // set rcode to BADVERS
-    }
-
     if(dq.dh->qr) { // something turned it into a response
       fixUpQueryTurnedResponse(dq, origFlags);
 
index c61ff4a2de7ef2b28e456f37ba7b6c5fcb1cbbdc..9cb18adf2b78265a45b8b56767270e849ce1a3e2 100644 (file)
@@ -79,7 +79,7 @@ struct DNSQuestion
   unsigned int consumed{0};
   uint16_t len;
   uint16_t ecsPrefixLength;
-  uint16_t ednsRCode; // WARNING: this is really 12 bits
+  uint8_t ednsRCode;
   boost::optional<uint32_t> tempFailureTTL;
   const bool tcp;
   const struct timespec* queryTime;
index f343f4f5db82b7356553aa5baf9a50a991184c8d..e3fd36a04de1cd3b0856936a38d52aed0da289cb 100644 (file)
@@ -882,6 +882,49 @@ private:
   uint8_t d_extrcode;  // upper bits in EDNS0 record
 };
 
+class EDNSVersionRule : public DNSRule
+{
+public:
+  EDNSVersionRule(uint8_t version) : d_version(version)
+  {
+  }
+  bool matches(const DNSQuestion* dq) const override
+  {
+    uint16_t optStart;
+    size_t optLen = 0;
+    bool last = false;
+    const char * packet = reinterpret_cast<const char*>(dq->dh);
+    std::string packetStr(packet, dq->len);
+    int res = locateEDNSOptRR(packetStr, &optStart, &optLen, &last);
+    if (res != 0) {
+      // no EDNS OPT RR
+      return false;
+    }
+
+    // root label (1), type (2), class (2), ttl (4) + rdlen (2)
+    if (optLen < 11) {
+      return false;
+    }
+
+    if (optStart < dq->len && packetStr.at(optStart) != 0) {
+      // OPT RR Name != '.'
+      return false;
+    }
+    EDNS0Record edns0;
+    static_assert(sizeof(EDNS0Record) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t) AKA RR TTL size");
+    // copy out 4-byte "ttl" (really the EDNS0 record), after root label (1) + type (2) + class (2).
+    memcpy(&edns0, packet + optStart + 5, sizeof edns0);
+
+    return d_version < edns0.version;
+  }
+  string toString() const override
+  {
+    return "ednsversion>"+std::to_string(d_version);
+  }
+private:
+  uint8_t d_version;
+};
+
 class EDNSOptionRule : public DNSRule
 {
 public: