dnssecsigner.cc \
dnswriter.cc \
ednsoptions.cc ednsoptions.hh \
+ ednscookies.cc \
ednssubnet.cc \
gettime.cc gettime.hh \
gss_context.cc gss_context.hh \
test-misc_hh.cc \
test-nameserver_cc.cc \
test-packetcache_cc.cc \
+ test-packetcache_hh.cc \
test-rcpgenerator_cc.cc \
test-signers.cc \
test-sha_hh.cc \
return false;
}
- uint32_t hash = canHashPacket(p->getString(), false);
+ uint32_t hash = canHashPacket(p->getString());
p->setHash(hash);
string value;
return false;
}
- haveSomething = getEntryLocked(mc.d_map, hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value);
+ haveSomething = getEntryLocked(mc.d_map, p->getString(), hash, p->qdomain, p->qtype.getCode(), p->d_tcp, now, value);
}
if (!haveSomething) {
return true;
}
+bool AuthPacketCache::entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp)
+{
+ return iter->tcp == tcp && iter->qtype == qtype && iter->qname == qname && queryMatches(iter->query, query, qname);
+}
+
void AuthPacketCache::insert(DNSPacket *q, DNSPacket *r, unsigned int maxTTL)
{
cleanupIfNeeded();
entry.qtype = q->qtype.getCode();
entry.value = r->getString();
entry.tcp = r->d_tcp;
+ entry.query = q->getString();
auto& mc = getMap(entry.qname);
{
auto iter = range.first;
for( ; iter != range.second ; ++iter) {
- if (iter->tcp != entry.tcp || iter->qtype != entry.qtype || iter->qname != entry.qname)
+ if (!entryMatches(iter, entry.query, entry.qname, entry.qtype, entry.tcp)) {
continue;
+ }
iter->value = entry.value;
iter->ttd = now + ourttl;
}
}
-bool AuthPacketCache::getEntryLocked(cmap_t& map, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
+bool AuthPacketCache::getEntryLocked(cmap_t& map, const std::string& query, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& value)
{
auto& idx = map.get<HashTag>();
auto range = idx.equal_range(hash);
for(auto iter = range.first; iter != range.second ; ++iter) {
- if (iter->ttd < now)
+ if (iter->ttd < now) {
continue;
+ }
- if (iter->tcp != tcp || iter->qtype != qtype || iter->qname != qname)
+ if (!entryMatches(iter, query, qname, qtype, tcp)) {
continue;
+ }
value = iter->value;
return true;
struct CacheEntry
{
+ mutable string query;
mutable string value;
DNSName qname;
return d_maps[name.hash() % d_maps.size()];
}
- bool getEntryLocked(cmap_t& map, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& entry);
+ static bool entryMatches(cmap_t::index<HashTag>::type::iterator& iter, const std::string& query, const DNSName& qname, uint16_t qtype, bool tcp);
+ bool getEntryLocked(cmap_t& map, const std::string& query, uint32_t hash, const DNSName &qname, uint16_t qtype, bool tcp, time_t now, string& entry);
void cleanupIfNeeded();
AtomicCounter d_ops{0};
class PacketCache : public boost::noncopyable
{
-protected:
- static uint32_t canHashPacket(const std::string& packet, bool skipECS=true)
+public:
+ static uint32_t canHashPacket(const std::string& packet, uint16_t* ecsBegin, uint16_t* ecsEnd)
{
uint32_t ret = 0;
- ret=burtle((const unsigned char*)packet.c_str() + 2, 10, ret); // rest of dnsheader, skip id
+ ret = burtle(reinterpret_cast<const unsigned char*>(packet.c_str()) + 2, sizeof(dnsheader) - 2, ret); // rest of dnsheader, skip id
size_t packetSize = packet.size();
- size_t pos = 12;
+ size_t pos = sizeof(dnsheader);
const char* end = packet.c_str() + packetSize;
const char* p = packet.c_str() + pos;
ret=burtle(&l, 1, ret);
} // XXX the embedded 0 in the qname will break the subnet stripping
- struct dnsheader* dh = (struct dnsheader*)packet.c_str();
+ const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(packet.c_str());
const char* skipBegin = p;
const char* skipEnd = p;
+ if (ecsBegin != nullptr && ecsEnd != nullptr) {
+ *ecsBegin = 0;
+ *ecsEnd = 0;
+ }
/* we need at least 1 (final empty label) + 2 (QTYPE) + 2 (QCLASS)
+ OPT root label (1), type (2), class (2) and ttl (4)
+ the OPT RR rdlen (2)
= 16
*/
- if(skipECS && ntohs(dh->arcount)==1 && (pos+16) < packetSize) {
+ if(ntohs(dh->arcount)==1 && (pos+16) < packetSize) {
char* optionBegin = nullptr;
size_t optionLen = 0;
/* skip the final empty label (1), the qtype (2), qclass (2) */
/* root label (1), type (2), class (2) and ttl (4) */
- int res = getEDNSOption((char*) p + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
+ int res = getEDNSOption(const_cast<char*>(reinterpret_cast<const char*>(p)) + 14, end - (p + 14), EDNSOptionCode::ECS, &optionBegin, &optionLen);
if (res == 0) {
skipBegin = optionBegin;
skipEnd = optionBegin + optionLen;
+ if (ecsBegin != nullptr && ecsEnd != nullptr) {
+ *ecsBegin = optionBegin - packet.c_str();
+ *ecsEnd = *ecsBegin + optionLen;
+ }
}
}
if (skipBegin > p) {
- // cerr << "Hashing from " << (p-packet.c_str()) << " for " << skipBegin-p << "bytes, end is at "<< end-packet.c_str() << endl;
- ret = burtle((const unsigned char*)p, skipBegin-p, ret);
+ ret = burtle(reinterpret_cast<const unsigned char*>(p), skipBegin-p, ret);
}
if (skipEnd < end) {
- // cerr << "Hashing from " << (skipEnd-packet.c_str()) << " for " << end-skipEnd << "bytes, end is at " << end-packet.c_str() << endl;
- ret = burtle((const unsigned char*) skipEnd, end-skipEnd, ret);
+ ret = burtle(reinterpret_cast<const unsigned char*>(skipEnd), end-skipEnd, ret);
}
return ret;
}
+
+ static uint32_t canHashPacket(const std::string& packet)
+ {
+ uint32_t ret = 0;
+ ret = burtle(reinterpret_cast<const unsigned char*>(packet.c_str()) + 2, sizeof(dnsheader) - 2, ret); // rest of dnsheader, skip id
+ size_t packetSize = packet.size();
+ size_t pos = sizeof(dnsheader);
+ const char* end = packet.c_str() + packetSize;
+ const char* p = packet.c_str() + pos;
+
+ for(; p < end && *p; ++p) { // XXX if you embed a 0 in your qname we'll stop lowercasing there
+ const unsigned char l = dns_tolower(*p); // label lengths can safely be lower cased
+ ret=burtle(&l, 1, ret);
+ } // XXX the embedded 0 in the qname will break the subnet stripping
+
+ if (p < end) {
+ ret = burtle(reinterpret_cast<const unsigned char*>(p), end-p, ret);
+ }
+
+ return ret;
+ }
+
+ static bool queryHeaderMatches(const std::string& cachedQuery, const std::string& query)
+ {
+ if (cachedQuery.size() != query.size()) {
+ return false;
+ }
+
+ return (cachedQuery.compare(/* skip the ID */ 2, sizeof(dnsheader) - 2, query, 2, sizeof(dnsheader) - 2) == 0);
+ }
+
+ static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname)
+ {
+ if (!queryHeaderMatches(cachedQuery, query)) {
+ return false;
+ }
+
+ size_t pos = sizeof(dnsheader) + qname.wirelength();
+
+ return (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) == 0);
+ }
+
+ static bool queryMatches(const std::string& cachedQuery, const std::string& query, const DNSName& qname, uint16_t ecsBegin, uint16_t ecsEnd)
+ {
+ if (!queryHeaderMatches(cachedQuery, query)) {
+ return false;
+ }
+
+ size_t pos = sizeof(dnsheader) + qname.wirelength();
+
+ if (ecsBegin != 0 && ecsBegin >= pos && ecsEnd > ecsBegin) {
+ if (cachedQuery.compare(pos, ecsBegin - pos, query, pos, ecsBegin - pos) != 0) {
+ return false;
+ }
+
+ if (cachedQuery.compare(ecsEnd, cachedQuery.size() - ecsEnd, query, ecsEnd, query.size() - ecsEnd) != 0) {
+ return false;
+ }
+ }
+ else {
+ if (cachedQuery.compare(pos, cachedQuery.size() - pos, query, pos, query.size() - pos) != 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
};
#endif /* PACKETCACHE_HH */
//! used to send information to a newborn mthread
struct DNSComboWriter {
- DNSComboWriter(const std::string& query, const struct timeval& now): d_mdp(true, query), d_now(now)
+ DNSComboWriter(const std::string& query, const struct timeval& now): d_mdp(true, query), d_now(now), d_query(query)
{
}
- DNSComboWriter(const std::string& query, const struct timeval& now, std::vector<std::string>&& policyTags, LuaContext::LuaObject&& data): d_mdp(true, query), d_now(now), d_policyTags(std::move(policyTags)), d_data(std::move(data))
+ DNSComboWriter(const std::string& query, const struct timeval& now, std::vector<std::string>&& policyTags, LuaContext::LuaObject&& data): d_mdp(true, query), d_now(now), d_query(query), d_policyTags(std::move(policyTags)), d_data(std::move(data))
{
}
string d_requestorId;
string d_deviceId;
#endif
+ std::string d_query;
std::vector<std::string> d_policyTags;
LuaContext::LuaObject d_data;
EDNSSubnetOpts d_ednssubnet;
unsigned int d_tag{0};
uint32_t d_qhash{0};
uint32_t d_ttlCap{std::numeric_limits<uint32_t>::max()};
+ uint16_t d_ecsBegin{0};
+ uint16_t d_ecsEnd{0};
bool d_variable{false};
bool d_ecsFound{false};
bool d_ecsParsed{false};
g_log<<Logger::Warning<<"Sending UDP reply to client "<<dc->getRemote()<<" failed with: "<<strerror(errno)<<endl;
if(!SyncRes::s_nopacketcache && !variableAnswer && !sr.wasVariable() ) {
- t_packetCache->insertResponsePacket(dc->d_tag, dc->d_qhash, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass,
+ t_packetCache->insertResponsePacket(dc->d_tag, dc->d_qhash, dc->d_query, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass,
string((const char*)&*packet.begin(), packet.size()),
g_now.tv_sec,
pw.getHeader()->rcode == RCode::ServFail ? SyncRes::s_packetcacheservfailttl :
min(minTTL,SyncRes::s_packetcachettl),
dq.validationState,
+ dc->d_ecsBegin,
+ dc->d_ecsEnd,
pbMessage);
}
// else cerr<<"Not putting in packet cache: "<<sr.wasVariable()<<endl;
EDNSSubnetOpts ednssubnet;
bool ecsFound = false;
bool ecsParsed = false;
+ uint16_t ecsBegin = 0;
+ uint16_t ecsEnd = 0;
uint32_t ttlCap = std::numeric_limits<uint32_t>::max();
bool variable = false;
try {
as cacheable we would cache it with a wrong tag, so better safe than sorry. */
vState valState;
if (qnameParsed) {
- cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, qtype, qclass, g_now.tv_sec, &response, &age, &valState, &qhash, pbMessage ? &(*pbMessage) : nullptr));
+ cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, qtype, qclass, g_now.tv_sec, &response, &age, &valState, &qhash, &ecsBegin, &ecsEnd, pbMessage ? &(*pbMessage) : nullptr));
}
else {
- cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, &qtype, &qclass, g_now.tv_sec, &response, &age, &valState, &qhash, pbMessage ? &(*pbMessage) : nullptr));
+ cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, &qtype, &qclass, g_now.tv_sec, &response, &age, &valState, &qhash, &ecsBegin, &ecsEnd, pbMessage ? &(*pbMessage) : nullptr));
}
if (cacheHit) {
dc->d_tcp=false;
dc->d_ecsFound = ecsFound;
dc->d_ecsParsed = ecsParsed;
+ dc->d_ecsBegin = ecsBegin;
+ dc->d_ecsEnd = ecsEnd;
dc->d_ednssubnet = ednssubnet;
dc->d_ttlCap = ttlCap;
dc->d_variable = variable;
return count;
}
-static bool qrMatch(const DNSName& qname, uint16_t qtype, uint16_t qclass, const DNSName& rname, uint16_t rtype, uint16_t rclass)
+bool RecursorPacketCache::qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint16_t ecsBegin, uint16_t ecsEnd)
{
// this ignores checking on the EDNS subnet flags!
- return qname==rname && rtype == qtype && rclass == qclass;
+ if (qname != iter->d_name || iter->d_type != qtype || iter->d_class != qclass) {
+ return false;
+ }
+
+ if (iter->d_ecsBegin != ecsBegin || iter->d_ecsEnd != ecsEnd) {
+ return false;
+ }
+
+ return queryMatches(iter->d_query, queryPacket, qname, ecsBegin, ecsEnd);
}
-bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage)
+bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage, uint16_t ecsBegin, uint16_t ecsEnd)
{
- for(auto iter = range.first ; iter != range.second ; ++ iter) {
+ for(auto iter = range.first ; iter != range.second ; ++iter) {
// the possibility is VERY real that we get hits that are not right - birthday paradox
- if(!qrMatch(qname, qtype, qclass, iter->d_name, iter->d_type, iter->d_class))
+ if (!qrMatch(iter, queryPacket, qname, qtype, qclass, ecsBegin, ecsEnd)) {
continue;
- if(now < iter->d_ttd) { // it is right, it is fresh!
+ }
+
+ if (now < iter->d_ttd) { // it is right, it is fresh!
*age = static_cast<uint32_t>(now - iter->d_creation);
*responsePacket = iter->d_packet;
responsePacket->replace(0, 2, queryPacket.c_str(), 2);
{
DNSName qname;
uint16_t qtype, qclass;
+ uint16_t ecsBegin;
+ uint16_t ecsEnd;
vState valState;
- return getResponsePacket(tag, queryPacket, qname, &qtype, &qclass, now, responsePacket, age, &valState, qhash, nullptr);
+ return getResponsePacket(tag, queryPacket, qname, &qtype, &qclass, now, responsePacket, age, &valState, qhash, &ecsBegin, &ecsEnd, nullptr);
}
bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
std::string* responsePacket, uint32_t* age, uint32_t* qhash)
{
vState valState;
- return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, &valState, qhash, nullptr);
+ uint16_t ecsBegin;
+ uint16_t ecsEnd;
+ return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, &valState, qhash, &ecsBegin, &ecsEnd, nullptr);
}
bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now,
- std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
+ std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage)
{
- *qhash = canHashPacket(queryPacket, true);
+ *qhash = canHashPacket(queryPacket, ecsBegin, ecsEnd);
const auto& idx = d_packetCache.get<HashTag>();
auto range = idx.equal_range(tie(tag,*qhash));
d_misses++;
return false;
}
- return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, protobufMessage);
+
+ return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, protobufMessage, *ecsBegin, *ecsEnd);
}
bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, DNSName& qname, uint16_t* qtype, uint16_t* qclass, time_t now,
- std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
+ std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage)
{
- *qhash = canHashPacket(queryPacket, true);
+ *qhash = canHashPacket(queryPacket, ecsBegin, ecsEnd);
const auto& idx = d_packetCache.get<HashTag>();
auto range = idx.equal_range(tie(tag,*qhash));
qname = DNSName(queryPacket.c_str(), queryPacket.length(), sizeof(dnsheader), false, qtype, qclass, 0);
- return checkResponseMatches(range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, protobufMessage);
+ return checkResponseMatches(range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, protobufMessage, *ecsBegin, *ecsEnd);
}
-void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl)
+void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, const std::string& query, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, uint16_t ecsBegin, uint16_t ecsEnd)
{
vState valState;
boost::optional<RecProtoBufMessage> pb(boost::none);
- insertResponsePacket(tag, qhash, qname, qtype, qclass, responsePacket, now, ttl, valState, pb);
+ insertResponsePacket(tag, qhash, query, qname, qtype, qclass, responsePacket, now, ttl, valState, ecsBegin, ecsEnd, pb);
}
-void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, const vState& valState, const boost::optional<RecProtoBufMessage>& protobufMessage)
+void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash, const std::string& query, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, const vState& valState, uint16_t ecsBegin, uint16_t ecsEnd, const boost::optional<RecProtoBufMessage>& protobufMessage)
{
auto& idx = d_packetCache.get<HashTag>();
auto range = idx.equal_range(tie(tag,qhash));
auto iter = range.first;
for( ; iter != range.second ; ++iter) {
- if(iter->d_type != qtype || iter->d_class != qclass || iter->d_name != qname)
+ if (iter->d_type != qtype || iter->d_class != qclass || iter->d_name != qname) {
continue;
+ }
moveCacheItemToBack(d_packetCache, iter);
iter->d_packet = responsePacket;
+ iter->d_query = query;
+ iter->d_ecsBegin = ecsBegin;
+ iter->d_ecsEnd = ecsEnd;
iter->d_ttd = now + ttl;
iter->d_creation = now;
iter->d_vstate = valState;
}
if(iter == range.second) { // nothing to refresh
- struct Entry e(qname, responsePacket);
+ struct Entry e(qname, responsePacket, query);
e.d_qhash = qhash;
+ e.d_ecsBegin = ecsBegin;
+ e.d_ecsEnd = ecsEnd;
e.d_type = qtype;
e.d_class = qclass;
e.d_ttd = now+ttl;
RecursorPacketCache();
bool getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now, std::string* responsePacket, uint32_t* age, uint32_t* qhash);
bool getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, uint32_t* qhash);
- bool getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, RecProtoBufMessage* protobufMessage);
- bool getResponsePacket(unsigned int tag, const std::string& queryPacket, DNSName& qname, uint16_t* qtype, uint16_t* qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, RecProtoBufMessage* protobufMessage);
- void insertResponsePacket(unsigned int tag, uint32_t qhash, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl);
- void insertResponsePacket(unsigned int tag, uint32_t qhash, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, const vState& valState, const boost::optional<RecProtoBufMessage>& protobufMessage);
+ bool getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage);
+ bool getResponsePacket(unsigned int tag, const std::string& queryPacket, DNSName& qname, uint16_t* qtype, uint16_t* qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage);
+ void insertResponsePacket(unsigned int tag, uint32_t qhash, const std::string& query, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, uint16_t ecsBegin, uint16_t ecsEnd);
+ void insertResponsePacket(unsigned int tag, uint32_t qhash, const std::string& query, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string& responsePacket, time_t now, uint32_t ttl, const vState& valState, uint16_t ecsBegin, uint16_t ecsEnd, const boost::optional<RecProtoBufMessage>& protobufMessage);
void doPruneTo(unsigned int maxSize=250000);
uint64_t doDump(int fd);
int doWipePacketCache(const DNSName& name, uint16_t qtype=0xffff, bool subtree=false);
struct NameTag {};
struct Entry
{
- Entry(const DNSName& qname, const std::string& packet): d_name(qname), d_packet(packet)
+ Entry(const DNSName& qname, const std::string& packet, const std::string& query): d_name(qname), d_packet(packet), d_query(query)
{
}
- mutable time_t d_ttd;
- mutable time_t d_creation; // so we can 'age' our packets
DNSName d_name;
- uint16_t d_type;
- uint16_t d_class;
mutable std::string d_packet; // "I know what I am doing"
+ mutable std::string d_query;
#ifdef HAVE_PROTOBUF
mutable boost::optional<RecProtoBufMessage> d_protobufMessage;
#endif
+ mutable time_t d_ttd;
+ mutable time_t d_creation; // so we can 'age' our packets
uint32_t d_qhash;
uint32_t d_tag;
+ uint16_t d_type;
+ uint16_t d_class;
+ mutable uint16_t d_ecsBegin;
+ mutable uint16_t d_ecsEnd;
mutable vState d_vstate;
inline bool operator<(const struct Entry& rhs) const;
packetCache_t d_packetCache;
- bool checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage);
+ static bool qrMatch(const packetCache_t::index<HashTag>::type::iterator& iter, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint16_t ecsBegin, uint16_t ecsEnd);
+ bool checkResponseMatches(std::pair<packetCache_t::index<HashTag>::type::iterator, packetCache_t::index<HashTag>::type::iterator> range, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, RecProtoBufMessage* protobufMessage, uint16_t ecsBegin, uint16_t ecsEnd);
public:
void preRemoval(const Entry& entry)
test-misc_hh.cc \
test-mtasker.cc \
test-negcache_cc.cc \
+ test-packetcache_hh.cc \
test-rcpgenerator_cc.cc \
test-recpacketcache_cc.cc \
test-recursorcache_cc.cc \
--- /dev/null
+../test-packetcache_hh.cc
\ No newline at end of file
--- /dev/null
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <boost/test/unit_test.hpp>
+
+#include "dnswriter.hh"
+#include "dnsrecords.hh"
+#include "ednscookies.hh"
+#include "ednssubnet.hh"
+#include "packetcache.hh"
+
+BOOST_AUTO_TEST_SUITE(packetcache_hh)
+
+BOOST_AUTO_TEST_CASE(test_PacketCacheAuthCollision) {
+
+ /* auth version (ECS is not processed, we just hash the whole query except for the ID, while lowercasing the qname) */
+ const DNSName qname("www.powerdns.com.");
+ uint16_t qtype = QType::AAAA;
+ EDNSSubnetOpts opt;
+ DNSPacketWriter::optvect_t ednsOptions;
+
+ {
+ /* same query, different IDs */
+ vector<uint8_t> packet;
+ DNSPacketWriter pw1(packet, qname, qtype);
+ pw1.getHeader()->rd = true;
+ pw1.getHeader()->qr = false;
+ pw1.getHeader()->id = 0x42;
+ string spacket1((const char*)&packet[0], packet.size());
+ auto hash1 = PacketCache::canHashPacket(spacket1);
+
+ packet.clear();
+ DNSPacketWriter pw2(packet, qname, qtype);
+ pw2.getHeader()->rd = true;
+ pw2.getHeader()->qr = false;
+ pw2.getHeader()->id = 0x84;
+ string spacket2((const char*)&packet[0], packet.size());
+ auto hash2 = PacketCache::canHashPacket(spacket2);
+
+ BOOST_CHECK_EQUAL(hash1, hash2);
+ BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname));
+ }
+
+ {
+ /* same query, different IDs, different ECS, still hashes to the same value */
+ vector<uint8_t> packet;
+ DNSPacketWriter pw1(packet, qname, qtype);
+ pw1.getHeader()->rd = true;
+ pw1.getHeader()->qr = false;
+ pw1.getHeader()->id = 0x42;
+ opt.source = Netmask("10.0.18.199/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pw1.addOpt(512, 0, 0, ednsOptions);
+ pw1.commit();
+
+ string spacket1((const char*)&packet[0], packet.size());
+ auto hash1 = PacketCache::canHashPacket(spacket1);
+
+ packet.clear();
+ DNSPacketWriter pw2(packet, qname, qtype);
+ pw2.getHeader()->rd = true;
+ pw2.getHeader()->qr = false;
+ pw2.getHeader()->id = 0x84;
+ opt.source = Netmask("10.0.131.66/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pw2.addOpt(512, 0, 0, ednsOptions);
+ pw2.commit();
+
+ string spacket2((const char*)&packet[0], packet.size());
+ auto hash2 = PacketCache::canHashPacket(spacket2);
+
+ BOOST_CHECK_EQUAL(hash1, hash2);
+ /* the hash is the same but we should _not_ match */
+ BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname));
+ }
+
+ {
+ /* same query but one has DNSSECOK, not the other, different IDs, different ECS, still hashes to the same value */
+ vector<uint8_t> packet;
+ DNSPacketWriter pw1(packet, qname, qtype);
+ pw1.getHeader()->rd = true;
+ pw1.getHeader()->qr = false;
+ pw1.getHeader()->id = 0x42;
+ opt.source = Netmask("47.8.0.0/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pw1.addOpt(512, 0, EDNSOpts::DNSSECOK, ednsOptions);
+ pw1.commit();
+
+ string spacket1((const char*)&packet[0], packet.size());
+ auto hash1 = PacketCache::canHashPacket(spacket1);
+
+ packet.clear();
+ DNSPacketWriter pw2(packet, qname, qtype);
+ pw2.getHeader()->rd = true;
+ pw2.getHeader()->qr = false;
+ pw2.getHeader()->id = 0x84;
+ opt.source = Netmask("18.43.1.0/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ /* no EDNSOpts::DNSSECOK !! */
+ pw2.addOpt(512, 0, 0, ednsOptions);
+ pw2.commit();
+
+ string spacket2((const char*)&packet[0], packet.size());
+ auto hash2 = PacketCache::canHashPacket(spacket2);
+
+ BOOST_CHECK_EQUAL(hash1, hash2);
+ /* the hash is the same but we should _not_ match */
+ BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname));
+ }
+
+ {
+ /* same query but different cookies, still hashes to the same value */
+ vector<uint8_t> packet;
+ DNSPacketWriter pw1(packet, qname, qtype);
+ pw1.getHeader()->rd = true;
+ pw1.getHeader()->qr = false;
+ pw1.getHeader()->id = 0x42;
+ opt.source = Netmask("192.0.2.1/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ EDNSCookiesOpt cookiesOpt;
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ cookiesOpt.server[4] = -42;
+ cookiesOpt.server[5] = -6;
+ cookiesOpt.server[6] = 1;
+ cookiesOpt.server[7] = 0;
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::COOKIE, makeEDNSCookiesOptString(cookiesOpt)));
+ pw1.addOpt(512, 0, EDNSOpts::DNSSECOK, ednsOptions);
+ pw1.commit();
+
+ string spacket1((const char*)&packet[0], packet.size());
+ auto hash1 = PacketCache::canHashPacket(spacket1);
+
+ packet.clear();
+ DNSPacketWriter pw2(packet, qname, qtype);
+ pw2.getHeader()->rd = true;
+ pw2.getHeader()->qr = false;
+ pw2.getHeader()->id = 0x84;
+ opt.source = Netmask("192.0.2.1/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ cookiesOpt.server[4] = 29;
+ cookiesOpt.server[5] = -79;
+ cookiesOpt.server[6] = 1;
+ cookiesOpt.server[7] = 0;
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::COOKIE, makeEDNSCookiesOptString(cookiesOpt)));
+ pw2.addOpt(512, 0, EDNSOpts::DNSSECOK, ednsOptions);
+ pw2.commit();
+
+ string spacket2((const char*)&packet[0], packet.size());
+ auto hash2 = PacketCache::canHashPacket(spacket2);
+
+ BOOST_CHECK_EQUAL(hash1, hash2);
+ /* the hash is the same but we should _not_ match */
+ BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_PacketCacheRecCollision) {
+
+ /* rec version (ECS is processed, we hash the whole query except for the ID and the ECS value, while lowercasing the qname) */
+ const DNSName qname("www.powerdns.com.");
+ uint16_t qtype = QType::AAAA;
+ EDNSSubnetOpts opt;
+ DNSPacketWriter::optvect_t ednsOptions;
+ uint16_t ecsBegin;
+ uint16_t ecsEnd;
+
+ {
+ /* same query, different IDs */
+ vector<uint8_t> packet;
+ DNSPacketWriter pw1(packet, qname, qtype);
+ pw1.getHeader()->rd = true;
+ pw1.getHeader()->qr = false;
+ pw1.getHeader()->id = 0x42;
+ string spacket1((const char*)&packet[0], packet.size());
+ auto hash1 = PacketCache::canHashPacket(spacket1, &ecsBegin, &ecsEnd);
+ /* no ECS */
+ BOOST_CHECK_EQUAL(ecsBegin, 0);
+ BOOST_CHECK_EQUAL(ecsEnd, 0);
+
+ packet.clear();
+ DNSPacketWriter pw2(packet, qname, qtype);
+ pw2.getHeader()->rd = true;
+ pw2.getHeader()->qr = false;
+ pw2.getHeader()->id = 0x84;
+ string spacket2((const char*)&packet[0], packet.size());
+ auto hash2 = PacketCache::canHashPacket(spacket2, &ecsBegin, &ecsEnd);
+ /* no ECS */
+ BOOST_CHECK_EQUAL(ecsBegin, 0);
+ BOOST_CHECK_EQUAL(ecsEnd, 0);
+
+ BOOST_CHECK_EQUAL(hash1, hash2);
+ BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, ecsBegin, ecsEnd));
+ }
+
+ {
+ /* same query, different IDs, different ECS, still hashes to the same value */
+ vector<uint8_t> packet;
+ DNSPacketWriter pw1(packet, qname, qtype);
+ pw1.getHeader()->rd = true;
+ pw1.getHeader()->qr = false;
+ pw1.getHeader()->id = 0x42;
+ opt.source = Netmask("10.0.18.199/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pw1.addOpt(512, 0, 0, ednsOptions);
+ pw1.commit();
+
+ string spacket1((const char*)&packet[0], packet.size());
+ auto hash1 = PacketCache::canHashPacket(spacket1, &ecsBegin, &ecsEnd);
+ /* ECS value */
+ BOOST_CHECK_EQUAL(ecsBegin, sizeof(dnsheader) + qname.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE);
+ BOOST_CHECK_EQUAL(ecsEnd, ecsBegin + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
+
+ packet.clear();
+ DNSPacketWriter pw2(packet, qname, qtype);
+ pw2.getHeader()->rd = true;
+ pw2.getHeader()->qr = false;
+ pw2.getHeader()->id = 0x84;
+ opt.source = Netmask("10.0.131.66/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ pw2.addOpt(512, 0, 0, ednsOptions);
+ pw2.commit();
+
+ string spacket2((const char*)&packet[0], packet.size());
+ auto hash2 = PacketCache::canHashPacket(spacket2, &ecsBegin, &ecsEnd);
+ /* ECS value */
+ BOOST_CHECK_EQUAL(ecsBegin, sizeof(dnsheader) + qname.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE);
+ BOOST_CHECK_EQUAL(ecsEnd, ecsBegin + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
+
+ BOOST_CHECK_EQUAL(hash1, hash2);
+ /* the hash is the same and we don't hash the ECS so we should match */
+ BOOST_CHECK(PacketCache::queryMatches(spacket1, spacket2, qname, ecsBegin, ecsEnd));
+ }
+
+ {
+ /* same query but different cookies, still hashes to the same value */
+ vector<uint8_t> packet;
+ DNSPacketWriter pw1(packet, qname, qtype);
+ pw1.getHeader()->rd = true;
+ pw1.getHeader()->qr = false;
+ pw1.getHeader()->id = 0x42;
+ opt.source = Netmask("192.0.2.1/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ EDNSCookiesOpt cookiesOpt;
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ cookiesOpt.server[4] = -20;
+ cookiesOpt.server[5] = -114;
+ cookiesOpt.server[6] = 0;
+ cookiesOpt.server[7] = 0;
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::COOKIE, makeEDNSCookiesOptString(cookiesOpt)));
+ pw1.addOpt(512, 0, EDNSOpts::DNSSECOK, ednsOptions);
+ pw1.commit();
+
+ string spacket1((const char*)&packet[0], packet.size());
+ auto hash1 = PacketCache::canHashPacket(spacket1, &ecsBegin, &ecsEnd);
+ /* ECS value */
+ BOOST_CHECK_EQUAL(ecsBegin, sizeof(dnsheader) + qname.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE);
+ BOOST_CHECK_EQUAL(ecsEnd, ecsBegin + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
+
+ packet.clear();
+ DNSPacketWriter pw2(packet, qname, qtype);
+ pw2.getHeader()->rd = true;
+ pw2.getHeader()->qr = false;
+ pw2.getHeader()->id = 0x84;
+ opt.source = Netmask("192.0.2.1/32");
+ ednsOptions.clear();
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(opt)));
+ cookiesOpt.client = string("deadbeef");
+ cookiesOpt.server = string("deadbeef");
+ cookiesOpt.server[4] = 103;
+ cookiesOpt.server[5] = 68;
+ cookiesOpt.server[6] = 0;
+ cookiesOpt.server[7] = 0;
+ ednsOptions.push_back(std::make_pair(EDNSOptionCode::COOKIE, makeEDNSCookiesOptString(cookiesOpt)));
+ pw2.addOpt(512, 0, EDNSOpts::DNSSECOK, ednsOptions);
+ pw2.commit();
+
+ string spacket2((const char*)&packet[0], packet.size());
+ auto hash2 = PacketCache::canHashPacket(spacket2, &ecsBegin, &ecsEnd);
+ /* ECS value */
+ BOOST_CHECK_EQUAL(ecsBegin, sizeof(dnsheader) + qname.wirelength() + ( 2 * sizeof(uint16_t)) /* qtype */ + (2 * sizeof(uint16_t)) /* qclass */ + /* OPT root label */ 1 + sizeof(uint32_t) /* TTL */ + DNS_RDLENGTH_SIZE);
+ BOOST_CHECK_EQUAL(ecsEnd, ecsBegin + EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE + 2 /* family */ + 1 /* scope length */ + 1 /* source length */ + 4 /* IPv4 */);
+
+ BOOST_CHECK_EQUAL(hash1, hash2);
+ /* the hash is the same but we should _not_ match, even though we skip the ECS part, because the cookies are different */
+ BOOST_CHECK(!PacketCache::queryMatches(spacket1, spacket2, qname, ecsBegin, ecsEnd));
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
pw.commit();
string rpacket((const char*)&packet[0], packet.size());
- rpc.insertResponsePacket(tag, qhash, qname, QType::A, QClass::IN, rpacket, time(0), ttd);
+ rpc.insertResponsePacket(tag, qhash, qpacket, qname, QType::A, QClass::IN, rpacket, time(0), ttd, 0, 0);
BOOST_CHECK_EQUAL(rpc.size(), 1);
rpc.doPruneTo(0);
BOOST_CHECK_EQUAL(rpc.size(), 0);
- rpc.insertResponsePacket(tag, qhash, qname, QType::A, QClass::IN, rpacket, time(0), ttd);
+ rpc.insertResponsePacket(tag, qhash, qpacket, qname, QType::A, QClass::IN, rpacket, time(0), ttd, 0, 0);
BOOST_CHECK_EQUAL(rpc.size(), 1);
rpc.doWipePacketCache(qname);
BOOST_CHECK_EQUAL(rpc.size(), 0);
- rpc.insertResponsePacket(tag, qhash, qname, QType::A, QClass::IN, rpacket, time(0), ttd);
+ rpc.insertResponsePacket(tag, qhash, qpacket, qname, QType::A, QClass::IN, rpacket, time(0), ttd, 0, 0);
+ BOOST_CHECK_EQUAL(rpc.size(), 1);
uint32_t qhash2 = 0;
bool found = rpc.getResponsePacket(tag, qpacket, time(nullptr), &fpacket, &age, &qhash2);
BOOST_CHECK_EQUAL(found, true);
BOOST_CHECK(r1packet != r2packet);
/* inserting a response for tag1 */
- rpc.insertResponsePacket(tag1, qhash, qname, QType::A, QClass::IN, r1packet, time(0), ttd);
+ rpc.insertResponsePacket(tag1, qhash, qpacket, qname, QType::A, QClass::IN, r1packet, time(0), ttd, 0, 0);
BOOST_CHECK_EQUAL(rpc.size(), 1);
/* inserting a different response for tag2, should not override the first one */
- rpc.insertResponsePacket(tag2, qhash, qname, QType::A, QClass::IN, r2packet, time(0), ttd);
+ rpc.insertResponsePacket(tag2, qhash, qpacket, qname, QType::A, QClass::IN, r2packet, time(0), ttd, 0, 0);
BOOST_CHECK_EQUAL(rpc.size(), 2);
/* remove all responses from the cache */
BOOST_CHECK_EQUAL(rpc.size(), 0);
/* reinsert both */
- rpc.insertResponsePacket(tag1, qhash, qname, QType::A, QClass::IN, r1packet, time(0), ttd);
+ rpc.insertResponsePacket(tag1, qhash, qpacket, qname, QType::A, QClass::IN, r1packet, time(0), ttd, 0, 0);
BOOST_CHECK_EQUAL(rpc.size(), 1);
- rpc.insertResponsePacket(tag2, qhash, qname, QType::A, QClass::IN, r2packet, time(0), ttd);
+ rpc.insertResponsePacket(tag2, qhash, qpacket, qname, QType::A, QClass::IN, r2packet, time(0), ttd, 0, 0);
BOOST_CHECK_EQUAL(rpc.size(), 2);
/* remove the responses by qname, should remove both */
BOOST_CHECK_EQUAL(rpc.size(), 0);
/* insert the response for tag1 */
- rpc.insertResponsePacket(tag1, qhash, qname, QType::A, QClass::IN, r1packet, time(0), ttd);
+ rpc.insertResponsePacket(tag1, qhash, qpacket, qname, QType::A, QClass::IN, r1packet, time(0), ttd, 0, 0);
BOOST_CHECK_EQUAL(rpc.size(), 1);
/* we can retrieve it */
BOOST_CHECK_EQUAL(temphash, qhash);
/* adding a response for the second tag */
- rpc.insertResponsePacket(tag2, qhash, qname, QType::A, QClass::IN, r2packet, time(0), ttd);
+ rpc.insertResponsePacket(tag2, qhash, qpacket, qname, QType::A, QClass::IN, r2packet, time(0), ttd, 0, 0);
BOOST_CHECK_EQUAL(rpc.size(), 2);
/* We still get the correct response for the first tag */