]> granicus.if.org Git - pdns/commitdiff
respond with RCODE=BADVERS on EDNS version >0 (rfc6891)
authorDmitry Alenichev <mitya@vk.com>
Tue, 22 Jan 2019 17:43:39 +0000 (20:43 +0300)
committerDmitry Alenichev <mitya@vk.com>
Tue, 22 Jan 2019 17:43:39 +0000 (20:43 +0300)
pdns/dnsdist-ecs.cc
pdns/dnsdist-ecs.hh
pdns/dnsdist-lua-actions.cc
pdns/dnsdist.cc
pdns/dnsdist.hh

index dbb6cad589d85d2c0fa0e8a8bb7e94d6e3fd418a..cfd8f084c088eff63a14b351d6f47857c28ef92e 100644 (file)
@@ -244,12 +244,12 @@ 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, bool dnssecOK)
+void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint16_t ednsrcode, bool dnssecOK)
 {
   const uint8_t name = 0;
   dnsrecordheader dh;
   EDNS0Record edns0;
-  edns0.extRCode = 0;
+  edns0.extRCode = ednsrcode;
   edns0.version = 0;
   edns0.extFlags = dnssecOK ? htons(EDNS_HEADER_FLAG_DO) : 0;
 
@@ -358,7 +358,7 @@ static bool addEDNSWithECS(char* const packet, size_t const packetSize, uint16_t
   /* we need to add a EDNS0 RR with one EDNS0 ECS option, fixing the AR count */
   string EDNSRR;
   struct dnsheader* dh = reinterpret_cast<struct dnsheader*>(packet);
-  generateOptRR(newECSOption, EDNSRR, g_EdnsUDPPayloadSize, false);
+  generateOptRR(newECSOption, EDNSRR, g_EdnsUDPPayloadSize, 0, false);
 
   /* does it fit in the existing buffer? */
   if (packetSize - *len <= EDNSRR.size()) {
@@ -626,14 +626,14 @@ 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)
+bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint16_t ednsrcode)
 {
   if (dh->arcount != 0) {
     return false;
   }
 
   std::string optRecord;
-  generateOptRR(std::string(), optRecord, payloadSize, dnssecOK);
+  generateOptRR(std::string(), optRecord, payloadSize, ednsrcode, dnssecOK);
 
   if (optRecord.size() >= size || (size - optRecord.size()) < len) {
     return false;
@@ -679,7 +679,7 @@ bool addEDNSToQueryTurnedResponse(DNSQuestion& dq)
 
   if (g_addEDNSToSelfGeneratedResponses) {
     /* now we need to add a new OPT record */
-    return addEDNS(dq.dh, dq.len, dq.size, dnssecOK, g_PayloadSizeSelfGenAnswers);
+    return addEDNS(dq.dh, dq.len, dq.size, dnssecOK, g_PayloadSizeSelfGenAnswers, dq.ednsRCode);
   }
 
   /* otherwise we are just fine */
@@ -741,3 +741,45 @@ 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 dd0a7829312d06a1f8730b59fed1d34ee00ff017..48c298b8641a09cd6bb3258cb5e9f11ffb7106d9 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, bool dnssecOK);
+void generateOptRR(const std::string& optRData, string& res, uint16_t udpPayloadSize, uint16_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);
+bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize, uint16_t ednsrcode);
 bool addEDNSToQueryTurnedResponse(DNSQuestion& dq);
 
 bool handleEDNSClientSubnet(DNSQuestion& dq, bool* ednsAdded, bool* ecsAdded, bool preserveTrailingData);
@@ -42,3 +42,4 @@ bool parseEDNSOptions(DNSQuestion& dq);
 
 int getEDNSZ(const DNSQuestion& dq);
 bool queryHasEDNS(const DNSQuestion& dq);
+int getEDNSVersion(const DNSQuestion& dq);
index 43fa9aa3c1918ffd1b30927b3aa810242a74a9dd..db70a8d4af7149afe77d76df99f5f7036c3d0dbf 100644 (file)
@@ -462,7 +462,7 @@ DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, string* ruleresult) c
   dq->dh->ancount = htons(dq->dh->ancount);
 
   if (hadEDNS) {
-    addEDNS(dq->dh, dq->len, dq->size, dnssecOK, g_PayloadSizeSelfGenAnswers);
+    addEDNS(dq->dh, dq->len, dq->size, dnssecOK, g_PayloadSizeSelfGenAnswers, 0);
   }
 
   return Action::HeaderModify;
@@ -486,7 +486,7 @@ public:
     generateEDNSOption(d_code, mac, optRData);
 
     string res;
-    generateOptRR(optRData, res, g_EdnsUDPPayloadSize, false);
+    generateOptRR(optRData, res, g_EdnsUDPPayloadSize, 0, false);
 
     if ((dq->size - dq->len) < res.length())
       return Action::None;
index 3e0607e6586ebca4203853550d8cd97f72bdf15a..70a5ddb8ba460b3cd2e7926624cc9de2280da8ef 100644 (file)
@@ -160,7 +160,7 @@ try
   dh->ancount = dh->arcount = dh->nscount = 0;
 
   if (hadEDNS) {
-    addEDNS(dh, *len, responseSize, z & EDNS_HEADER_FLAG_DO, payloadSize);
+    addEDNS(dh, *len, responseSize, z & EDNS_HEADER_FLAG_DO, payloadSize, 0);
   }
 }
 catch(...)
@@ -1403,6 +1403,12 @@ 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 8b36320f6c43ab063710875b14f913399294def5..c61ff4a2de7ef2b28e456f37ba7b6c5fcb1cbbdc 100644 (file)
@@ -79,6 +79,7 @@ struct DNSQuestion
   unsigned int consumed{0};
   uint16_t len;
   uint16_t ecsPrefixLength;
+  uint16_t ednsRCode; // WARNING: this is really 12 bits
   boost::optional<uint32_t> tempFailureTTL;
   const bool tcp;
   const struct timespec* queryTime;