From 29e6303ae880ef57e78377eb910687e8ae7b0779 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 18 Jun 2018 17:55:07 +0200 Subject: [PATCH] rec: Support multiple values for the same EDNS option in gettag And in the FFI version of gettag. --- pdns/dnsrecords.cc | 4 +- pdns/ednsoptions.cc | 10 +-- pdns/ednsoptions.hh | 11 ++- pdns/lua-recursor4.cc | 81 +++++++++++++++------ pdns/lua-recursor4.hh | 6 +- pdns/pdns_recursor.cc | 10 +-- pdns/recursordist/docs/lua-scripting/dq.rst | 16 +++- pdns/recursordist/test-ednsoptions_cc.cc | 18 +++-- 8 files changed, 105 insertions(+), 51 deletions(-) diff --git a/pdns/dnsrecords.cc b/pdns/dnsrecords.cc index 14a10bb7d..0eaeed74b 100644 --- a/pdns/dnsrecords.cc +++ b/pdns/dnsrecords.cc @@ -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); diff --git a/pdns/ednsoptions.cc b/pdns/ednsoptions.cc index eade5df5f..6b4ec1098 100644 --- a/pdns/ednsoptions.cc +++ b/pdns/ednsoptions.cc @@ -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& 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 (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; diff --git a/pdns/ednsoptions.hh b/pdns/ednsoptions.hh index 2fdc9a57d..019ac9bb9 100644 --- a/pdns/ednsoptions.hh +++ b/pdns/ednsoptions.hh @@ -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 values; +}; + +typedef std::map 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& options); +int getEDNSOptions(const char* optRR, size_t len, EDNSOptionViewMap& options); void generateEDNSOption(uint16_t optionCode, const std::string& payload, std::string& res); diff --git a/pdns/lua-recursor4.cc b/pdns/lua-recursor4.cc index cb16d9d3b..beb62ed36 100644 --- a/pdns/lua-recursor4.cc +++ b/pdns/lua-recursor4.cc @@ -158,7 +158,6 @@ boost::optional RecursorLua4::DNSQuestion::getEDNSOption(uint16_t code) boost::optional 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("getContent", [](const EDNSOptionView& option) { return std::string(option.content, option.size); }); + d_lw->registerMember("size", &EDNSOptionViewValue::size); + d_lw->registerFunction("getContent", [](const EDNSOptionViewValue& value) { return std::string(value.content, value.size); }); + d_lw->registerFunction("count", [](const EDNSOptionView& option) { return option.values.size(); }); + d_lw->registerFunction>(EDNSOptionView::*)()>("getValues", [] (const EDNSOptionView& option) { + std::vector > 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("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("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("getContent", [](const DNSRecord& dr) { return dr.d_content->getZoneRepresentation(); }); d_lw->registerFunction(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* policyTags, LuaContext::LuaObject& data, const std::map& 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* 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& policyTags_, const std::map& 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& 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& policyTags; - const std::map& 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* policyTags, LuaContext::LuaObject& data, const std::map& 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* 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) diff --git a/pdns/lua-recursor4.hh b/pdns/lua-recursor4.hh index 9130bac5f..10928c5cd 100644 --- a/pdns/lua-recursor4.hh +++ b/pdns/lua-recursor4.hh @@ -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* policyTags, LuaContext::LuaObject& data, const std::map&, 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* policyTags, LuaContext::LuaObject& data, const std::map&, 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* 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* 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 >,boost::optional,boost::optional,boost::optional >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t, const std::map&, bool)> gettag_t; + typedef std::function >,boost::optional,boost::optional,boost::optional >(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(pdns_ffi_param_t*)> gettag_ffi_t; gettag_ffi_t d_gettag_ffi; diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 07ea2d8fa..d01bef0c9 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -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* 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(&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 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 ednsOptions; + EDNSOptionViewMap ednsOptions; bool xpfFound = false; ecsFound = false; diff --git a/pdns/recursordist/docs/lua-scripting/dq.rst b/pdns/recursordist/docs/lua-scripting/dq.rst index a8c7621c2..9f6f78ec0 100644 --- a/pdns/recursordist/docs/lua-scripting/dq.rst +++ b/pdns/recursordist/docs/lua-scripting/dq.rst @@ -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. diff --git a/pdns/recursordist/test-ednsoptions_cc.cc b/pdns/recursordist/test-ednsoptions_cc.cc index fe12a962c..d62a64c19 100644 --- a/pdns/recursordist/test-ednsoptions_cc.cc +++ b/pdns/recursordist/test-ednsoptions_cc.cc @@ -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 options; + EDNSOptionViewMap options; int res = getEDNSOptions(reinterpret_cast(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) -- 2.40.0