]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Handle multiple EDNS0 Options in gettag 3537/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 10 Mar 2016 14:25:20 +0000 (15:25 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 10 Mar 2016 14:25:20 +0000 (15:25 +0100)
Refactor the handling of EDNS0 Options parsing to use the same code
in dnsdist and the recursor (packet cache and gettag).
Closes #3516.

16 files changed:
pdns/Makefile.am
pdns/dnsdist-ecs.cc
pdns/dnsdist-ecs.hh
pdns/dnsdist.hh
pdns/dnsdistdist/Makefile.am
pdns/dnsdistdist/ednsoptions.cc [new symlink]
pdns/dnsdistdist/ednsoptions.hh [new symlink]
pdns/dnsrulactions.hh
pdns/ednsoptions.cc [new file with mode: 0644]
pdns/ednsoptions.hh [new file with mode: 0644]
pdns/pdns_recursor.cc
pdns/recpacketcache.cc
pdns/recursordist/Makefile.am
pdns/recursordist/ednsoptions.cc [new symlink]
pdns/recursordist/ednsoptions.hh [new symlink]
pdns/test-dnsdist_cc.cc

index 20c36386590c655eab12d31b1ba2f35e42447c16..37a2cd92d2c2423c9cc01b0635e0fb57f4e09249 100644 (file)
@@ -1021,6 +1021,7 @@ testrunner_SOURCES = \
        dnsrecords.cc \
        dnssecinfra.cc \
        dnswriter.cc \
+       ednsoptions.cc \
        ednssubnet.cc \
        gss_context.cc gss_context.hh \
        iputils.cc \
index dc29efd4842b581dbad647e7113e49a7b8db3b16..cf9fdaaf9c64d1f7ae19f5fa09cb88465b93e18f 100644 (file)
@@ -4,6 +4,7 @@
 #include "dnsdist-ecs.hh"
 #include "dnsparser.hh"
 #include "dnswriter.hh"
+#include "ednsoptions.hh"
 #include "ednssubnet.hh"
 
 /* when we add EDNS to a query, we don't want to advertise
@@ -174,10 +175,10 @@ int locateEDNSOptRR(const char * packet, const size_t len, const char ** optStar
 }
 
 /* extract the start of the OPT RR in a QUERY packet if any */
-static int getEDNSOptionsStart(char* packet, const size_t offset, const size_t len, char ** optStart, size_t * remaining, unsigned char ** optRDLen)
+static int getEDNSOptionsStart(char* packet, const size_t offset, const size_t len, char ** optRDLen, size_t * remaining)
 {
   assert(packet != NULL);
-  assert(optStart != NULL);
+  assert(optRDLen != NULL);
   assert(remaining != NULL);
   const struct dnsheader* dh = (const struct dnsheader*) packet;
   
@@ -201,72 +202,20 @@ static int getEDNSOptionsStart(char* packet, const size_t offset, const size_t l
   if(qtype != QType::OPT || (len - pos) < (DNS_TTL_SIZE + DNS_RDLENGTH_SIZE))
     return ENOENT;
 
-  *optStart = packet + pos;
+  pos += DNS_TTL_SIZE;
+  *optRDLen = packet + pos;
   *remaining = len - pos;
 
-  if (optRDLen) {
-    *optRDLen = ((unsigned char*) packet + pos + DNS_TTL_SIZE);
-  }
-
   return 0;
 }
 
-/* extract a specific EDNS0 option from a pointer on the beginning of the OPT RR */
-static int getEDNSOption(char* optRR, const size_t len, const uint16_t wantedOption, char ** optionValue, size_t * optionValueSize)
-{
-  assert(optRR != NULL);
-  assert(optionValue != NULL);
-  assert(optionValueSize != NULL);
-  size_t pos = 0;
-
-  pos += DNS_TTL_SIZE;
-  const uint16_t rdLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]);
-  size_t rdPos = 0;
-  pos += DNS_RDLENGTH_SIZE;  
-
-  while(pos < (len - ((size_t) EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) &&
-        rdPos < (rdLen - ((size_t) EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE))) {
-    const uint16_t optionCode = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]);
-    pos += EDNS_OPTION_CODE_SIZE;
-    rdPos += EDNS_OPTION_CODE_SIZE;
-    const uint16_t optionLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]);
-    pos += EDNS_OPTION_LENGTH_SIZE;
-    rdPos += EDNS_OPTION_LENGTH_SIZE;
-
-    if (optionLen > (rdLen - rdPos) || optionLen > (len - pos))
-      return EINVAL;
-    
-    if (optionCode == wantedOption) {
-      *optionValue = optRR + pos - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE);
-      *optionValueSize = optionLen + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE;
-      return 0;
-    }
-    else {
-      /* skip this option */
-      pos += optionLen;
-      rdPos += optionLen;
-    }
-  }
-  
-  return ENOENT;
-}
-
-void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res)
-{
-  const uint16_t ecsOptionCode = htons(optionCode);
-  const uint16_t payloadLen = htons(payload.length());
-  res.append((const char *) &ecsOptionCode, sizeof ecsOptionCode);
-  res.append((const char *) &payloadLen, sizeof payloadLen);
-  res.append(payload);
-}
-
 static void generateECSOption(const ComboAddress& source, string& res)
 {
   Netmask sourceNetmask(source, source.sin4.sin_family == AF_INET ? g_ECSSourcePrefixV4 : g_ECSSourcePrefixV6);
   EDNSSubnetOpts ecsOpts;
   ecsOpts.source = sourceNetmask;
   string payload = makeEDNSSubnetOptsString(ecsOpts);
-  generateEDNSOption(EDNS0_OPTION_CODE_ECS, payload, res);
+  generateEDNSOption(EDNSOptionCode::ECS, payload, res);
 }
 
 void generateOptRR(const std::string& optRData, string& res)
@@ -343,20 +292,19 @@ void handleEDNSClientSubnet(char * const packet, const size_t packetSize, const
   assert(len != NULL);
   assert(consumed <= (size_t) *len);
   assert(ednsAdded != NULL);
-  char * optRRStart = NULL;
   unsigned char * optRDLen = NULL;
   size_t remaining = 0;
         
-  int res = getEDNSOptionsStart(packet, consumed, *len, &optRRStart, &remaining, &optRDLen);
+  int res = getEDNSOptionsStart(packet, consumed, *len, (char**) &optRDLen, &remaining);
         
   if (res == 0) {
     char * ecsOptionStart = NULL;
     size_t ecsOptionSize = 0;
     
-    res = getEDNSOption(optRRStart, remaining, EDNS0_OPTION_CODE_ECS, &ecsOptionStart, &ecsOptionSize);
+    res = getEDNSOption((char*)optRDLen, remaining, EDNSOptionCode::ECS, &ecsOptionStart, &ecsOptionSize);
     
     if (res == 0) {
-      /* there is already an EDNS0_OPTION_CODE_ECS value */
+      /* there is already an ECS value */
       if (g_ECSOverride) {
         replaceEDNSClientSubnetOption(packet, packetSize, len, largerPacket, remote, ecsOptionStart, ecsOptionSize, optRDLen);
       }
index eb782968a46c139a9430623f2e316f57d1c26d9f..c28985c4f3dffe0e241f2be77ae10811fcc8ff24 100644 (file)
@@ -4,7 +4,3 @@ int rewriteResponseWithoutEDNS(const char * packet, size_t len, vector<uint8_t>&
 int locateEDNSOptRR(const char * packet, size_t len, const char ** optStart, size_t * optLen, bool * last);
 void handleEDNSClientSubnet(char * packet, size_t packetSize, unsigned int consumed, uint16_t * len, string& largerPacket, bool * ednsAdded, const ComboAddress& remote);
 void generateOptRR(const std::string& optRData, string& res);
-void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res);
-
-
-
index 2a4e8b4b73347f4a5a4624d1d3e8a96e3586f1e7..ff910ffa65533aa9fe185bc48dd94542b435bb0c 100644 (file)
@@ -443,11 +443,6 @@ enum ednsHeaderFlags {
   EDNS_HEADER_FLAG_DO = 32768
 };
 
-enum ednsOptionCodes {
-  EDNS0_OPTION_CODE_NONE = 0,
-  EDNS0_OPTION_CODE_ECS = 8,
-};
-
 extern GlobalStateHolder<CarbonConfig> g_carbon;
 extern GlobalStateHolder<ServerPolicy> g_policy;
 extern GlobalStateHolder<servers_t> g_dstates;
index 5ba6f6ccd31168831f42081262969afcba9482f9..2b13b44ac92d93dde0a0e97c2d26719acf0b6ac4 100644 (file)
@@ -65,6 +65,7 @@ dnsdist_SOURCES = \
        dnsrulactions.hh \
        dnswriter.cc dnswriter.hh \
        dolog.hh \
+       ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc ednssubnet.hh \
        iputils.cc iputils.hh \
        lock.hh \
@@ -115,6 +116,7 @@ testrunner_SOURCES = \
        dnsparser.hh dnsparser.cc \
        dnswriter.cc dnswriter.hh \
        dolog.hh \
+       ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc ednssubnet.hh \
        iputils.cc iputils.hh \
        misc.cc misc.hh \
diff --git a/pdns/dnsdistdist/ednsoptions.cc b/pdns/dnsdistdist/ednsoptions.cc
new file mode 120000 (symlink)
index 0000000..c182e6f
--- /dev/null
@@ -0,0 +1 @@
+../ednsoptions.cc
\ No newline at end of file
diff --git a/pdns/dnsdistdist/ednsoptions.hh b/pdns/dnsdistdist/ednsoptions.hh
new file mode 120000 (symlink)
index 0000000..e11521c
--- /dev/null
@@ -0,0 +1 @@
+../ednsoptions.hh
\ No newline at end of file
index 8ebb09dc69658f0e76590b0a464e3e5333c400bc..3a0b857fabce63fd3b5bfb0877660253ca43c917 100644 (file)
@@ -2,6 +2,7 @@
 #include "dnsdist-ecs.hh"
 #include "dnsname.hh"
 #include "dolog.hh"
+#include "ednsoptions.hh"
 
 class MaxQPSIPRule : public DNSRule
 {
diff --git a/pdns/ednsoptions.cc b/pdns/ednsoptions.cc
new file mode 100644 (file)
index 0000000..89eaf29
--- /dev/null
@@ -0,0 +1,56 @@
+
+#include "dns.hh"
+#include "ednsoptions.hh"
+#include "iputils.hh"
+
+/* extract a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */
+int getEDNSOption(char* optRR, const size_t len, uint16_t wantedOption, char ** optionValue, size_t * optionValueSize)
+{
+  assert(optRR != NULL);
+  assert(optionValue != NULL);
+  assert(optionValueSize != NULL);
+  size_t pos = 0;
+  if (len < DNS_RDLENGTH_SIZE)
+    return EINVAL;
+
+  const uint16_t rdLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]);
+  size_t rdPos = 0;
+  pos += DNS_RDLENGTH_SIZE;
+  if ((pos + rdLen) > len) {
+    return EINVAL;
+  }
+
+  while(len >= (pos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE) &&
+        rdLen >= (rdPos + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE)) {
+    const uint16_t optionCode = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]);
+    pos += EDNS_OPTION_CODE_SIZE;
+    rdPos += EDNS_OPTION_CODE_SIZE;
+    const uint16_t optionLen = (((unsigned char) optRR[pos]) * 256) + ((unsigned char) optRR[pos+1]);
+    pos += EDNS_OPTION_LENGTH_SIZE;
+    rdPos += EDNS_OPTION_LENGTH_SIZE;
+    if (optionLen > (rdLen - rdPos) || optionLen > (len - pos))
+      return EINVAL;
+
+    if (optionCode == wantedOption) {
+      *optionValue = optRR + pos - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE);
+      *optionValueSize = optionLen + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE;
+      return 0;
+    }
+    else {
+      /* skip this option */
+      pos += optionLen;
+      rdPos += optionLen;
+    }
+  }
+
+  return ENOENT;
+}
+
+void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res)
+{
+  const uint16_t ednsOptionCode = htons(optionCode);
+  const uint16_t payloadLen = htons(payload.length());
+  res.append((const char *) &ednsOptionCode, sizeof ednsOptionCode);
+  res.append((const char *) &payloadLen, sizeof payloadLen);
+  res.append(payload);
+}
diff --git a/pdns/ednsoptions.hh b/pdns/ednsoptions.hh
new file mode 100644 (file)
index 0000000..7bd278f
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+    PowerDNS Versatile Database Driven Nameserver
+    Copyright (C) 2011 - 2016  Netherlabs Computer Consulting BV
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation
+
+    Additionally, the license of this program contains a special
+    exception which allows to distribute the program in binary form when
+    it is linked against OpenSSL.
+
+    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 St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef PDNS_EDNSOPTIONS_HH
+#define PDNS_EDNSOPTIONS_HH
+
+#include "namespaces.hh"
+
+struct EDNSOptionCode
+{
+  enum EDNSOptionCodeEnum {NSID=3, DAU=4, DHU=6, N3U=7, ECS=8, EXPIRE=9, COOKIE=10, TCPKEEPALIVE=11, PADDING=12, CHAIN=13};
+};
+
+/* extract a specific EDNS0 option from a pointer on the beginning rdLen of the OPT RR */
+int getEDNSOption(char* optRR, size_t len, uint16_t wantedOption, char ** optionValue, size_t * optionValueSize);
+void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res);
+
+#endif
index ccc45a1690a743cee9ec4df1d41845030184f2ef..fd6e453ed0ebcc5f81b13dfd619f9b6760c1c6cd 100644 (file)
@@ -80,6 +80,7 @@ extern SortList g_sortlist;
 #include "rpzloader.hh"
 #include "validate-recursor.hh"
 #include "rec-lua-conf.hh"
+#include "ednsoptions.hh"
 
 #ifndef RECURSOR
 #include "statbag.hh"
@@ -1156,16 +1157,23 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr
       unsigned int consumed=0;
       uint16_t qtype=0;
       try {
-        DNSName qname(question.c_str(), question.length(), sizeof(dnsheader), false, &qtype, 0, &consumed);
+        size_t questionLen = question.length();
+        DNSName qname(question.c_str(), questionLen, sizeof(dnsheader), false, &qtype, 0, &consumed);
         Netmask ednssubnet;
-        auto pos= sizeof(dnsheader)+consumed+4;        
-        if(ntohs(dh->arcount) == 1 && question.length() > pos + 16) { // this code can extract one (1) EDNS Subnet option
-          uint16_t optlen=0x100*question.at(pos+9)+question.at(pos+10);
-          uint16_t optcode=0x100*question.at(pos+11)+question.at(pos+12);
-          if(question.at(pos)==0 && question.at(pos+1)==0 && question.at(pos+2)==QType::OPT && optlen && optcode==8) {
-            EDNSSubnetOpts eso;
-            if(getEDNSSubnetOptsFromString(question.c_str()+pos+15, question.length()-15-pos, &eso)) {
-              ednssubnet=eso.source;
+        size_t pos= sizeof(dnsheader)+consumed+4;
+        /* at least OPT root label (1), type (2), class (2) and ttl (4) + OPT RR rdlen (2)
+           = 11 */
+        if(ntohs(dh->arcount) == 1 && questionLen > pos + 11) { // this code can extract one (1) EDNS Subnet option
+          /* OPT root label (1) followed by type (2) */
+          if(question.at(pos)==0 && question.at(pos+1)==0 && question.at(pos+2)==QType::OPT) {
+            char* ecsStart = nullptr;
+            size_t ecsLen = 0;
+            int res = getEDNSOption((char*)question.c_str()+pos+9, questionLen - pos - 9, EDNSOptionCode::ECS, &ecsStart, &ecsLen);
+            if (res == 0 && ecsLen > 4) {
+              EDNSSubnetOpts eso;
+              if(getEDNSSubnetOptsFromString(ecsStart + 4, ecsLen - 4, &eso)) {
+                ednssubnet=eso.source;
+              }
             }
           }
         }
index 2e91c431bd84e293d2a10df19d0caf645b096f14..873e36962b6be74ffcb3f6e1977921e64c344d52 100644 (file)
@@ -10,6 +10,7 @@
 #include "namespaces.hh"
 #include "lock.hh"
 #include "dnswriter.hh"
+#include "ednsoptions.hh"
 
 RecursorPacketCache::RecursorPacketCache()
 {
@@ -73,33 +74,14 @@ uint32_t RecursorPacketCache::canHashPacket(const std::string& origPacket)
      = 16
   */
   if(ntohs(dh->arcount)==1 && (p+16) < end) {
+    char* optionBegin = nullptr;
+    size_t optionLen = 0;
     /* skip the final empty label (1), the qtype (2), qclass (2) */
     /* root label (1), type (2), class (2) and ttl (4) */
-    const unsigned char *q = (const unsigned char*) p + 14;
-    uint16_t optRRLen = (0x100*q[0] + q[1]);
-    q += 2;
-    if ((q + optRRLen) <= (const unsigned char*) end) {
-      const unsigned char* optRRend = q + optRRLen;
-      while((q + 4) <= optRRend) {
-        const unsigned char* optionBegin = q;
-        uint16_t optionCode = 0x100*q[0] + q[1];
-        //cout << "OPT RR Option Code is " << optionCode << endl;
-        q += 2;
-        uint16_t optionLen = 0x100*q[0] + q[1];
-        //cout << "OPT RR Option Length is " << optionLen << endl;
-        q += 2;
-        if ((q + optionLen) > (const unsigned char*) end) {
-          break;
-        }
-        if (optionCode == 8) {
-          //cout << "Skipping OPT RR Option Client Subnet:" << endl;
-          //cout << makeHexDump(string((const char*)optionBegin, (const char*) q + optionLen)) << endl;
-          skipBegin = (const char*) optionBegin;
-          skipEnd = (const char*) q + optionLen;
-          break;
-        }
-        q += optionLen;
-      }
+    int res = getEDNSOption((char*) p + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
+    if (res == 0) {
+      skipBegin = optionBegin;
+      skipEnd = optionBegin + optionLen;
     }
   }
   if (skipBegin > p) {
index 929895726dbaf96fc684e44afaf131f8dea90d08..7af1cf179f44acd137665d4b869abae2781c52ec 100644 (file)
@@ -72,6 +72,7 @@ pdns_recursor_SOURCES = \
        dnssecinfra.hh dnssecinfra.cc \
        dnsseckeeper.hh \
        dnswriter.cc dnswriter.hh \
+       ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc ednssubnet.hh \
        filterpo.cc filterpo.hh \
        gss_context.cc gss_context.hh \
diff --git a/pdns/recursordist/ednsoptions.cc b/pdns/recursordist/ednsoptions.cc
new file mode 120000 (symlink)
index 0000000..c182e6f
--- /dev/null
@@ -0,0 +1 @@
+../ednsoptions.cc
\ No newline at end of file
diff --git a/pdns/recursordist/ednsoptions.hh b/pdns/recursordist/ednsoptions.hh
new file mode 120000 (symlink)
index 0000000..e11521c
--- /dev/null
@@ -0,0 +1 @@
+../ednsoptions.hh
\ No newline at end of file
index 7331678cd1b7bff8ecbf54c251975bf784b878e0..d2bba08e8728881e363b61a5e03b3b5f5a2bbf5d 100644 (file)
@@ -32,6 +32,7 @@
 #include "dnsname.hh"
 #include "dnsparser.hh"
 #include "dnswriter.hh"
+#include "ednsoptions.hh"
 #include "ednssubnet.hh"
 #include <unistd.h>
 
@@ -164,7 +165,7 @@ BOOST_AUTO_TEST_CASE(replaceECSWithSameSize) {
   ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
   string origECSOption = makeEDNSSubnetOptsString(ecsOpts);
   DNSPacketWriter::optvect_t opts;
-  opts.push_back(make_pair(EDNS0_OPTION_CODE_ECS, origECSOption));
+  opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOption));
   pw.addOpt(512, 0, 0, opts);
   pw.commit();
   uint16_t len = query.size();
@@ -201,7 +202,7 @@ BOOST_AUTO_TEST_CASE(replaceECSWithSmaller) {
   ecsOpts.source = Netmask(origRemote, 32);
   string origECSOption = makeEDNSSubnetOptsString(ecsOpts);
   DNSPacketWriter::optvect_t opts;
-  opts.push_back(make_pair(EDNS0_OPTION_CODE_ECS, origECSOption));
+  opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOption));
   pw.addOpt(512, 0, 0, opts);
   pw.commit();
   uint16_t len = query.size();
@@ -238,7 +239,7 @@ BOOST_AUTO_TEST_CASE(replaceECSWithLarger) {
   ecsOpts.source = Netmask(origRemote, 8);
   string origECSOption = makeEDNSSubnetOptsString(ecsOpts);
   DNSPacketWriter::optvect_t opts;
-  opts.push_back(make_pair(EDNS0_OPTION_CODE_ECS, origECSOption));
+  opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOption));
   pw.addOpt(512, 0, 0, opts);
   pw.commit();
   uint16_t len = query.size();