pr.xfrBlob(blob);
pw.xfrBlob(blob);
}
-
/* consume AR, looking for OPT */
for (idx = 0; idx < arcount; idx++) {
rrname = pr.getName();
return 0;
}
-int locateEDNSOptRR(const char * packet, const size_t len, const char ** optStart, size_t * optLen, bool * last)
+int locateEDNSOptRR(char * packet, const size_t len, char ** optStart, size_t * optLen, bool * last)
{
assert(packet != NULL);
assert(optStart != NULL);
}
}
-void handleEDNSClientSubnet(char * const packet, const size_t packetSize, const unsigned int consumed, uint16_t * const len, string& largerPacket, bool * const ednsAdded, const ComboAddress& remote)
+void handleEDNSClientSubnet(char* const packet, const size_t packetSize, const unsigned int consumed, uint16_t* const len, string& largerPacket, bool* const ednsAdded, bool* const ecsAdded, const ComboAddress& remote)
{
assert(packet != NULL);
assert(len != NULL);
assert(consumed <= (size_t) *len);
assert(ednsAdded != NULL);
+ assert(ecsAdded != NULL);
unsigned char * optRDLen = NULL;
size_t remaining = 0;
largerPacket.append(packet, *len);
largerPacket.append(ECSOption);
}
+ *ecsAdded = true;
}
}
else {
}
}
}
+
+static int removeEDNSOptionFromOptions(unsigned char* optionsStart, const uint16_t optionsLen, const uint16_t optionCodeToRemove, uint16_t* newOptionsLen)
+{
+ unsigned char* p = optionsStart;
+ const unsigned char* end = p + optionsLen;
+ while ((p + 4) <= end) {
+ unsigned char* optionBegin = p;
+ const uint16_t optionCode = 0x100*p[0] + p[1];
+ p += sizeof(optionCode);
+ const uint16_t optionLen = 0x100*p[0] + p[1];
+ p += sizeof(optionLen);
+ if ((p + optionLen) > end) {
+ return EINVAL;
+ }
+ if (optionCode == optionCodeToRemove) {
+ if (p + optionLen < end) {
+ /* move remaining options over the removed one,
+ if any */
+ memmove(optionBegin, p + optionLen, end - (p + optionLen));
+ }
+ *newOptionsLen = optionsLen - (sizeof(optionCode) + sizeof(optionLen) + optionLen);
+ return 0;
+ }
+ p += optionLen;
+ }
+ return ENOENT;
+}
+
+int removeEDNSOptionFromOPT(char* optStart, size_t* optLen, const uint16_t optionCodeToRemove)
+{
+ /* we need at least:
+ root label (1), type (2), class (2), ttl (4) + rdlen (2)*/
+ if (*optLen < 11) {
+ return EINVAL;
+ }
+ const unsigned char* end = (const unsigned char*) optStart + *optLen;
+ unsigned char* p = (unsigned char*) optStart + 9;
+ unsigned char* rdLenPtr = p;
+ uint16_t rdLen = (0x100*p[0] + p[1]);
+ p += sizeof(rdLen);
+ if (p + rdLen != end) {
+ return EINVAL;
+ }
+ uint16_t newRdLen = 0;
+ int res = removeEDNSOptionFromOptions(p, rdLen, optionCodeToRemove, &newRdLen);
+ if (res != 0) {
+ return res;
+ }
+ *optLen -= (rdLen - newRdLen);
+ rdLenPtr[0] = newRdLen / 0x100;
+ rdLenPtr[1] = newRdLen % 0x100;
+ return 0;
+}
+
+int rewriteResponseWithoutEDNSOption(const char * packet, const size_t len, const uint16_t optionCodeToSkip, vector<uint8_t>& newContent)
+{
+ assert(packet != NULL);
+ assert(len >= sizeof(dnsheader));
+ const struct dnsheader* dh = (const struct dnsheader*) packet;
+
+ if (ntohs(dh->arcount) == 0)
+ return ENOENT;
+
+ if (ntohs(dh->qdcount) == 0)
+ return ENOENT;
+
+ vector<uint8_t> content(len - sizeof(dnsheader));
+ copy(packet + sizeof(dnsheader), packet + len, content.begin());
+ PacketReader pr(content);
+
+ size_t idx = 0;
+ DNSName rrname;
+ uint16_t qdcount = ntohs(dh->qdcount);
+ uint16_t ancount = ntohs(dh->ancount);
+ uint16_t nscount = ntohs(dh->nscount);
+ uint16_t arcount = ntohs(dh->arcount);
+ uint16_t rrtype;
+ uint16_t rrclass;
+ string blob;
+ struct dnsrecordheader ah;
+
+ rrname = pr.getName();
+ rrtype = pr.get16BitInt();
+ rrclass = pr.get16BitInt();
+
+ DNSPacketWriter pw(newContent, rrname, rrtype, rrclass, dh->opcode);
+ pw.getHeader()->id=dh->id;
+ pw.getHeader()->qr=dh->qr;
+ pw.getHeader()->aa=dh->aa;
+ pw.getHeader()->tc=dh->tc;
+ pw.getHeader()->rd=dh->rd;
+ pw.getHeader()->ra=dh->ra;
+ pw.getHeader()->ad=dh->ad;
+ pw.getHeader()->cd=dh->cd;
+ pw.getHeader()->rcode=dh->rcode;
+
+ /* consume remaining qd if any */
+ if (qdcount > 1) {
+ for(idx = 1; idx < qdcount; idx++) {
+ rrname = pr.getName();
+ rrtype = pr.get16BitInt();
+ rrclass = pr.get16BitInt();
+ (void) rrtype;
+ (void) rrclass;
+ }
+ }
+
+ /* copy AN and NS */
+ for (idx = 0; idx < ancount; idx++) {
+ rrname = pr.getName();
+ pr.getDnsrecordheader(ah);
+
+ pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ANSWER, true);
+ pr.xfrBlob(blob);
+ pw.xfrBlob(blob);
+ }
+
+ for (idx = 0; idx < nscount; idx++) {
+ rrname = pr.getName();
+ pr.getDnsrecordheader(ah);
+
+ pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::AUTHORITY, true);
+ pr.xfrBlob(blob);
+ pw.xfrBlob(blob);
+ }
+
+ /* consume AR, looking for OPT */
+ for (idx = 0; idx < arcount; idx++) {
+ rrname = pr.getName();
+ pr.getDnsrecordheader(ah);
+
+ if (ah.d_type != QType::OPT) {
+ pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true);
+ pr.xfrBlob(blob);
+ pw.xfrBlob(blob);
+ } else {
+ pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, false);
+ pr.xfrBlob(blob);
+ uint16_t rdLen = blob.length();
+ removeEDNSOptionFromOptions((unsigned char*)blob.c_str(), rdLen, optionCodeToSkip, &rdLen);
+ /* xfrBlob(string, size) completely ignores size.. */
+ if (rdLen > 0) {
+ blob.resize((size_t)rdLen);
+ pw.xfrBlob(blob);
+ } else {
+ pw.commit();
+ }
+ }
+ }
+ pw.commit();
+
+ return 0;
+}
#pragma once
int rewriteResponseWithoutEDNS(const char * packet, size_t len, vector<uint8_t>& newContent);
-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);
+int locateEDNSOptRR(char * packet, size_t len, char ** optStart, size_t * optLen, bool * last);
+void handleEDNSClientSubnet(char * packet, size_t packetSize, unsigned int consumed, uint16_t * len, string& largerPacket, bool* ednsAdded, bool* ecsAdded, const ComboAddress& remote);
void generateOptRR(const std::string& optRData, string& res);
+int removeEDNSOptionFromOPT(char* optStart, size_t* optLen, const uint16_t optionCodeToRemove);
+int rewriteResponseWithoutEDNSOption(const char * packet, const size_t len, const uint16_t optionCodeToSkip, vector<uint8_t>& newContent);
#include "dnsdist.hh"
#include "dnsdist-ecs.hh"
+#include "ednsoptions.hh"
#include "dolog.hh"
#include "lock.hh"
#include <thread>
string poolname;
string largerQuery;
vector<uint8_t> rewrittenResponse;
- bool ednsAdded = false;
shared_ptr<DownstreamState> ds;
if (!setNonBlocking(ci.fd))
goto drop;
break;
}
+ bool ednsAdded = false;
+ bool ecsAdded = false;
/* if the query is small, allocate a bit more
memory to be able to spoof the content,
or to add ECS without allocating a new buffer */
if (ds && ds->useECS) {
uint16_t newLen = dq.len;
- handleEDNSClientSubnet(queryBuffer, dq.size, consumed, &newLen, largerQuery, &ednsAdded, ci.remote);
+ handleEDNSClientSubnet(queryBuffer, dq.size, consumed, &newLen, largerQuery, &ednsAdded, &ecsAdded, ci.remote);
if (largerQuery.empty() == false) {
query = largerQuery.c_str();
dq.len = largerQuery.size();
break;
}
- if (!fixUpResponse(&response, &responseLen, &responseSize, qname, origFlags, ednsAdded, rewrittenResponse, addRoom)) {
+ if (!fixUpResponse(&response, &responseLen, &responseSize, qname, origFlags, ednsAdded, ecsAdded, rewrittenResponse, addRoom)) {
break;
}
*flags |= origFlags;
}
-bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize, const DNSName& qname, uint16_t origFlags, bool ednsAdded, std::vector<uint8_t>& rewrittenResponse, uint16_t addRoom)
+bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize, const DNSName& qname, uint16_t origFlags, bool ednsAdded, bool ecsAdded, std::vector<uint8_t>& rewrittenResponse, uint16_t addRoom)
{
struct dnsheader* dh = (struct dnsheader*) *response;
restoreFlags(dh, origFlags);
- if (ednsAdded) {
- const char * optStart = NULL;
+ if (ednsAdded || ecsAdded) {
+ char * optStart = NULL;
size_t optLen = 0;
bool last = false;
int res = locateEDNSOptRR(*response, *responseLen, &optStart, &optLen, &last);
if (res == 0) {
- if (last) {
- /* simply remove the last AR */
- *responseLen -= optLen;
- uint16_t arcount = ntohs(dh->arcount);
- arcount--;
- dh->arcount = htons(arcount);
+ if (ednsAdded) {
+ /* we added the entire OPT RR,
+ therefore we need to remove it entirely */
+ if (last) {
+ /* simply remove the last AR */
+ *responseLen -= optLen;
+ uint16_t arcount = ntohs(dh->arcount);
+ arcount--;
+ dh->arcount = htons(arcount);
+ }
+ else {
+ /* Removing an intermediary RR could lead to compression error */
+ if (rewriteResponseWithoutEDNS(*response, *responseLen, rewrittenResponse) == 0) {
+ *responseLen = rewrittenResponse.size();
+ if (addRoom && (UINT16_MAX - *responseLen) > addRoom) {
+ rewrittenResponse.reserve(*responseLen + addRoom);
+ }
+ *responseSize = rewrittenResponse.capacity();
+ *response = reinterpret_cast<char*>(rewrittenResponse.data());
+ }
+ else {
+ warnlog("Error rewriting content");
+ }
+ }
}
else {
- /* Removing an intermediary RR could lead to compression error */
- if (rewriteResponseWithoutEDNS(*response, *responseLen, rewrittenResponse) == 0) {
- *responseLen = rewrittenResponse.size();
- if (addRoom && (UINT16_MAX - *responseLen) > addRoom) {
- rewrittenResponse.reserve(*responseLen + addRoom);
- }
- *responseSize = rewrittenResponse.capacity();
- *response = reinterpret_cast<char*>(rewrittenResponse.data());
+ /* the OPT RR was already present, but without ECS,
+ we need to remove the ECS option if any */
+ if (last) {
+ /* nothing after the OPT RR, we can simply remove the
+ ECS option */
+ size_t existingOptLen = optLen;
+ removeEDNSOptionFromOPT(optStart, &optLen, EDNSOptionCode::ECS);
+ *responseLen -= (existingOptLen - optLen);
}
else {
- warnlog("Error rewriting content");
+ /* Removing an intermediary RR could lead to compression error */
+ if (rewriteResponseWithoutEDNSOption(*response, *responseLen, EDNSOptionCode::ECS, rewrittenResponse) == 0) {
+ *responseLen = rewrittenResponse.size();
+ if (addRoom && (UINT16_MAX - *responseLen) > addRoom) {
+ rewrittenResponse.reserve(*responseLen + addRoom);
+ }
+ *responseSize = rewrittenResponse.capacity();
+ *response = reinterpret_cast<char*>(rewrittenResponse.data());
+ }
+ else {
+ warnlog("Error rewriting content");
+ }
}
}
}
addRoom = DNSCRYPT_MAX_RESPONSE_PADDING_AND_MAC_SIZE;
}
#endif
- if (!fixUpResponse(&response, &responseLen, &responseSize, ids->qname, ids->origFlags, ids->ednsAdded, rewrittenResponse, addRoom)) {
+ if (!fixUpResponse(&response, &responseLen, &responseSize, ids->qname, ids->origFlags, ids->ednsAdded, ids->ecsAdded, rewrittenResponse, addRoom)) {
continue;
}
}
bool ednsAdded = false;
+ bool ecsAdded = false;
if (ss && ss->useECS) {
- handleEDNSClientSubnet(query, dq.size, consumed, &dq.len, largerQuery, &(ednsAdded), remote);
+ handleEDNSClientSubnet(query, dq.size, consumed, &dq.len, largerQuery, &(ednsAdded), &(ecsAdded), remote);
}
uint32_t cacheKey = 0;
ids->origDest.sin4.sin_family=0;
ids->delayMsec = delayMsec;
ids->origFlags = origFlags;
- ids->ednsAdded = false;
ids->cacheKey = cacheKey;
ids->skipCache = dq.skipCache;
ids->packetCache = packetCache;
ids->ednsAdded = ednsAdded;
+ ids->ecsAdded = ecsAdded;
#ifdef HAVE_DNSCRYPT
ids->dnsCryptQuery = dnsCryptQuery;
#endif
uint16_t origFlags; // 2
int delayMsec;
bool ednsAdded{false};
+ bool ecsAdded{false};
bool skipCache{false};
};
bool responseContentMatches(const char* response, const uint16_t responseLen, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const ComboAddress& remote);
bool processQuery(LocalStateHolder<NetmaskTree<DynBlock> >& localDynBlock, LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSAction> > > >& localRulactions, blockfilter_t blockFilter, DNSQuestion& dq, string& poolname, int* delayMsec, const struct timespec& now);
bool processResponse(LocalStateHolder<vector<pair<std::shared_ptr<DNSRule>, std::shared_ptr<DNSResponseAction> > > >& localRespRulactions, DNSQuestion& dq);
-bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize, const DNSName& qname, uint16_t origFlags, bool ednsAdded, std::vector<uint8_t>& rewrittenResponse, uint16_t addRoom);
+bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize, const DNSName& qname, uint16_t origFlags, bool ednsAdded, bool ecsAdded, std::vector<uint8_t>& rewrittenResponse, uint16_t addRoom);
void restoreFlags(struct dnsheader* dh, uint16_t origFlags);
#ifdef HAVE_DNSCRYPT
dnswriter.cc dnswriter.hh \
dolog.hh \
ednsoptions.cc ednsoptions.hh \
+ ednscookies.cc ednscookies.hh \
ednssubnet.cc ednssubnet.hh \
iputils.cc iputils.hh \
lock.hh \
dnswriter.cc dnswriter.hh \
dolog.hh \
ednsoptions.cc ednsoptions.hh \
+ ednscookies.cc ednscookies.hh \
ednssubnet.cc ednssubnet.hh \
iputils.cc iputils.hh \
misc.cc misc.hh \
--- /dev/null
+../ednscookies.cc
\ No newline at end of file
--- /dev/null
+../ednscookies.hh
\ No newline at end of file
--- /dev/null
+/*
+ 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
+*/
+#include "ednscookies.hh"
+
+bool getEDNSCookiesOptFromString(const string& option, EDNSCookiesOpt* eco)
+{
+ return getEDNSCookiesOptFromString(option.c_str(), option.length(), eco);
+}
+
+bool getEDNSCookiesOptFromString(const char* option, unsigned int len, EDNSCookiesOpt* eco)
+{
+ if(len != 8 && len < 16)
+ return false;
+ eco->client = string(option, 8);
+ if (len > 8) {
+ eco->server = string(option + 8, len - 8);
+ }
+ return true;
+}
+
+string makeEDNSCookiesOptString(const EDNSCookiesOpt& eco)
+{
+ string ret;
+ if (eco.client.length() != 8)
+ return ret;
+ if (eco.server.length() != 0 && (eco.server.length() < 8 || eco.server.length() > 32))
+ return ret;
+ ret.assign(eco.client);
+ if (eco.server.length() != 0)
+ ret.append(eco.server);
+ return ret;
+}
--- /dev/null
+/*
+ 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_EDNSCOOKIES_HH
+#define PDNS_EDNSCOOKIES_HH
+
+#include "namespaces.hh"
+
+struct EDNSCookiesOpt
+{
+ string client;
+ string server;
+};
+
+bool getEDNSCookiesOptFromString(const char* option, unsigned int len, EDNSCookiesOpt* eco);
+bool getEDNSCookiesOptFromString(const string& option, EDNSCookiesOpt* eco);
+string makeEDNSCookiesOptString(const EDNSCookiesOpt& eco);
+#endif
#include "dnsparser.hh"
#include "dnswriter.hh"
#include "ednsoptions.hh"
+#include "ednscookies.hh"
#include "ednssubnet.hh"
#include <unistd.h>
BOOST_CHECK_EQUAL(mdp.d_header.arcount, 1);
}
-static void validateResponse(const char * packet, size_t packetSize, bool hasEdns)
+static void validateResponse(const char * packet, size_t packetSize, bool hasEdns, uint8_t additionalCount=0)
{
MOADNSParser mdp(packet, packetSize);
BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1);
BOOST_CHECK_EQUAL(mdp.d_header.ancount, 1);
BOOST_CHECK_EQUAL(mdp.d_header.nscount, 0);
- BOOST_CHECK_EQUAL(mdp.d_header.arcount, hasEdns ? 1 : 0);
+ BOOST_CHECK_EQUAL(mdp.d_header.arcount, (hasEdns ? 1 : 0) + additionalCount);
}
BOOST_AUTO_TEST_CASE(addECSWithoutEDNS)
{
string largerPacket;
bool ednsAdded = false;
+ bool ecsAdded = false;
ComboAddress remote;
DNSName name("www.powerdns.com.");
BOOST_CHECK_EQUAL(qname, name);
BOOST_CHECK(qtype == QType::A);
- handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, remote);
+ handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, &ecsAdded, remote);
BOOST_CHECK((size_t) len > query.size());
BOOST_CHECK_EQUAL(largerPacket.size(), 0);
BOOST_CHECK_EQUAL(ednsAdded, true);
+ BOOST_CHECK_EQUAL(ecsAdded, false);
validateQuery(packet, len);
/* not large enought packet */
BOOST_CHECK_EQUAL(qname, name);
BOOST_CHECK(qtype == QType::A);
- handleEDNSClientSubnet((char*) query.data(), query.size(), consumed, &len, largerPacket, &ednsAdded, remote);
+ handleEDNSClientSubnet((char*) query.data(), query.size(), consumed, &len, largerPacket, &ednsAdded, &ecsAdded, remote);
BOOST_CHECK_EQUAL((size_t) len, query.size());
BOOST_CHECK(largerPacket.size() > query.size());
BOOST_CHECK_EQUAL(ednsAdded, true);
+ BOOST_CHECK_EQUAL(ecsAdded, false);
validateQuery(largerPacket.c_str(), largerPacket.size());
}
BOOST_AUTO_TEST_CASE(addECSWithEDNSNoECS) {
string largerPacket;
bool ednsAdded = false;
+ bool ecsAdded = false;
ComboAddress remote;
DNSName name("www.powerdns.com.");
BOOST_CHECK_EQUAL(qname, name);
BOOST_CHECK(qtype == QType::A);
- handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, remote);
+ handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, &ecsAdded, remote);
BOOST_CHECK((size_t) len > query.size());
BOOST_CHECK_EQUAL(largerPacket.size(), 0);
BOOST_CHECK_EQUAL(ednsAdded, false);
+ BOOST_CHECK_EQUAL(ecsAdded, true);
validateQuery(packet, len);
/* not large enought packet */
BOOST_CHECK_EQUAL(qname, name);
BOOST_CHECK(qtype == QType::A);
- handleEDNSClientSubnet((char*) query.data(), query.size(), consumed, &len, largerPacket, &ednsAdded, remote);
+ handleEDNSClientSubnet((char*) query.data(), query.size(), consumed, &len, largerPacket, &ednsAdded, &ecsAdded, remote);
BOOST_CHECK_EQUAL((size_t) len, query.size());
BOOST_CHECK(largerPacket.size() > query.size());
BOOST_CHECK_EQUAL(ednsAdded, false);
+ BOOST_CHECK_EQUAL(ecsAdded, true);
validateQuery(largerPacket.c_str(), largerPacket.size());
}
BOOST_AUTO_TEST_CASE(replaceECSWithSameSize) {
string largerPacket;
bool ednsAdded = false;
+ bool ecsAdded = false;
ComboAddress remote("192.168.1.25");
DNSName name("www.powerdns.com.");
ComboAddress origRemote("127.0.0.1");
BOOST_CHECK(qtype == QType::A);
g_ECSOverride = true;
- handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, remote);
+ handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, &ecsAdded, remote);
BOOST_CHECK_EQUAL((size_t) len, query.size());
BOOST_CHECK_EQUAL(largerPacket.size(), 0);
BOOST_CHECK_EQUAL(ednsAdded, false);
+ BOOST_CHECK_EQUAL(ecsAdded, false);
validateQuery(packet, len);
}
BOOST_AUTO_TEST_CASE(replaceECSWithSmaller) {
string largerPacket;
bool ednsAdded = false;
+ bool ecsAdded = false;
ComboAddress remote("192.168.1.25");
DNSName name("www.powerdns.com.");
ComboAddress origRemote("127.0.0.1");
BOOST_CHECK(qtype == QType::A);
g_ECSOverride = true;
- handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, remote);
+ handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, &ecsAdded, remote);
BOOST_CHECK((size_t) len < query.size());
BOOST_CHECK_EQUAL(largerPacket.size(), 0);
BOOST_CHECK_EQUAL(ednsAdded, false);
+ BOOST_CHECK_EQUAL(ecsAdded, false);
validateQuery(packet, len);
}
BOOST_AUTO_TEST_CASE(replaceECSWithLarger) {
string largerPacket;
bool ednsAdded = false;
+ bool ecsAdded = false;
ComboAddress remote("192.168.1.25");
DNSName name("www.powerdns.com.");
ComboAddress origRemote("127.0.0.1");
BOOST_CHECK(qtype == QType::A);
g_ECSOverride = true;
- handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, remote);
+ handleEDNSClientSubnet(packet, sizeof packet, consumed, &len, largerPacket, &ednsAdded, &ecsAdded, remote);
BOOST_CHECK((size_t) len > query.size());
BOOST_CHECK_EQUAL(largerPacket.size(), 0);
BOOST_CHECK_EQUAL(ednsAdded, false);
+ BOOST_CHECK_EQUAL(ecsAdded, false);
validateQuery(packet, len);
/* not large enought packet */
BOOST_CHECK(qtype == QType::A);
g_ECSOverride = true;
- handleEDNSClientSubnet((char*) query.data(), query.size(), consumed, &len, largerPacket, &ednsAdded, remote);
+ handleEDNSClientSubnet((char*) query.data(), query.size(), consumed, &len, largerPacket, &ednsAdded, &ecsAdded, remote);
BOOST_CHECK_EQUAL((size_t) len, query.size());
BOOST_CHECK(largerPacket.size() > query.size());
BOOST_CHECK_EQUAL(ednsAdded, false);
+ BOOST_CHECK_EQUAL(ecsAdded, false);
validateQuery(largerPacket.c_str(), largerPacket.size());
}
-BOOST_AUTO_TEST_CASE(removeEDNS) {
+BOOST_AUTO_TEST_CASE(removeEDNSWhenFirst) {
DNSName name("www.powerdns.com.");
vector<uint8_t> response;
pw.xfr32BitInt(0x01020304);
pw.addOpt(512, 0, 0);
pw.commit();
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
vector<uint8_t> newResponse;
int res = rewriteResponseWithoutEDNS((const char *) response.data(), response.size(), newResponse);
+ BOOST_CHECK_EQUAL(res, 0);
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+ size_t const ednsOptRRSize = sizeof(struct dnsrecordheader) + 1 /* root in OPT RR */;
+ BOOST_CHECK_EQUAL(newResponse.size(), response.size() - ednsOptRRSize);
+
+ validateResponse((const char *) newResponse.data(), newResponse.size(), false, 1);
+}
+
+BOOST_AUTO_TEST_CASE(removeEDNSWhenIntermediary) {
+ DNSName name("www.powerdns.com.");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.startRecord(DNSName("other.powerdns.com."), QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+ pw.addOpt(512, 0, 0);
+ pw.commit();
+ pw.startRecord(DNSName("yetanother.powerdns.com."), QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+ vector<uint8_t> newResponse;
+ int res = rewriteResponseWithoutEDNS((const char *) response.data(), response.size(), newResponse);
BOOST_CHECK_EQUAL(res, 0);
unsigned int consumed = 0;
size_t const ednsOptRRSize = sizeof(struct dnsrecordheader) + 1 /* root in OPT RR */;
BOOST_CHECK_EQUAL(newResponse.size(), response.size() - ednsOptRRSize);
- validateResponse((const char *) newResponse.data(), newResponse.size(), false);
+ validateResponse((const char *) newResponse.data(), newResponse.size(), false, 2);
+}
+
+BOOST_AUTO_TEST_CASE(removeEDNSWhenLast) {
+ DNSName name("www.powerdns.com.");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+ pw.startRecord(DNSName("other.powerdns.com."), QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+ pw.addOpt(512, 0, 0);
+ pw.commit();
+
+ vector<uint8_t> newResponse;
+ int res = rewriteResponseWithoutEDNS((const char *) response.data(), response.size(), newResponse);
+
+ BOOST_CHECK_EQUAL(res, 0);
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+ size_t const ednsOptRRSize = sizeof(struct dnsrecordheader) + 1 /* root in OPT RR */;
+ BOOST_CHECK_EQUAL(newResponse.size(), response.size() - ednsOptRRSize);
+
+ validateResponse((const char *) newResponse.data(), newResponse.size(), false, 1);
+}
+
+BOOST_AUTO_TEST_CASE(removeECSWhenOnlyOption) {
+ DNSName name("www.powerdns.com.");
+ ComboAddress origRemote("127.0.0.1");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+
+ EDNSSubnetOpts ecsOpts;
+ ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
+ string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
+ DNSPacketWriter::optvect_t opts;
+ opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
+ pw.addOpt(512, 0, 0, opts);
+ pw.commit();
+
+ char * optStart = NULL;
+ size_t optLen = 0;
+ bool last = false;
+
+ int res = locateEDNSOptRR((char *) response.data(), response.size(), &optStart, &optLen, &last);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(last, true);
+
+ size_t responseLen = response.size();
+ size_t existingOptLen = optLen;
+ BOOST_CHECK(existingOptLen < responseLen);
+ res = removeEDNSOptionFromOPT(optStart, &optLen, EDNSOptionCode::ECS);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(optLen, existingOptLen - (origECSOptionStr.size() + 4));
+ responseLen -= (existingOptLen - optLen);
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) response.data(), responseLen, sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+
+ validateResponse((const char *) response.data(), responseLen, true, 1);
+}
+
+BOOST_AUTO_TEST_CASE(removeECSWhenFirstOption) {
+ DNSName name("www.powerdns.com.");
+ ComboAddress origRemote("127.0.0.1");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+
+ EDNSSubnetOpts ecsOpts;
+ ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
+ string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
+ EDNSCookiesOpt cookiesOpt;
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
+ DNSPacketWriter::optvect_t opts;
+ opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
+ opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
+ pw.addOpt(512, 0, 0, opts);
+ pw.commit();
+
+ char * optStart = NULL;
+ size_t optLen = 0;
+ bool last = false;
+
+ int res = locateEDNSOptRR((char *) response.data(), response.size(), &optStart, &optLen, &last);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(last, true);
+
+ size_t responseLen = response.size();
+ size_t existingOptLen = optLen;
+ BOOST_CHECK(existingOptLen < responseLen);
+ res = removeEDNSOptionFromOPT(optStart, &optLen, EDNSOptionCode::ECS);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(optLen, existingOptLen - (origECSOptionStr.size() + 4));
+ responseLen -= (existingOptLen - optLen);
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) response.data(), responseLen, sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+
+ validateResponse((const char *) response.data(), responseLen, true, 1);
+}
+
+BOOST_AUTO_TEST_CASE(removeECSWhenIntermediaryOption) {
+ DNSName name("www.powerdns.com.");
+ ComboAddress origRemote("127.0.0.1");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+
+ EDNSSubnetOpts ecsOpts;
+ ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
+ string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
+
+ EDNSCookiesOpt cookiesOpt;
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ string cookiesOptionStr1 = makeEDNSCookiesOptString(cookiesOpt);
+ string cookiesOptionStr2 = makeEDNSCookiesOptString(cookiesOpt);
+
+ DNSPacketWriter::optvect_t opts;
+ opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr1));
+ opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
+ opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr2));
+ pw.addOpt(512, 0, 0, opts);
+ pw.commit();
+
+ char * optStart = NULL;
+ size_t optLen = 0;
+ bool last = false;
+
+ int res = locateEDNSOptRR((char *) response.data(), response.size(), &optStart, &optLen, &last);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(last, true);
+
+ size_t responseLen = response.size();
+ size_t existingOptLen = optLen;
+ BOOST_CHECK(existingOptLen < responseLen);
+ res = removeEDNSOptionFromOPT(optStart, &optLen, EDNSOptionCode::ECS);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(optLen, existingOptLen - (origECSOptionStr.size() + 4));
+ responseLen -= (existingOptLen - optLen);
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) response.data(), responseLen, sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+
+ validateResponse((const char *) response.data(), responseLen, true, 1);
+}
+
+BOOST_AUTO_TEST_CASE(removeECSWhenLastOption) {
+ DNSName name("www.powerdns.com.");
+ ComboAddress origRemote("127.0.0.1");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+
+ EDNSCookiesOpt cookiesOpt;
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
+ EDNSSubnetOpts ecsOpts;
+ ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
+ string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
+ DNSPacketWriter::optvect_t opts;
+ opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
+ opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
+ pw.addOpt(512, 0, 0, opts);
+ pw.commit();
+
+ char * optStart = NULL;
+ size_t optLen = 0;
+ bool last = false;
+
+ int res = locateEDNSOptRR((char *) response.data(), response.size(), &optStart, &optLen, &last);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(last, true);
+
+ size_t responseLen = response.size();
+ size_t existingOptLen = optLen;
+ BOOST_CHECK(existingOptLen < responseLen);
+ res = removeEDNSOptionFromOPT(optStart, &optLen, EDNSOptionCode::ECS);
+ BOOST_CHECK_EQUAL(res, 0);
+ BOOST_CHECK_EQUAL(optLen, existingOptLen - (origECSOptionStr.size() + 4));
+ responseLen -= (existingOptLen - optLen);
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) response.data(), responseLen, sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+
+ validateResponse((const char *) response.data(), responseLen, true, 1);
+}
+
+BOOST_AUTO_TEST_CASE(rewritingWithoutECSWhenOnlyOption) {
+ DNSName name("www.powerdns.com.");
+ ComboAddress origRemote("127.0.0.1");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+
+ EDNSSubnetOpts ecsOpts;
+ ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
+ string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
+ DNSPacketWriter::optvect_t opts;
+ opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
+ pw.addOpt(512, 0, 0, opts);
+ pw.commit();
+
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+
+ vector<uint8_t> newResponse;
+ int res = rewriteResponseWithoutEDNSOption((const char *) response.data(), response.size(), EDNSOptionCode::ECS, newResponse);
+ BOOST_CHECK_EQUAL(res, 0);
+
+ BOOST_CHECK_EQUAL(newResponse.size(), response.size() - (origECSOptionStr.size() + 4));
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+
+ validateResponse((const char *) newResponse.data(), newResponse.size(), true, 1);
+}
+
+BOOST_AUTO_TEST_CASE(rewritingWithoutECSWhenFirstOption) {
+ DNSName name("www.powerdns.com.");
+ ComboAddress origRemote("127.0.0.1");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+
+ EDNSSubnetOpts ecsOpts;
+ ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
+ string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
+ EDNSCookiesOpt cookiesOpt;
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
+ DNSPacketWriter::optvect_t opts;
+ opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
+ opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
+ pw.addOpt(512, 0, 0, opts);
+ pw.commit();
+
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+
+ vector<uint8_t> newResponse;
+ int res = rewriteResponseWithoutEDNSOption((const char *) response.data(), response.size(), EDNSOptionCode::ECS, newResponse);
+ BOOST_CHECK_EQUAL(res, 0);
+
+ BOOST_CHECK_EQUAL(newResponse.size(), response.size() - (origECSOptionStr.size() + 4));
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+
+ validateResponse((const char *) newResponse.data(), newResponse.size(), true, 1);
+}
+
+BOOST_AUTO_TEST_CASE(rewritingWithoutECSWhenIntermediaryOption) {
+ DNSName name("www.powerdns.com.");
+ ComboAddress origRemote("127.0.0.1");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+
+ EDNSSubnetOpts ecsOpts;
+ ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
+ string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
+ EDNSCookiesOpt cookiesOpt;
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ string cookiesOptionStr1 = makeEDNSCookiesOptString(cookiesOpt);
+ string cookiesOptionStr2 = makeEDNSCookiesOptString(cookiesOpt);
+ DNSPacketWriter::optvect_t opts;
+ opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr1));
+ opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
+ opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr2));
+ pw.addOpt(512, 0, 0, opts);
+ pw.commit();
+
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+
+ vector<uint8_t> newResponse;
+ int res = rewriteResponseWithoutEDNSOption((const char *) response.data(), response.size(), EDNSOptionCode::ECS, newResponse);
+ BOOST_CHECK_EQUAL(res, 0);
+
+ BOOST_CHECK_EQUAL(newResponse.size(), response.size() - (origECSOptionStr.size() + 4));
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+
+ validateResponse((const char *) newResponse.data(), newResponse.size(), true, 1);
+}
+
+BOOST_AUTO_TEST_CASE(rewritingWithoutECSWhenLastOption) {
+ DNSName name("www.powerdns.com.");
+ ComboAddress origRemote("127.0.0.1");
+
+ vector<uint8_t> response;
+ DNSPacketWriter pw(response, name, QType::A, QClass::IN, 0);
+ pw.getHeader()->qr = 1;
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ANSWER, true);
+ pw.xfr32BitInt(0x01020304);
+
+ EDNSSubnetOpts ecsOpts;
+ ecsOpts.source = Netmask(origRemote, g_ECSSourcePrefixV4);
+ string origECSOptionStr = makeEDNSSubnetOptsString(ecsOpts);
+ EDNSCookiesOpt cookiesOpt;
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ string cookiesOptionStr = makeEDNSCookiesOptString(cookiesOpt);
+ DNSPacketWriter::optvect_t opts;
+ opts.push_back(make_pair(EDNSOptionCode::COOKIE, cookiesOptionStr));
+ opts.push_back(make_pair(EDNSOptionCode::ECS, origECSOptionStr));
+ pw.addOpt(512, 0, 0, opts);
+ pw.commit();
+
+ pw.startRecord(name, QType::A, 3600, QClass::IN, DNSResourceRecord::ADDITIONAL, true);
+ pw.xfr32BitInt(0x01020304);
+ pw.commit();
+
+ vector<uint8_t> newResponse;
+ int res = rewriteResponseWithoutEDNSOption((const char *) response.data(), response.size(), EDNSOptionCode::ECS, newResponse);
+ BOOST_CHECK_EQUAL(res, 0);
+
+ BOOST_CHECK_EQUAL(newResponse.size(), response.size() - (origECSOptionStr.size() + 4));
+
+ unsigned int consumed = 0;
+ uint16_t qtype;
+ DNSName qname((const char*) newResponse.data(), newResponse.size(), sizeof(dnsheader), false, &qtype, NULL, &consumed);
+ BOOST_CHECK_EQUAL(qname, name);
+ BOOST_CHECK(qtype == QType::A);
+
+ validateResponse((const char *) newResponse.data(), newResponse.size(), true, 1);
}
BOOST_AUTO_TEST_SUITE_END();
test = test[-(mask_bits // 8):]
format = "!HBB%ds" % (mask_bits // 8)
- data = struct.pack(format, self.family, self.mask, 0, test)
+ data = struct.pack(format, self.family, self.mask, self.scope, test)
file.write(data)
def from_wire(cls, otype, wire, current, olen):
--- /dev/null
+#!/usr/bin/env python2
+
+import dns
+import dns.edns
+import dns.flags
+import dns.message
+import dns.query
+
+class CookiesOption(dns.edns.Option):
+ """Implementation of draft-ietf-dnsop-cookies-09.
+ """
+
+ def __init__(self, client, server):
+ super(CookiesOption, self).__init__(10)
+
+ if len(client) != 8:
+ raise Exception('invalid client cookie length')
+
+ if server is not None and len(server) != 0 and (len(server) < 8 or len(server) > 32):
+ raise Exception('invalid server cookie length')
+
+ self.client = client
+ self.server = server
+
+ def to_wire(self, file):
+ """Create EDNS packet as definied in draft-ietf-dnsop-cookies-09."""
+
+ file.write(self.client)
+ if self.server and len(self.server) > 0:
+ file.write(self.server)
+
+ def from_wire(cls, otype, wire, current, olen):
+ """Read EDNS packet as defined in draft-ietf-dnsop-cookies-09.
+
+ Returns:
+ An instance of CookiesOption based on the EDNS packet
+ """
+
+ data = wire[current:current + olen]
+ if len(data) != 8 and (len(data) < 16 or len(data) > 40):
+ raise Exception('Invalid EDNS Cookies option')
+
+ client = data[:8]
+ if len(data) > 8:
+ server = data[8:]
+ else:
+ server = None
+
+ return cls(client, server)
+
+ from_wire = classmethod(from_wire)
+
+ def __repr__(self):
+ return '%s(%s, %s)' % (
+ self.__class__.__name__,
+ self.client,
+ self.server
+ )
+
+ def __eq__(self, other):
+ if not isinstance(other, CookiesOption):
+ return False
+ if self.client != other.client:
+ return False
+ if self.server != other.server:
+ return False
+ return True
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+
+dns.edns._type_to_class[0x000A] = CookiesOption
+
import unittest
import dns
import clientsubnetoption
+import cookiesoption
from dnsdisttests import DNSDistTest
class TestEdnsClientSubnetNoOverride(DNSDistTest):
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, -1)
+ self.assertEquals(len(receivedResponse.options), 0)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, -1)
+ self.assertEquals(len(receivedResponse.options), 0)
def testWithEDNSNoECS(self):
"""
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
def testWithEDNSECS(self):
"""
receivedQuery.id = query.id
self.assertEquals(query, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
receivedQuery.id = query.id
self.assertEquals(query, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
+
+ def testWithoutEDNSResponseWithECS(self):
+ """
+ ECS: No existing EDNS (BE returning ECS)
+
+ Send a query without EDNS, check that the query
+ received by the responder has the correct ECS value
+ and that the response received from dnsdist does not
+ have an EDNS pseudo-RR.
+ This time the response returned by the backend contains
+ an ECS option with scope set.
+ """
+ name = 'withoutedns.bereturnsecs.ecs.tests.powerdns.com.'
+ ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
+ query = dns.message.make_query(name, 'A', 'IN')
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, options=[ecso], payload=512)
+ response = dns.message.make_response(expectedQuery)
+ ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ response.use_edns(edns=True, payload=4096, options=[ecsoResponse])
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+ expectedResponse.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, -1)
+ self.assertEquals(len(receivedResponse.options), 0)
+
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, -1)
+ self.assertEquals(len(receivedResponse.options), 0)
+
+ def testWithEDNSNoECSResponseWithECS(self):
+ """
+ ECS: Existing EDNS without ECS (BE returning only the ECS option)
+
+ Send a query with EDNS but no ECS value.
+ Check that the query received by the responder
+ has a valid ECS value and that the response
+ received from dnsdist contains an EDNS pseudo-RR.
+ This time the response returned by the backend contains
+ an ECS option with scope set.
+ """
+ name = 'withednsnoecs.bereturnsecs.ecs.tests.powerdns.com.'
+ ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ response = dns.message.make_response(expectedQuery)
+ ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ response.use_edns(edns=True, payload=4096, options=[ecsoResponse])
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+ expectedResponse.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
+
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
+
+ def testWithEDNSNoECSResponseWithCookiesThenECS(self):
+ """
+ ECS: Existing EDNS without ECS (BE returning Cookies then ECS options)
+
+ Send a query with EDNS but no ECS value.
+ Check that the query received by the responder
+ has a valid ECS value and that the response
+ received from dnsdist contains an EDNS pseudo-RR.
+ This time the response returned by the backend contains
+ one cookies then one ECS option.
+ """
+ name = 'withednsnoecs.bereturnscookiesthenecs.ecs.tests.powerdns.com.'
+ ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ response = dns.message.make_response(expectedQuery)
+ ecoResponse = cookiesoption.CookiesOption('deadbeef', 'deadbeef')
+ ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ response.use_edns(edns=True, payload=4096, options=[ecoResponse, ecsoResponse])
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+ expectedResponse.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 1)
+
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 1)
+
+ def testWithEDNSNoECSResponseWithECSThenCookies(self):
+ """
+ ECS: Existing EDNS without ECS (BE returning ECS then Cookies options)
+
+ Send a query with EDNS but no ECS value.
+ Check that the query received by the responder
+ has a valid ECS value and that the response
+ received from dnsdist contains an EDNS pseudo-RR.
+ This time the response returned by the backend contains
+ one ECS then one Cookies option.
+ """
+ name = 'withednsnoecs.bereturnsecsthencookies.ecs.tests.powerdns.com.'
+ ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ response = dns.message.make_response(expectedQuery)
+ ecoResponse = cookiesoption.CookiesOption('deadbeef', 'deadbeef')
+ ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ response.use_edns(edns=True, payload=4096, options=[ecsoResponse, ecoResponse])
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+ expectedResponse.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 1)
+
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 1)
+
+ def testWithEDNSNoECSResponseWithCookiesThenECSThenCookies(self):
+ """
+ ECS: Existing EDNS without ECS (BE returning Cookies, ECS then Cookies options)
+
+ Send a query with EDNS but no ECS value.
+ Check that the query received by the responder
+ has a valid ECS value and that the response
+ received from dnsdist contains an EDNS pseudo-RR.
+ This time the response returned by the backend contains
+ one Cookies, one ECS then one Cookies option.
+ """
+ name = 'withednsnoecs.bereturnscookiesecscookies.ecs.tests.powerdns.com.'
+ ecso = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24)
+ query = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+ expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096, options=[ecso])
+ response = dns.message.make_response(expectedQuery)
+ ecoResponse = cookiesoption.CookiesOption('deadbeef', 'deadbeef')
+ ecsoResponse = clientsubnetoption.ClientSubnetOption('127.0.0.1', 24, scope=24)
+ response.use_edns(edns=True, payload=4096, options=[ecoResponse, ecsoResponse, ecoResponse])
+ expectedResponse = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 3600,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '127.0.0.1')
+ response.answer.append(rrset)
+ expectedResponse.answer.append(rrset)
+
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 2)
+
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ self.assertTrue(receivedQuery)
+ self.assertTrue(receivedResponse)
+ receivedQuery.id = expectedQuery.id
+ self.assertEquals(expectedQuery, receivedQuery)
+ self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 2)
+
class TestEdnsClientSubnetOverride(DNSDistTest):
"""
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, -1)
+ self.assertEquals(len(receivedResponse.options), 0)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, -1)
+ self.assertEquals(len(receivedResponse.options), 0)
def testWithEDNSNoECS(self):
"""
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(expectedResponse, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
def testWithEDNSShorterInitialECS(self):
"""
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
def testWithEDNSLongerInitialECS(self):
"""
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
def testWithEDNSSameSizeInitialECS(self):
"""
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)
(receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
self.assertTrue(receivedQuery)
receivedQuery.id = expectedQuery.id
self.assertEquals(expectedQuery, receivedQuery)
self.assertEquals(response, receivedResponse)
-
-
-if __name__ == '__main__':
- unittest.main()
- exit(0)
+ self.assertEquals(receivedResponse.edns, 0)
+ self.assertEquals(len(receivedResponse.options), 0)