]> granicus.if.org Git - pdns/commitdiff
rec: Support multiple values for the same EDNS option in gettag
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 18 Jun 2018 15:55:07 +0000 (17:55 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 19 Jun 2018 11:54:11 +0000 (13:54 +0200)
And in the FFI version of gettag.

pdns/dnsrecords.cc
pdns/ednsoptions.cc
pdns/ednsoptions.hh
pdns/lua-recursor4.cc
pdns/lua-recursor4.hh
pdns/pdns_recursor.cc
pdns/recursordist/docs/lua-scripting/dq.rst
pdns/recursordist/test-ednsoptions_cc.cc

index 14a10bb7def2f5d4def3ecb8048278149824aa62..0eaeed74bae355cfffaa79aff323b9081fcb0aab 100644 (file)
@@ -548,12 +548,12 @@ bool getEDNSOpts(const MOADNSParser& mdp, EDNSOpts* eo)
     for(const MOADNSParser::answers_t::value_type& val :  mdp.d_answers) {
       if(val.first.d_place == DNSResourceRecord::ADDITIONAL && val.first.d_type == QType::OPT) {
         eo->d_packetsize=val.first.d_class;
-       
+
         EDNS0Record stuff;
         uint32_t ttl=ntohl(val.first.d_ttl);
         static_assert(sizeof(EDNS0Record) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t)");
         memcpy(&stuff, &ttl, sizeof(stuff));
-        
+
         eo->d_extRCode=stuff.extRCode;
         eo->d_version=stuff.version;
         eo->d_extFlags = ntohs(stuff.extFlags);
index eade5df5fffaf2d007b74fe143d686e781ba988c..6b4ec1098ff2c8ac7d0607992abde92616177770 100644 (file)
@@ -67,7 +67,7 @@ int getEDNSOption(char* optRR, const size_t len, uint16_t wantedOption, char **
 }
 
 /* extract all EDNS0 options from a pointer on the beginning rdLen of the OPT RR */
-int getEDNSOptions(const char* optRR, const size_t len, std::map<uint16_t, EDNSOptionView>& options)
+int getEDNSOptions(const char* optRR, const size_t len, EDNSOptionViewMap& options)
 {
   assert(optRR != NULL);
   size_t pos = 0;
@@ -92,10 +92,10 @@ int getEDNSOptions(const char* optRR, const size_t len, std::map<uint16_t, EDNSO
     if (optionLen > (rdLen - rdPos) || optionLen > (len - pos))
       return EINVAL;
 
-    EDNSOptionView view;
-    view.content = optRR + pos;
-    view.size = optionLen;
-    options[optionCode] = view;
+    EDNSOptionViewValue value;
+    value.content = optRR + pos;
+    value.size = optionLen;
+    options[optionCode].values.push_back(std::move(value));
 
     /* skip this option */
     pos += optionLen;
index 2fdc9a57d0ca7ff19621fa2c125ceab5d95e89ea..019ac9bb9b01d343d7f8fdf62b00dd7ac19f2f42 100644 (file)
@@ -32,14 +32,21 @@ struct EDNSOptionCode
 /* 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);
 
-struct EDNSOptionView
+struct EDNSOptionViewValue
 {
   const char* content{nullptr};
   uint16_t size{0};
 };
 
+struct EDNSOptionView
+{
+  std::vector<EDNSOptionViewValue> values;
+};
+
+typedef std::map<uint16_t, EDNSOptionView> EDNSOptionViewMap;
+
 /* extract all EDNS0 options from a pointer on the beginning rdLen of the OPT RR */
-int getEDNSOptions(const char* optRR, size_t len, std::map<uint16_t, EDNSOptionView>& options);
+int getEDNSOptions(const char* optRR, size_t len, EDNSOptionViewMap& options);
 
 void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res);
 
index cb16d9d3b30552b5e278c98207fe39e989c2fdae..beb62ed36b2e51b86e58b41c8f9b892d817a9396 100644 (file)
@@ -158,7 +158,6 @@ boost::optional<string>  RecursorLua4::DNSQuestion::getEDNSOption(uint16_t code)
 
 boost::optional<Netmask>  RecursorLua4::DNSQuestion::getEDNSSubnet() const
 {
-
   if(ednsOptions) {
     for(const auto& o : *ednsOptions) {
       if(o.first==EDNSOptionCode::ECS) {
@@ -273,8 +272,32 @@ void RecursorLua4::postPrepareContext()
   d_lw->registerMember("ttl", &DNSRecord::d_ttl);
   d_lw->registerMember("place", &DNSRecord::d_place);
 
-  d_lw->registerMember("size", &EDNSOptionView::size);
-  d_lw->registerFunction<std::string(EDNSOptionView::*)()>("getContent", [](const EDNSOptionView& option) { return std::string(option.content, option.size); });
+  d_lw->registerMember("size", &EDNSOptionViewValue::size);
+  d_lw->registerFunction<std::string(EDNSOptionViewValue::*)()>("getContent", [](const EDNSOptionViewValue& value) { return std::string(value.content, value.size); });
+  d_lw->registerFunction<size_t(EDNSOptionView::*)()>("count", [](const EDNSOptionView& option) { return option.values.size(); });
+  d_lw->registerFunction<std::vector<std::pair<int, string>>(EDNSOptionView::*)()>("getValues", [] (const EDNSOptionView& option) {
+      std::vector<std::pair<int, string> > values;
+      for (const auto& value : option.values) {
+        values.push_back(std::make_pair(values.size(), std::string(value.content, value.size)));
+      }
+      return values;
+    });
+
+  /* pre 4.2 API compatibility, when we had only one value for a given EDNS option */
+  d_lw->registerMember<uint16_t(EDNSOptionView::*)>("size", [](const EDNSOptionView& option) -> uint16_t {
+      uint16_t result = 0;
+
+      if (!option.values.empty()) {
+        result = option.values.at(0).size;
+      }
+      return result;
+    },
+    [](EDNSOptionView& option, uint16_t newSize) { (void) newSize; });
+  d_lw->registerFunction<std::string(EDNSOptionView::*)()>("getContent", [](const EDNSOptionView& option) {
+      if (option.values.empty()) {
+        return std::string();
+      }
+      return std::string(option.values.at(0).content, option.values.at(0).size); });
 
   d_lw->registerFunction<string(DNSRecord::*)()>("getContent", [](const DNSRecord& dr) { return dr.d_content->getZoneRepresentation(); });
   d_lw->registerFunction<boost::optional<ComboAddress>(DNSRecord::*)()>("getCA", [](const DNSRecord& dr) { 
@@ -458,7 +481,7 @@ bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& loca
   return false; // don't block
 }
 
-unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const std::map<uint16_t, EDNSOptionView>& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId) const
+unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId) const
 {
   if(d_gettag) {
     auto ret = d_gettag(remote, ednssubnet, local, qname, qtype, ednsOptions, tcp);
@@ -491,7 +514,7 @@ unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& edn
 struct pdns_ffi_param
 {
 public:
-  pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::vector<std::string>& policyTags_, const std::map<uint16_t, EDNSOptionView>& ednsOptions_, std::string& requestorId_, std::string& deviceId_, uint32_t& ttlCap_, bool& variable_, bool tcp_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), ednsOptions(ednsOptions_), requestorId(requestorId_), deviceId(deviceId_), ttlCap(ttlCap_), variable(variable_), qtype(qtype_), tcp(tcp_)
+  pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::vector<std::string>& policyTags_, const EDNSOptionViewMap& ednsOptions_, std::string& requestorId_, std::string& deviceId_, uint32_t& ttlCap_, bool& variable_, bool tcp_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), ednsOptions(ednsOptions_), requestorId(requestorId_), deviceId(deviceId_), ttlCap(ttlCap_), variable(variable_), qtype(qtype_), tcp(tcp_)
   {
   }
 
@@ -506,7 +529,7 @@ public:
   const ComboAddress& remote;
   const Netmask& ednssubnet;
   std::vector<std::string>& policyTags;
-  const std::map<uint16_t, EDNSOptionView>& ednsOptions;
+  const EDNSOptionViewMap& ednsOptions;
   std::string& requestorId;
   std::string& deviceId;
   uint32_t& ttlCap;
@@ -517,7 +540,7 @@ public:
   bool tcp;
 };
 
-unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const std::map<uint16_t, EDNSOptionView>& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, uint32_t& ttlCap, bool& variable) const
+unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, uint32_t& ttlCap, bool& variable) const
 {
   if (d_gettag_ffi) {
     pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, ednsOptions, requestorId, deviceId, ttlCap, variable, tcp);
@@ -692,13 +715,13 @@ uint8_t pdns_ffi_param_get_edns_cs_source_mask(const pdns_ffi_param_t* ref)
   return ref->ednssubnet.getBits();
 }
 
-static void fill_edns_option(const EDNSOptionView& view, pdns_ednsoption_t& option)
+static void fill_edns_option(const EDNSOptionViewValue& value, pdns_ednsoption_t& option)
 {
-  option.len = view.size;
+  option.len = value.size;
   option.data = nullptr;
 
-  if (view.size > 0) {
-    option.data = view.content;
+  if (value.size > 0) {
+    option.data = value.content;
   }
 }
 
@@ -708,36 +731,46 @@ size_t pdns_ffi_param_get_edns_options(pdns_ffi_param_t* ref, const pdns_ednsopt
     return 0;
   }
 
-  size_t count = ref->ednsOptions.size();
-  ref->ednsOptionsVect.resize(count);
+  size_t totalCount = 0;
+  for (const auto& option : ref->ednsOptions) {
+    totalCount += option.second.values.size();
+  }
+
+  ref->ednsOptionsVect.resize(totalCount);
 
   size_t pos = 0;
-  for (const auto& entry : ref->ednsOptions) {
-    fill_edns_option(entry.second, ref->ednsOptionsVect.at(pos));
-    ref->ednsOptionsVect.at(pos).optionCode = entry.first;
-    pos++;
+  for (const auto& option : ref->ednsOptions) {
+    for (const auto& entry : option.second.values) {
+      fill_edns_option(entry, ref->ednsOptionsVect.at(pos));
+      ref->ednsOptionsVect.at(pos).optionCode = option.first;
+      pos++;
+    }
   }
 
   *out = ref->ednsOptionsVect.data();
 
-  return count;
+  return totalCount;
 }
 
 size_t pdns_ffi_param_get_edns_options_by_code(pdns_ffi_param_t* ref, uint16_t optionCode, const pdns_ednsoption_t** out)
 {
   const auto& it = ref->ednsOptions.find(optionCode);
-  if (it == ref->ednsOptions.cend()) {
+  if (it == ref->ednsOptions.cend() || it->second.values.empty()) {
     return 0;
   }
 
-  /* the current code deals with only one entry per code, but we will fix that */
-  ref->ednsOptionsVect.resize(1);
-  fill_edns_option(it->second, ref->ednsOptionsVect.at(0));
-  ref->ednsOptionsVect.at(0).optionCode = it->first;
+  ref->ednsOptionsVect.resize(it->second.values.size());
+
+  size_t pos = 0;
+  for (const auto& entry : it->second.values) {
+    fill_edns_option(entry, ref->ednsOptionsVect.at(pos));
+    ref->ednsOptionsVect.at(pos).optionCode = optionCode;
+    pos++;
+  }
 
   *out = ref->ednsOptionsVect.data();
 
-  return 1;
+  return pos;
 }
 
 void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag)
index 9130bac5faf846c559f69152ea8764d0f3df3100..10928c5cd942b5298b4a8a33c9a6c1d26184fe70 100644 (file)
@@ -109,8 +109,8 @@ public:
     DNSName followupName;
   };
 
-  unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const std::map<uint16_t, EDNSOptionView>&, bool tcp, std::string& requestorId, std::string& deviceId) const;
-  unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const std::map<uint16_t, EDNSOptionView>&, bool tcp, std::string& requestorId, std::string& deviceId, uint32_t& ttlCap, bool& variable) const;
+  unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId) const;
+  unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const EDNSOptionViewMap&, bool tcp, std::string& requestorId, std::string& deviceId, uint32_t& ttlCap, bool& variable) const;
 
   void maintenance() const;
   bool prerpz(DNSQuestion& dq, int& ret) const;
@@ -131,7 +131,7 @@ public:
             d_postresolve);
   }
 
-  typedef std::function<std::tuple<unsigned int,boost::optional<std::unordered_map<int,string> >,boost::optional<LuaContext::LuaObject>,boost::optional<std::string>,boost::optional<std::string> >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t, const std::map<uint16_t, EDNSOptionView>&, bool)> gettag_t;
+  typedef std::function<std::tuple<unsigned int,boost::optional<std::unordered_map<int,string> >,boost::optional<LuaContext::LuaObject>,boost::optional<std::string>,boost::optional<std::string> >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t, const EDNSOptionViewMap&, bool)> gettag_t;
   gettag_t d_gettag; // public so you can query if we have this hooked
   typedef std::function<boost::optional<LuaContext::LuaObject>(pdns_ffi_param_t*)> gettag_ffi_t;
   gettag_ffi_t d_gettag_ffi;
index 07ea2d8fa19305dc67e8b4b9fde2021f720442a9..d01bef0c99e9d22a19239a1b28b44df7bbc6bda6 100644 (file)
@@ -1521,7 +1521,7 @@ static void makeControlChannelSocket(int processNum=-1)
 }
 
 static void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
-                              bool& foundECS, EDNSSubnetOpts* ednssubnet, std::map<uint16_t, EDNSOptionView>* options,
+                              bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options,
                               bool& foundXPF, ComboAddress* xpfSource, ComboAddress* xpfDest)
 {
   const bool lookForXPF = xpfSource != nullptr && g_xpfRRCode != 0;
@@ -1569,9 +1569,9 @@ static void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uin
         int res = getEDNSOptions(reinterpret_cast<const char*>(&question.at(pos -sizeof(drh->d_clen))), questionLen - pos + (sizeof(drh->d_clen)), *options);
         if (res == 0) {
           const auto& it = options->find(EDNSOptionCode::ECS);
-          if (it != options->end() && it->second.content != nullptr && it->second.size > 0) {
+          if (it != options->end() && !it->second.values.empty() && it->second.values.at(0).content != nullptr && it->second.values.at(0).size > 0) {
             EDNSSubnetOpts eso;
-            if(getEDNSSubnetOptsFromString(it->second.content, it->second.size, &eso)) {
+            if(getEDNSSubnetOptsFromString(it->second.values.at(0).content, it->second.values.at(0).size, &eso)) {
               *ednssubnet=eso;
               foundECS = true;
             }
@@ -1673,7 +1673,7 @@ static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
       if(needECS || needXPF || (t_pdl && (t_pdl->d_gettag_ffi || t_pdl->d_gettag))) {
 
         try {
-          std::map<uint16_t, EDNSOptionView> ednsOptions;
+          EDNSOptionViewMap ednsOptions;
           bool xpfFound = false;
           dc->d_ecsParsed = true;
           dc->d_ecsFound = false;
@@ -1863,7 +1863,7 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr
 
     if(needECS || needXPF || (t_pdl && (t_pdl->d_gettag || t_pdl->d_gettag_ffi))) {
       try {
-        std::map<uint16_t, EDNSOptionView> ednsOptions;
+        EDNSOptionViewMap ednsOptions;
         bool xpfFound = false;
 
         ecsFound = false;
index a8c7621c22f7f9b0c63f3b2afbe40d3dceff0984..9f6f78ec0a49426607e9b94fb28b3c25a8a2cf9f 100644 (file)
@@ -259,12 +259,22 @@ The EDNSOptionView Class
 
 .. class:: EDNSOptionView
 
-  An object that represents a single EDNS option
+  An object that represents the values of a single EDNS option
+
+  .. method:: EDNSOptionView:count()
+     .. versionadded:: 4.2.0
+
+    The number of values for this EDNS option.
+
+  .. method:: EDNSOptionView:getValues()
+     .. versionadded:: 4.2.0
+
+    Return a table of NULL-safe strings values for this EDNS option.
 
   .. attribute:: EDNSOptionView.size
 
-    The size in bytes of the EDNS option.
+    The size in bytes of the first value of this EDNS option.
 
   .. method:: EDNSOptionView:getContent()
 
-    Returns a NULL-safe string object of the EDNS option's content
+    Returns a NULL-safe string object of the first value of this EDNS option.
index fe12a962cca48c3d4e30caa85fbc2abae51fe042..d62a64c194c8cbd195db0c83d905585d048c9f2e 100644 (file)
@@ -87,26 +87,30 @@ BOOST_AUTO_TEST_CASE(test_getEDNSOptions) {
   BOOST_REQUIRE_EQUAL(query.at(pos), 0);
   BOOST_REQUIRE(query.at(pos+2) == QType::OPT);
 
-  std::map<uint16_t, EDNSOptionView> options;
+  EDNSOptionViewMap options;
   int res = getEDNSOptions(reinterpret_cast<char*>(query.data())+pos+9, questionLen - pos - 9, options);
   BOOST_REQUIRE_EQUAL(res, 0);
 
-  /* 3 EDNS options but two of them are EDNS Cookie, so we only keep one */
+  /* 3 EDNS options but two of them are EDNS Cookie, so we only have two entries in the map */
   BOOST_CHECK_EQUAL(options.size(), 2);
 
   auto it = options.find(EDNSOptionCode::ECS);
   BOOST_REQUIRE(it != options.end());
-  BOOST_REQUIRE(it->second.content != nullptr);
-  BOOST_REQUIRE_GT(it->second.size, 0);
+  BOOST_REQUIRE_EQUAL(it->second.values.size(), 1);
+  BOOST_REQUIRE(it->second.values.at(0).content != nullptr);
+  BOOST_REQUIRE_GT(it->second.values.at(0).size, 0);
 
   EDNSSubnetOpts eso;
-  BOOST_REQUIRE(getEDNSSubnetOptsFromString(it->second.content, it->second.size, &eso));
+  BOOST_REQUIRE(getEDNSSubnetOptsFromString(it->second.values.at(0).content, it->second.values.at(0).size, &eso));
   BOOST_CHECK(eso.source == ecs);
 
   it = options.find(EDNSOptionCode::COOKIE);
   BOOST_REQUIRE(it != options.end());
-  BOOST_REQUIRE(it->second.content != nullptr);
-  BOOST_REQUIRE_GT(it->second.size, 0);
+  BOOST_REQUIRE_EQUAL(it->second.values.size(), 2);
+  BOOST_REQUIRE(it->second.values.at(0).content != nullptr);
+  BOOST_REQUIRE_GT(it->second.values.at(0).size, 0);
+  BOOST_REQUIRE(it->second.values.at(1).content != nullptr);
+  BOOST_REQUIRE_GT(it->second.values.at(1).size, 0);
 }
 
 static void checkECSOptionValidity(const std::string& sourceStr, uint8_t sourceMask, uint8_t scopeMask)