From: Dmitry Alenichev Date: Wed, 23 Jan 2019 22:05:56 +0000 (+0300) Subject: replace RCODE=BADVERS response on version >0 with a rule to match on the X-Git-Tag: auth-4.2.0-beta1~9^2~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a9613dfeea2750356db04f756c32e08e11b73d6b;p=pdns replace RCODE=BADVERS response on version >0 with a rule to match on the highest implementation level of EDNS version and action to return the ERCode. --- diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index 01458399d..0ca1d581d 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -374,6 +374,7 @@ const std::vector 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" }, diff --git a/pdns/dnsdist-ecs.cc b/pdns/dnsdist-ecs.cc index cfd8f084c..56422e48a 100644 --- a/pdns/dnsdist-ecs.cc +++ b/pdns/dnsdist-ecs.cc @@ -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(dq.dh); - - if (packet[pos] != 0) { - /* not root, so not a valid OPT record */ - return 0; - } - - pos++; - - uint16_t qtype = (reinterpret_cast(packet)[pos])*256 + reinterpret_cast(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(packet) + pos + EDNS_EXTENDED_RCODE_SIZE; - return 0x100 * (*z) + *(z+1); -} -catch(...) -{ - return 0; -} diff --git a/pdns/dnsdist-ecs.hh b/pdns/dnsdist-ecs.hh index 48c298b86..7c3739f44 100644 --- a/pdns/dnsdist-ecs.hh +++ b/pdns/dnsdist-ecs.hh @@ -26,13 +26,13 @@ extern uint16_t g_PayloadSizeSelfGenAnswers; int rewriteResponseWithoutEDNS(const std::string& initialPacket, vector& 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& 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); diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index db70a8d4a..55c7251e0 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -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(new RCodeAction(rcode)); }); + g_lua.writeFunction("ERCodeAction", [](uint8_t rcode) { + return std::shared_ptr(new ERCodeAction(rcode)); + }); + g_lua.writeFunction("SkipCacheAction", []() { return std::shared_ptr(new SkipCacheAction); }); diff --git a/pdns/dnsdist-lua-rules.cc b/pdns/dnsdist-lua-rules.cc index 9d83032bf..b27a4a3d5 100644 --- a/pdns/dnsdist-lua-rules.cc +++ b/pdns/dnsdist-lua-rules.cc @@ -418,6 +418,10 @@ void setupLuaRules() return std::shared_ptr(new ERCodeRule(rcode)); }); + g_lua.writeFunction("EDNSVersionRule", [](uint8_t version) { + return std::shared_ptr(new EDNSVersionRule(version)); + }); + g_lua.writeFunction("EDNSOptionRule", [](uint16_t optcode) { return std::shared_ptr(new EDNSOptionRule(optcode)); }); diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 70a5ddb8b..67750aefb 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -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); diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index c61ff4a2d..9cb18adf2 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -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 tempFailureTTL; const bool tcp; const struct timespec* queryTime; diff --git a/pdns/dnsdistdist/dnsdist-rules.hh b/pdns/dnsdistdist/dnsdist-rules.hh index f343f4f5d..e3fd36a04 100644 --- a/pdns/dnsdistdist/dnsdist-rules.hh +++ b/pdns/dnsdistdist/dnsdist-rules.hh @@ -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(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: