From 53c57da74b609d48b137dca312623ebc16632ad2 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 21 Sep 2018 20:06:06 +0200 Subject: [PATCH] dnsdist: Handle trailing data correctly when adding OPT or ECS info --- pdns/dnsdist-console.cc | 1 + pdns/dnsdist-ecs.cc | 26 ++- pdns/dnsdist-ecs.hh | 4 +- pdns/dnsdist-lua-actions.cc | 2 +- pdns/dnsdist-lua.cc | 2 + pdns/dnsdist-tcp.cc | 5 +- pdns/dnsdist-xpf.cc | 67 ++++++++ pdns/dnsdist-xpf.hh | 27 +++ pdns/dnsdist.cc | 41 +---- pdns/dnsdist.hh | 1 + pdns/dnsdistdist/Makefile.am | 2 + pdns/dnsdistdist/dnsdist-xpf.cc | 1 + pdns/dnsdistdist/dnsdist-xpf.hh | 1 + pdns/dnsdistdist/docs/advanced/ecs.rst | 2 + pdns/dnsparser.cc | 2 +- pdns/test-dnsdist_cc.cc | 217 ++++++++++++++++++++++--- pdns/test-dnsparser_cc.cc | 26 +++ 17 files changed, 354 insertions(+), 73 deletions(-) create mode 100644 pdns/dnsdist-xpf.cc create mode 100644 pdns/dnsdist-xpf.hh create mode 120000 pdns/dnsdistdist/dnsdist-xpf.cc create mode 120000 pdns/dnsdistdist/dnsdist-xpf.hh diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index a697912bc..10f1e9191 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -430,6 +430,7 @@ const std::vector g_consoleKeywords{ { "setPayloadSizeOnSelfGeneratedAnswers", true, "payloadSize", "set the UDP payload size advertised via EDNS on self-generated responses" }, { "setPoolServerPolicy", true, "policy, pool", "set the server selection policy for this pool to that policy" }, { "setPoolServerPolicy", true, "name, func, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" }, + { "setPreserveTrailingData", true, "bool", "set whether trailing data should be preserved while adding ECS or XPF records to incoming queries" }, { "setQueryCount", true, "bool", "set whether queries should be counted" }, { "setQueryCountFilter", true, "func", "filter queries that would be counted, where `func` is a function with parameter `dq` which decides whether a query should and how it should be counted" }, { "setRingBuffersLockRetries", true, "n", "set the number of attempts to get a non-blocking lock to a ringbuffer shard before blocking" }, diff --git a/pdns/dnsdist-ecs.cc b/pdns/dnsdist-ecs.cc index 71b852f25..d1cd71a86 100644 --- a/pdns/dnsdist-ecs.cc +++ b/pdns/dnsdist-ecs.cc @@ -353,7 +353,7 @@ static bool addECSToExistingOPT(char* const packet, size_t const packetSize, uin return true; } -static bool addEDNSWithECS(char* const packet, size_t const packetSize, uint16_t* const len, const string& newECSOption, bool* const ednsAdded) +static bool addEDNSWithECS(char* const packet, size_t const packetSize, uint16_t* const len, const string& newECSOption, bool* const ednsAdded, bool preserveTrailingData) { /* we need to add a EDNS0 RR with one EDNS0 ECS option, fixing the AR count */ string EDNSRR; @@ -365,17 +365,27 @@ static bool addEDNSWithECS(char* const packet, size_t const packetSize, uint16_t return false; } + uint32_t realPacketLen = getDNSPacketLength(packet, *len); + if (realPacketLen < *len && preserveTrailingData) { + size_t toMove = *len - realPacketLen; + memmove(packet + realPacketLen + EDNSRR.size(), packet + realPacketLen, toMove); + *len += EDNSRR.size(); + } + else { + *len = realPacketLen + EDNSRR.size(); + } + uint16_t arcount = ntohs(dh->arcount); arcount++; dh->arcount = htons(arcount); *ednsAdded = true; - memcpy(packet + *len, EDNSRR.c_str(), EDNSRR.size()); - *len += EDNSRR.size(); + memcpy(packet + realPacketLen, EDNSRR.c_str(), EDNSRR.size()); + return true; } -bool handleEDNSClientSubnet(char* const packet, const size_t packetSize, const unsigned int consumed, uint16_t* const len, bool* const ednsAdded, bool* const ecsAdded, bool overrideExisting, const string& newECSOption) +bool handleEDNSClientSubnet(char* const packet, const size_t packetSize, const unsigned int consumed, uint16_t* const len, bool* const ednsAdded, bool* const ecsAdded, bool overrideExisting, const string& newECSOption, bool preserveTrailingData) { assert(packet != nullptr); assert(len != nullptr); @@ -388,7 +398,7 @@ bool handleEDNSClientSubnet(char* const packet, const size_t packetSize, const u int res = getEDNSOptionsStart(packet, consumed, *len, &optRDPosition, &remaining); if (res != 0) { - return addEDNSWithECS(packet, packetSize, len, newECSOption, ednsAdded); + return addEDNSWithECS(packet, packetSize, len, newECSOption, ednsAdded, preserveTrailingData); } unsigned char* optRDLen = reinterpret_cast(packet) + optRDPosition; @@ -412,14 +422,14 @@ bool handleEDNSClientSubnet(char* const packet, const size_t packetSize, const u return true; } -bool handleEDNSClientSubnet(DNSQuestion& dq, bool* ednsAdded, bool* ecsAdded) +bool handleEDNSClientSubnet(DNSQuestion& dq, bool* ednsAdded, bool* ecsAdded, bool preserveTrailingData) { assert(dq.remote != nullptr); string newECSOption; - generateECSOption(dq.ecsSet ? dq.ecs.getNetwork() : *dq.remote, newECSOption, dq.ecsSet ? dq.ecs.getBits() : dq.ecsPrefixLength); + generateECSOption(dq.ecsSet ? dq.ecs.getNetwork() : *dq.remote, newECSOption, dq.ecsSet ? dq.ecs.getBits() : dq.ecsPrefixLength); char* packet = reinterpret_cast(dq.dh); - return handleEDNSClientSubnet(packet, dq.size, dq.consumed, &dq.len, ednsAdded, ecsAdded, dq.ecsOverride, newECSOption); + return handleEDNSClientSubnet(packet, dq.size, dq.consumed, &dq.len, ednsAdded, ecsAdded, dq.ecsOverride, newECSOption, preserveTrailingData); } static int removeEDNSOptionFromOptions(unsigned char* optionsStart, const uint16_t optionsLen, const uint16_t optionCodeToRemove, uint16_t* newOptionsLen) diff --git a/pdns/dnsdist-ecs.hh b/pdns/dnsdist-ecs.hh index 5b664407b..40bf974bd 100644 --- a/pdns/dnsdist-ecs.hh +++ b/pdns/dnsdist-ecs.hh @@ -35,8 +35,8 @@ bool isEDNSOptionInOpt(const std::string& packet, const size_t optStart, const s bool addEDNS(dnsheader* dh, uint16_t& len, const size_t size, bool dnssecOK, uint16_t payloadSize); bool addEDNSToQueryTurnedResponse(DNSQuestion& dq); -bool handleEDNSClientSubnet(DNSQuestion& dq, bool* ednsAdded, bool* ecsAdded); -bool handleEDNSClientSubnet(char* const packet, const size_t packetSize, const unsigned int consumed, uint16_t* const len, bool* const ednsAdded, bool* const ecsAdded, bool overrideExisting, const string& newECSOption); +bool handleEDNSClientSubnet(DNSQuestion& dq, bool* ednsAdded, bool* ecsAdded, bool preserveTrailingData); +bool handleEDNSClientSubnet(char* const packet, const size_t packetSize, const unsigned int consumed, uint16_t* const len, bool* const ednsAdded, bool* const ecsAdded, bool overrideExisting, const string& newECSOption, bool preserveTrailingData); bool parseEDNSOptions(DNSQuestion& dq); diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index 6e9233634..5cb2dc3e8 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -176,7 +176,7 @@ DNSAction::Action TeeAction::operator()(DNSQuestion* dq, string* ruleresult) con string newECSOption; generateECSOption(dq->ecsSet ? dq->ecs.getNetwork() : *dq->remote, newECSOption, dq->ecsSet ? dq->ecs.getBits() : dq->ecsPrefixLength); - if (!handleEDNSClientSubnet(const_cast(query.c_str()), query.capacity(), dq->qname->wirelength(), &len, &ednsAdded, &ecsAdded, dq->ecsOverride, newECSOption)) { + if (!handleEDNSClientSubnet(const_cast(query.c_str()), query.capacity(), dq->qname->wirelength(), &len, &ednsAdded, &ecsAdded, dq->ecsOverride, newECSOption, g_preserveTrailingData)) { return DNSAction::Action::None; } diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 2bc27b1e6..1f468ebd2 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -913,6 +913,8 @@ void setupLuaConfig(bool client) g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; }); + g_lua.writeFunction("setPreserveTrailingData", [](bool preserve) { g_preserveTrailingData = preserve; }); + g_lua.writeFunction("showDynBlocks", []() { setLuaNoSideEffect(); auto slow = g_dynblockNMG.getCopy(); diff --git a/pdns/dnsdist-tcp.cc b/pdns/dnsdist-tcp.cc index 656f8aad4..98b6e7837 100644 --- a/pdns/dnsdist-tcp.cc +++ b/pdns/dnsdist-tcp.cc @@ -22,6 +22,7 @@ #include "dnsdist.hh" #include "dnsdist-ecs.hh" #include "dnsdist-rings.hh" +#include "dnsdist-xpf.hh" #include "dnsparser.hh" #include "ednsoptions.hh" @@ -433,7 +434,7 @@ void* tcpClientThread(int pipefd) } if (dq.useECS && ((ds && ds->useECS) || (!ds && serverPool->getECS()))) { - if (!handleEDNSClientSubnet(dq, &(ednsAdded), &(ecsAdded))) { + if (!handleEDNSClientSubnet(dq, &(ednsAdded), &(ecsAdded), g_preserveTrailingData)) { vinfolog("Dropping query from %s because we couldn't insert the ECS value", ci.remote.toStringWithPort()); goto drop; } @@ -501,7 +502,7 @@ void* tcpClientThread(int pipefd) } if (dq.addXPF && ds->xpfRRCode != 0) { - addXPF(dq, ds->xpfRRCode); + addXPF(dq, ds->xpfRRCode, g_preserveTrailingData); } int dsock = -1; diff --git a/pdns/dnsdist-xpf.cc b/pdns/dnsdist-xpf.cc new file mode 100644 index 000000000..c828aadfb --- /dev/null +++ b/pdns/dnsdist-xpf.cc @@ -0,0 +1,67 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "dnsdist-xpf.hh" + +#include "dnsparser.hh" +#include "xpf.hh" + +bool addXPF(DNSQuestion& dq, uint16_t optionCode, bool preserveTrailingData) +{ + std::string payload = generateXPFPayload(dq.tcp, *dq.remote, *dq.local); + uint8_t root = '\0'; + dnsrecordheader drh; + drh.d_type = htons(optionCode); + drh.d_class = htons(QClass::IN); + drh.d_ttl = 0; + drh.d_clen = htons(payload.size()); + size_t recordHeaderLen = sizeof(root) + sizeof(drh); + + size_t available = dq.size - dq.len; + + if ((payload.size() + recordHeaderLen) > available) { + return false; + } + + size_t xpfSize = sizeof(root) + sizeof(drh) + payload.size(); + uint32_t realPacketLen = getDNSPacketLength(reinterpret_cast(dq.dh), dq.len); + if (realPacketLen < dq.len && preserveTrailingData) { + size_t toMove = dq.len - realPacketLen; + memmove(reinterpret_cast(dq.dh) + realPacketLen + xpfSize, reinterpret_cast(dq.dh) + realPacketLen, toMove); + dq.len += xpfSize; + } + else { + dq.len = realPacketLen + xpfSize; + } + + size_t pos = realPacketLen; + memcpy(reinterpret_cast(dq.dh) + pos, &root, sizeof(root)); + pos += sizeof(root); + memcpy(reinterpret_cast(dq.dh) + pos, &drh, sizeof(drh)); + pos += sizeof(drh); + memcpy(reinterpret_cast(dq.dh) + pos, payload.data(), payload.size()); + pos += payload.size(); + + dq.dh->arcount = htons(ntohs(dq.dh->arcount) + 1); + + return true; +} diff --git a/pdns/dnsdist-xpf.hh b/pdns/dnsdist-xpf.hh new file mode 100644 index 000000000..5a1b41114 --- /dev/null +++ b/pdns/dnsdist-xpf.hh @@ -0,0 +1,27 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once + +#include "dnsdist.hh" + +bool addXPF(DNSQuestion& dq, uint16_t optionCode, bool preserveTrailingData); + diff --git a/pdns/dnsdist.cc b/pdns/dnsdist.cc index 488056870..ecd402b82 100644 --- a/pdns/dnsdist.cc +++ b/pdns/dnsdist.cc @@ -48,6 +48,7 @@ #include "dnsdist-lua.hh" #include "dnsdist-rings.hh" #include "dnsdist-secpoll.hh" +#include "dnsdist-xpf.hh" #include "base64.hh" #include "delaypipe.hh" @@ -62,7 +63,6 @@ #include "sodcrypto.hh" #include "sstuff.hh" #include "threadname.hh" -#include "xpf.hh" thread_local boost::uuids::random_generator t_uuidGenerator; @@ -143,7 +143,8 @@ int g_udpTimeout{2}; bool g_servFailOnNoPolicy{false}; bool g_truncateTC{false}; -bool g_fixupCase{0}; +bool g_fixupCase{false}; +bool g_preserveTrailingData{false}; static void truncateTC(char* packet, uint16_t* len, size_t responseSize, unsigned int consumed) try @@ -1197,38 +1198,6 @@ static ssize_t udpClientSendRequestToBackend(DownstreamState* ss, const int sd, return result; } -bool addXPF(DNSQuestion& dq, uint16_t optionCode) -{ - std::string payload = generateXPFPayload(dq.tcp, *dq.remote, *dq.local); - uint8_t root = '\0'; - dnsrecordheader drh; - drh.d_type = htons(optionCode); - drh.d_class = htons(QClass::IN); - drh.d_ttl = 0; - drh.d_clen = htons(payload.size()); - size_t recordHeaderLen = sizeof(root) + sizeof(drh); - - size_t available = dq.size - dq.len; - - if ((payload.size() + recordHeaderLen) > available) { - return false; - } - - size_t pos = dq.len; - memcpy(reinterpret_cast(dq.dh) + pos, &root, sizeof(root)); - pos += sizeof(root); - memcpy(reinterpret_cast(dq.dh) + pos, &drh, sizeof(drh)); - pos += sizeof(drh); - memcpy(reinterpret_cast(dq.dh) + pos, payload.data(), payload.size()); - pos += payload.size(); - - dq.len = pos; - - dq.dh->arcount = htons(ntohs(dq.dh->arcount) + 1); - - return true; -} - static bool isUDPQueryAcceptable(ClientState& cs, LocalHolders& holders, const struct msghdr* msgh, const ComboAddress& remote, ComboAddress& dest) { if (msgh->msg_flags & MSG_TRUNC) { @@ -1422,7 +1391,7 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct bool ednsAdded = false; bool ecsAdded = false; if (dq.useECS && ((ss && ss->useECS) || (!ss && serverPool->getECS()))) { - if (!handleEDNSClientSubnet(dq, &(ednsAdded), &(ecsAdded))) { + if (!handleEDNSClientSubnet(dq, &(ednsAdded), &(ecsAdded), g_preserveTrailingData)) { vinfolog("Dropping query from %s because we couldn't insert the ECS value", remote.toStringWithPort()); return; } @@ -1514,7 +1483,7 @@ static void processUDPQuery(ClientState& cs, LocalHolders& holders, const struct } if (dq.addXPF && ss->xpfRRCode != 0) { - addXPF(dq, ss->xpfRRCode); + addXPF(dq, ss->xpfRRCode, g_preserveTrailingData); } ss->queries++; diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 566a2c245..8cb7c062c 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -948,6 +948,7 @@ extern uint32_t g_hashperturb; extern bool g_useTCPSinglePipe; extern std::atomic g_downstreamTCPCleanupInterval; extern size_t g_udpVectorSize; +extern bool g_preserveTrailingData; #ifdef HAVE_EBPF extern shared_ptr g_defaultBPFFilter; diff --git a/pdns/dnsdistdist/Makefile.am b/pdns/dnsdistdist/Makefile.am index f2d79be91..92c8632ce 100644 --- a/pdns/dnsdistdist/Makefile.am +++ b/pdns/dnsdistdist/Makefile.am @@ -111,6 +111,7 @@ dnsdist_SOURCES = \ dnsdist-snmp.cc dnsdist-snmp.hh \ dnsdist-tcp.cc \ dnsdist-web.cc \ + dnsdist-xpf.cc dnsdist-xpf.hh \ dnslabeltext.cc \ dnsname.cc dnsname.hh \ dnsparser.hh dnsparser.cc \ @@ -231,6 +232,7 @@ testrunner_SOURCES = \ dnsdist.hh \ dnsdist-cache.cc dnsdist-cache.hh \ dnsdist-ecs.cc dnsdist-ecs.hh \ + dnsdist-xpf.cc dnsdist-xpf.hh \ dnscrypt.cc dnscrypt.hh \ dnslabeltext.cc \ dnsname.cc dnsname.hh \ diff --git a/pdns/dnsdistdist/dnsdist-xpf.cc b/pdns/dnsdistdist/dnsdist-xpf.cc new file mode 120000 index 000000000..66fd88d61 --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-xpf.cc @@ -0,0 +1 @@ +../dnsdist-xpf.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/dnsdist-xpf.hh b/pdns/dnsdistdist/dnsdist-xpf.hh new file mode 120000 index 000000000..c2b75e2df --- /dev/null +++ b/pdns/dnsdistdist/dnsdist-xpf.hh @@ -0,0 +1 @@ +../dnsdist-xpf.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/docs/advanced/ecs.rst b/pdns/dnsdistdist/docs/advanced/ecs.rst index b8ce4b2d0..643f467a0 100644 --- a/pdns/dnsdistdist/docs/advanced/ecs.rst +++ b/pdns/dnsdistdist/docs/advanced/ecs.rst @@ -14,3 +14,5 @@ In addition to the global settings, rules and Lua bindings can alter this behavi * calling :func:`ECSPrefixLengthAction(v4, v6)` or setting ``dq.ecsPrefixLength`` will override the global :func:`setECSSourcePrefixV4()` and :func:`setECSSourcePrefixV6()` values. In effect this means that for the EDNS Client Subnet option to be added to the request, ``useClientSubnet`` should be set to ``true`` for the backend used (default to ``false``) and ECS should not have been disabled by calling :func:`DisableECSAction` or setting ``dq.useECS`` to ``false`` (default to true). + +Note that any trailing data present in the incoming query is removed by default when an OPT (or XPF) record has to be inserted. This behaviour can be modified using :func:`setPreserveTrailingData()`. diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc index e88881113..4103df84f 100644 --- a/pdns/dnsparser.cc +++ b/pdns/dnsparser.cc @@ -811,7 +811,7 @@ uint32_t getDNSPacketLength(const char* packet, size_t length) } try { - const dnsheader* dh = (const dnsheader*) packet; + const dnsheader* dh = reinterpret_cast(packet); DNSPacketMangler dpm(const_cast(packet), length); const uint16_t qdcount = ntohs(dh->qdcount); diff --git a/pdns/test-dnsdist_cc.cc b/pdns/test-dnsdist_cc.cc index 9996b6edf..c579f40c1 100644 --- a/pdns/test-dnsdist_cc.cc +++ b/pdns/test-dnsdist_cc.cc @@ -23,9 +23,12 @@ #define BOOST_TEST_NO_MAIN #include +#include #include "dnsdist.hh" #include "dnsdist-ecs.hh" +#include "dnsdist-xpf.hh" + #include "dolog.hh" #include "dnsname.hh" #include "dnsparser.hh" @@ -33,17 +36,13 @@ #include "ednsoptions.hh" #include "ednscookies.hh" #include "ednssubnet.hh" -#include BOOST_AUTO_TEST_SUITE(test_dnsdist_cc) -bool g_syslog{true}; -bool g_verbose{true}; - static const uint16_t ECSSourcePrefixV4 = 24; static const uint16_t ECSSourcePrefixV6 = 56; -static void validateQuery(const char * packet, size_t packetSize, bool hasEdns=true) +static void validateQuery(const char * packet, size_t packetSize, bool hasEdns=true, bool hasXPF=false) { MOADNSParser mdp(true, packet, packetSize); @@ -52,7 +51,8 @@ static void validateQuery(const char * packet, size_t packetSize, bool hasEdns=t BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1); BOOST_CHECK_EQUAL(mdp.d_header.ancount, 0); BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0); - BOOST_CHECK_EQUAL(mdp.d_header.arcount, (hasEdns ? 1 : 0)); + uint16_t expectedARCount = 0 + (hasEdns ? 1 : 0) + (hasXPF ? 1 : 0); + BOOST_CHECK_EQUAL(mdp.d_header.arcount, expectedARCount); } static void validateECS(const char* packet, size_t packetSize, const ComboAddress& expected) @@ -89,6 +89,124 @@ static void validateResponse(const char * packet, size_t packetSize, bool hasEdn BOOST_CHECK_EQUAL(mdp.d_header.arcount, (hasEdns ? 1 : 0) + additionalCount); } +BOOST_AUTO_TEST_CASE(test_addXPF) +{ + static const uint16_t xpfOptionCode = 65422; + + struct timespec queryTime; + gettime(&queryTime); // does not have to be accurate ("realTime") in tests + ComboAddress remote; + DNSName name("www.powerdns.com."); + + vector query; + DNSPacketWriter pw(query, name, QType::A, QClass::IN, 0); + pw.getHeader()->rd = 1; + const uint16_t len = query.size(); + vector queryWithXPF; + + { + char packet[1500]; + memcpy(packet, query.data(), query.size()); + + /* large enough packet */ + unsigned int consumed = 0; + uint16_t qtype; + DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed); + BOOST_CHECK_EQUAL(qname, name); + BOOST_CHECK(qtype == QType::A); + + auto dh = reinterpret_cast(packet); + DNSQuestion dq(&qname, qtype, QClass::IN, qname.wirelength(), &remote, &remote, dh, sizeof(packet), query.size(), false, &queryTime); + + BOOST_CHECK(addXPF(dq, xpfOptionCode, false)); + BOOST_CHECK(static_cast(dq.len) > query.size()); + validateQuery(packet, dq.len, false, true); + queryWithXPF.resize(dq.len); + memcpy(queryWithXPF.data(), packet, dq.len); + } + + { + char packet[1500]; + memcpy(packet, query.data(), query.size()); + + /* not large enough packet */ + unsigned int consumed = 0; + uint16_t qtype; + DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed); + BOOST_CHECK_EQUAL(qname, name); + BOOST_CHECK(qtype == QType::A); + + auto dh = reinterpret_cast(packet); + DNSQuestion dq(&qname, qtype, QClass::IN, qname.wirelength(), &remote, &remote, dh, sizeof(packet), query.size(), false, &queryTime); + dq.size = dq.len; + + BOOST_CHECK(!addXPF(dq, xpfOptionCode, false)); + BOOST_CHECK_EQUAL(static_cast(dq.len), query.size()); + validateQuery(packet, dq.len, false, false); + } + + { + char packet[1500]; + memcpy(packet, query.data(), query.size()); + + /* packet with trailing data (overriding it) */ + unsigned int consumed = 0; + uint16_t qtype; + DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed); + BOOST_CHECK_EQUAL(qname, name); + BOOST_CHECK(qtype == QType::A); + + auto dh = reinterpret_cast(packet); + DNSQuestion dq(&qname, qtype, QClass::IN, qname.wirelength(), &remote, &remote, dh, sizeof(packet), query.size(), false, &queryTime); + + /* add trailing data */ + const size_t trailingDataSize = 10; + /* Making sure we have enough room to allow for fake trailing data */ + BOOST_REQUIRE(sizeof(packet) > dq.len && (sizeof(packet) - dq.len) > trailingDataSize); + for (size_t idx = 0; idx < trailingDataSize; idx++) { + packet[dq.len + idx] = 'A'; + } + dq.len += trailingDataSize; + + BOOST_CHECK(addXPF(dq, xpfOptionCode, false)); + BOOST_CHECK_EQUAL(static_cast(dq.len), queryWithXPF.size()); + BOOST_CHECK_EQUAL(memcmp(queryWithXPF.data(), packet, queryWithXPF.size()), 0); + validateQuery(packet, dq.len, false, true); + } + + { + char packet[1500]; + memcpy(packet, query.data(), query.size()); + + /* packet with trailing data (preserving trailing data) */ + unsigned int consumed = 0; + uint16_t qtype; + DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed); + BOOST_CHECK_EQUAL(qname, name); + BOOST_CHECK(qtype == QType::A); + + auto dh = reinterpret_cast(packet); + DNSQuestion dq(&qname, qtype, QClass::IN, qname.wirelength(), &remote, &remote, dh, sizeof(packet), query.size(), false, &queryTime); + + /* add trailing data */ + const size_t trailingDataSize = 10; + /* Making sure we have enough room to allow for fake trailing data */ + BOOST_REQUIRE(sizeof(packet) > dq.len && (sizeof(packet) - dq.len) > trailingDataSize); + for (size_t idx = 0; idx < trailingDataSize; idx++) { + packet[dq.len + idx] = 'A'; + } + dq.len += trailingDataSize; + + BOOST_CHECK(addXPF(dq, xpfOptionCode, true)); + BOOST_CHECK(static_cast(dq.len) > queryWithXPF.size()); + BOOST_CHECK_EQUAL(memcmp(queryWithXPF.data(), packet, queryWithXPF.size()), 0); + for (size_t idx = 0; idx < trailingDataSize; idx++) { + BOOST_CHECK_EQUAL(packet[queryWithXPF.size() + idx], 'A'); + } + validateQuery(packet, dq.len, false, true); + } +} + BOOST_AUTO_TEST_CASE(addECSWithoutEDNS) { bool ednsAdded = false; @@ -109,31 +227,84 @@ BOOST_AUTO_TEST_CASE(addECSWithoutEDNS) unsigned int consumed = 0; uint16_t qtype; - DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, NULL, &consumed); + DNSName qname(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed); BOOST_CHECK_EQUAL(qname, name); BOOST_CHECK(qtype == QType::A); - BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption)); - BOOST_CHECK((size_t) len > query.size()); + BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false)); + BOOST_CHECK(static_cast(len) > query.size()); BOOST_CHECK_EQUAL(ednsAdded, true); BOOST_CHECK_EQUAL(ecsAdded, false); validateQuery(packet, len); validateECS(packet, len, remote); + vector queryWithEDNS; + queryWithEDNS.resize(len); + memcpy(queryWithEDNS.data(), packet, len); /* not large enough packet */ ednsAdded = false; ecsAdded = false; consumed = 0; len = query.size(); - qname = DNSName(reinterpret_cast(query.data()), len, sizeof(dnsheader), false, &qtype, NULL, &consumed); + qname = DNSName(reinterpret_cast(query.data()), len, sizeof(dnsheader), false, &qtype, nullptr, &consumed); BOOST_CHECK_EQUAL(qname, name); BOOST_CHECK(qtype == QType::A); - BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption)); - BOOST_CHECK_EQUAL((size_t) len, query.size()); + BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false)); + BOOST_CHECK_EQUAL(static_cast(len), query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); validateQuery(reinterpret_cast(query.data()), len, false); + + /* packet with trailing data (overriding it) */ + memcpy(packet, query.data(), query.size()); + ednsAdded = false; + ecsAdded = false; + consumed = 0; + len = query.size(); + qname = DNSName(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed); + BOOST_CHECK_EQUAL(qname, name); + BOOST_CHECK(qtype == QType::A); + /* add trailing data */ + const size_t trailingDataSize = 10; + /* Making sure we have enough room to allow for fake trailing data */ + BOOST_REQUIRE(sizeof(packet) > len && (sizeof(packet) - len) > trailingDataSize); + for (size_t idx = 0; idx < trailingDataSize; idx++) { + packet[len + idx] = 'A'; + } + len += trailingDataSize; + BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false)); + BOOST_REQUIRE_EQUAL(static_cast(len), queryWithEDNS.size()); + BOOST_CHECK_EQUAL(memcmp(queryWithEDNS.data(), packet, queryWithEDNS.size()), 0); + BOOST_CHECK_EQUAL(ednsAdded, true); + BOOST_CHECK_EQUAL(ecsAdded, false); + validateQuery(packet, len); + + /* packet with trailing data (preserving trailing data) */ + memcpy(packet, query.data(), query.size()); + ednsAdded = false; + ecsAdded = false; + consumed = 0; + len = query.size(); + qname = DNSName(packet, len, sizeof(dnsheader), false, &qtype, nullptr, &consumed); + BOOST_CHECK_EQUAL(qname, name); + BOOST_CHECK(qtype == QType::A); + /* add trailing data */ + /* Making sure we have enough room to allow for fake trailing data */ + BOOST_REQUIRE(sizeof(packet) > len && (sizeof(packet) - len) > trailingDataSize); + for (size_t idx = 0; idx < trailingDataSize; idx++) { + packet[len + idx] = 'A'; + } + len += trailingDataSize; + BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, true)); + BOOST_REQUIRE_EQUAL(static_cast(len), queryWithEDNS.size() + trailingDataSize); + BOOST_CHECK_EQUAL(memcmp(queryWithEDNS.data(), packet, queryWithEDNS.size()), 0); + for (size_t idx = 0; idx < trailingDataSize; idx++) { + BOOST_CHECK_EQUAL(packet[queryWithEDNS.size() + idx], 'A'); + } + BOOST_CHECK_EQUAL(ednsAdded, true); + BOOST_CHECK_EQUAL(ecsAdded, false); + validateQuery(packet, len); } BOOST_AUTO_TEST_CASE(addECSWithoutEDNSAlreadyParsed) @@ -164,7 +335,7 @@ BOOST_AUTO_TEST_CASE(addECSWithoutEDNSAlreadyParsed) BOOST_CHECK(!parseEDNSOptions(dq)); /* And now we add our own ECS */ - BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded)); + BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded, false)); BOOST_CHECK_GT(static_cast(dq.len), query.size()); BOOST_CHECK_EQUAL(ednsAdded, true); BOOST_CHECK_EQUAL(ecsAdded, false); @@ -181,7 +352,7 @@ BOOST_AUTO_TEST_CASE(addECSWithoutEDNSAlreadyParsed) BOOST_CHECK(qclass == QClass::IN); DNSQuestion dq2(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast(query.data()), query.size(), query.size(), false, nullptr); - BOOST_CHECK(!handleEDNSClientSubnet(dq2, &ednsAdded, &ecsAdded)); + BOOST_CHECK(!handleEDNSClientSubnet(dq2, &ednsAdded, &ecsAdded, false)); BOOST_CHECK_EQUAL(static_cast(dq2.len), query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); @@ -213,7 +384,7 @@ BOOST_AUTO_TEST_CASE(addECSWithEDNSNoECS) { BOOST_CHECK_EQUAL(qname, name); BOOST_CHECK(qtype == QType::A); - BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption)); + BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false)); BOOST_CHECK((size_t) len > query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, true); @@ -229,7 +400,7 @@ BOOST_AUTO_TEST_CASE(addECSWithEDNSNoECS) { BOOST_CHECK_EQUAL(qname, name); BOOST_CHECK(qtype == QType::A); - BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption)); + BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, false, newECSOption, false)); BOOST_CHECK_EQUAL((size_t) len, query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); @@ -265,7 +436,7 @@ BOOST_AUTO_TEST_CASE(addECSWithEDNSNoECSAlreadyParsed) { BOOST_CHECK(parseEDNSOptions(dq)); /* And now we add our own ECS */ - BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded)); + BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded, false)); BOOST_CHECK_GT(static_cast(dq.len), query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, true); @@ -282,7 +453,7 @@ BOOST_AUTO_TEST_CASE(addECSWithEDNSNoECSAlreadyParsed) { BOOST_CHECK(qclass == QClass::IN); DNSQuestion dq2(&qname, qtype, qclass, consumed, nullptr, &remote, reinterpret_cast(query.data()), query.size(), query.size(), false, nullptr); - BOOST_CHECK(!handleEDNSClientSubnet(dq2, &ednsAdded, &ecsAdded)); + BOOST_CHECK(!handleEDNSClientSubnet(dq2, &ednsAdded, &ecsAdded, false)); BOOST_CHECK_EQUAL(static_cast(dq2.len), query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); @@ -320,7 +491,7 @@ BOOST_AUTO_TEST_CASE(replaceECSWithSameSize) { BOOST_CHECK_EQUAL(qname, name); BOOST_CHECK(qtype == QType::A); - BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption)); + BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption, false)); BOOST_CHECK_EQUAL((size_t) len, query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); @@ -365,7 +536,7 @@ BOOST_AUTO_TEST_CASE(replaceECSWithSameSizeAlreadyParsed) { BOOST_CHECK(parseEDNSOptions(dq)); /* And now we add our own ECS */ - BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded)); + BOOST_CHECK(handleEDNSClientSubnet(dq, &ednsAdded, &ecsAdded, false)); BOOST_CHECK_EQUAL(static_cast(dq.len), query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); @@ -404,7 +575,7 @@ BOOST_AUTO_TEST_CASE(replaceECSWithSmaller) { BOOST_CHECK_EQUAL(qname, name); BOOST_CHECK(qtype == QType::A); - BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption)); + BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption, false)); BOOST_CHECK((size_t) len < query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); @@ -443,7 +614,7 @@ BOOST_AUTO_TEST_CASE(replaceECSWithLarger) { BOOST_CHECK_EQUAL(qname, name); BOOST_CHECK(qtype == QType::A); - BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption)); + BOOST_CHECK(handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption, false)); BOOST_CHECK((size_t) len > query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); @@ -459,7 +630,7 @@ BOOST_AUTO_TEST_CASE(replaceECSWithLarger) { BOOST_CHECK_EQUAL(qname, name); BOOST_CHECK(qtype == QType::A); - BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption)); + BOOST_CHECK(!handleEDNSClientSubnet(reinterpret_cast(query.data()), query.size(), consumed, &len, &ednsAdded, &ecsAdded, true, newECSOption, false)); BOOST_CHECK_EQUAL((size_t) len, query.size()); BOOST_CHECK_EQUAL(ednsAdded, false); BOOST_CHECK_EQUAL(ecsAdded, false); diff --git a/pdns/test-dnsparser_cc.cc b/pdns/test-dnsparser_cc.cc index e9f248cb6..4a2f6ddfd 100644 --- a/pdns/test-dnsparser_cc.cc +++ b/pdns/test-dnsparser_cc.cc @@ -417,6 +417,32 @@ BOOST_AUTO_TEST_CASE(test_getDNSPacketLength) { BOOST_CHECK_EQUAL(result, realSize); } + { + /* truncated packet, should return the full size */ + vector packet; + DNSPacketWriter pwR(packet, name, QType::A, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.commit(); + + pwR.startRecord(name, QType::A, 255, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrIP(v4.sin4.sin_addr.s_addr); + pwR.commit(); + + pwR.startRecord(name, QType::SOA, 257, QClass::IN, DNSResourceRecord::AUTHORITY); + pwR.commit(); + + pwR.startRecord(name, QType::A, 256, QClass::IN, DNSResourceRecord::ADDITIONAL); + pwR.xfrIP(v4.sin4.sin_addr.s_addr); + pwR.commit(); + + pwR.addOpt(4096, 0, 0); + pwR.commit(); + + size_t fakeSize = packet.size()-1; + auto result = getDNSPacketLength(reinterpret_cast(packet.data()), fakeSize); + BOOST_CHECK_EQUAL(result, fakeSize); + } + } BOOST_AUTO_TEST_CASE(test_getRecordsOfTypeCount) { -- 2.40.0