Skip to content

Commit

Permalink
Merge pull request #6713 from mind04/bogus-ring
Browse files Browse the repository at this point in the history
rec: add bogus ring to make it more easy to detect high profile domains with broken dnssec
  • Loading branch information
rgacogne committed Jun 21, 2018
2 parents e6f7624 + 4d3f74e commit c5c9ae7
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 23 deletions.
25 changes: 21 additions & 4 deletions pdns/pdns_recursor.cc
Expand Up @@ -113,8 +113,8 @@ thread_local std::unique_ptr<MT_t> MT; // the big MTasker
thread_local std::unique_ptr<MemRecursorCache> t_RC;
thread_local std::unique_ptr<RecursorPacketCache> t_packetCache;
thread_local FDMultiplexer* t_fdm{nullptr};
thread_local std::unique_ptr<addrringbuf_t> t_remotes, t_servfailremotes, t_largeanswerremotes;
thread_local std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > > t_queryring, t_servfailqueryring;
thread_local std::unique_ptr<addrringbuf_t> t_remotes, t_servfailremotes, t_largeanswerremotes, t_bogusremotes;
thread_local std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > > t_queryring, t_servfailqueryring, t_bogusqueryring;
thread_local std::shared_ptr<NetmaskGroup> t_allowFrom;
#ifdef HAVE_PROTOBUF
thread_local std::unique_ptr<boost::uuids::random_generator> t_uuidGenerator;
Expand Down Expand Up @@ -1238,6 +1238,10 @@ static void startDoResolve(void *p)
pw.getHeader()->ad=0;
}
else if(state == Bogus) {
if(t_bogusremotes)
t_bogusremotes->push_back(dc->d_source);
if(t_bogusqueryring)
t_bogusqueryring->push_back(make_pair(dc->d_mdp.d_qname, dc->d_mdp.d_qtype));
if(g_dnssecLogBogus || sr.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
g_log<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->getRemote()<<" validates as Bogus"<<endl;
}
Expand Down Expand Up @@ -1353,6 +1357,7 @@ static void startDoResolve(void *p)
g_now.tv_sec,
pw.getHeader()->rcode == RCode::ServFail ? SyncRes::s_packetcacheservfailttl :
min(minTTL,SyncRes::s_packetcachettl),
dq.validationState,
pbMessage);
}
// else cerr<<"Not putting in packet cache: "<<sr.wasVariable()<<endl;
Expand Down Expand Up @@ -1911,14 +1916,22 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr
/* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
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, &qhash, pbMessage ? &(*pbMessage) : nullptr));
cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, qtype, qclass, g_now.tv_sec, &response, &age, &valState, &qhash, pbMessage ? &(*pbMessage) : nullptr));
}
else {
cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, g_now.tv_sec, &response, &age, &qhash, pbMessage ? &(*pbMessage) : nullptr));
cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, &qtype, &qclass, g_now.tv_sec, &response, &age, &valState, &qhash, pbMessage ? &(*pbMessage) : nullptr));
}

if (cacheHit) {
if(valState == Bogus) {
if(t_bogusremotes)
t_bogusremotes->push_back(source);
if(t_bogusqueryring)
t_bogusqueryring->push_back(make_pair(qname, qtype));
}

#ifdef HAVE_PROTOBUF
if(t_protobufServer && (!luaconfsLocal->protobufTaggedOnly || !pbMessage->getAppliedPolicy().empty() || !pbMessage->getPolicyTags().empty())) {
Netmask requestorNM(source, source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
Expand Down Expand Up @@ -3503,13 +3516,17 @@ try
t_remotes->set_capacity(ringsize);
t_servfailremotes = std::unique_ptr<addrringbuf_t>(new addrringbuf_t());
t_servfailremotes->set_capacity(ringsize);
t_bogusremotes = std::unique_ptr<addrringbuf_t>(new addrringbuf_t());
t_bogusremotes->set_capacity(ringsize);
t_largeanswerremotes = std::unique_ptr<addrringbuf_t>(new addrringbuf_t());
t_largeanswerremotes->set_capacity(ringsize);

t_queryring = std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > >(new boost::circular_buffer<pair<DNSName, uint16_t> >());
t_queryring->set_capacity(ringsize);
t_servfailqueryring = std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > >(new boost::circular_buffer<pair<DNSName, uint16_t> >());
t_servfailqueryring->set_capacity(ringsize);
t_bogusqueryring = std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t> > >(new boost::circular_buffer<pair<DNSName, uint16_t> >());
t_bogusqueryring->set_capacity(ringsize);
}

MT=std::unique_ptr<MTasker<PacketID,string> >(new MTasker<PacketID,string>(::arg().asNum("stack-size")));
Expand Down
2 changes: 2 additions & 0 deletions pdns/rec_channel.hh
Expand Up @@ -68,8 +68,10 @@ extern pthread_mutex_t g_carbon_config_lock;
void sortPublicSuffixList();
std::vector<std::pair<DNSName, uint16_t> >* pleaseGetQueryRing();
std::vector<std::pair<DNSName, uint16_t> >* pleaseGetServfailQueryRing();
std::vector<std::pair<DNSName, uint16_t> >* pleaseGetBogusQueryRing();
std::vector<ComboAddress>* pleaseGetRemotes();
std::vector<ComboAddress>* pleaseGetServfailRemotes();
std::vector<ComboAddress>* pleaseGetBogusRemotes();
std::vector<ComboAddress>* pleaseGetLargeAnswerRemotes();
DNSName getRegisteredName(const DNSName& dom);
std::atomic<unsigned long>* getDynMetric(const std::string& str);
Expand Down
36 changes: 36 additions & 0 deletions pdns/rec_channel_rec.cc
Expand Up @@ -1033,6 +1033,18 @@ vector<pair<DNSName,uint16_t> >* pleaseGetServfailQueryRing()
}
return ret;
}
vector<pair<DNSName,uint16_t> >* pleaseGetBogusQueryRing()
{
typedef pair<DNSName,uint16_t> query_t;
vector<query_t>* ret = new vector<query_t>();
if(!t_bogusqueryring)
return ret;
ret->reserve(t_bogusqueryring->size());
for(const query_t& q : *t_bogusqueryring) {
ret->push_back(q);
}
return ret;
}



Expand Down Expand Up @@ -1064,6 +1076,18 @@ vector<ComboAddress>* pleaseGetServfailRemotes()
return ret;
}

vector<ComboAddress>* pleaseGetBogusRemotes()
{
vector<ComboAddress>* ret = new vector<ComboAddress>();
if(!t_bogusremotes)
return ret;
ret->reserve(t_bogusremotes->size());
for(const ComboAddress& ca : *t_bogusremotes) {
ret->push_back(ca);
}
return ret;
}

vector<ComboAddress>* pleaseGetLargeAnswerRemotes()
{
vector<ComboAddress>* ret = new vector<ComboAddress>();
Expand Down Expand Up @@ -1252,8 +1276,11 @@ string RecursorControlParser::getAnswer(const string& question, RecursorControlP
"top-pub-queries show top queries grouped by public suffix list\n"
"top-remotes show top remotes\n"
"top-servfail-queries show top queries receiving servfail answers\n"
"top-bogus-queries show top queries validating as bogus\n"
"top-pub-servfail-queries show top queries receiving servfail answers grouped by public suffix list\n"
"top-pub-bogus-queries show top queries validating as bogus grouped by public suffix list\n"
"top-servfail-remotes show top remotes receiving servfail answers\n"
"top-bogus-remotes show top remotes receiving bogus answers\n"
"unload-lua-script unload Lua script\n"
"version return Recursor version number\n"
"wipe-cache domain0 [domain1] .. wipe domain data from cache\n";
Expand Down Expand Up @@ -1367,10 +1394,19 @@ string RecursorControlParser::getAnswer(const string& question, RecursorControlP
if(cmd=="top-pub-servfail-queries")
return doGenericTopQueries(pleaseGetServfailQueryRing, getRegisteredName);

if(cmd=="top-bogus-queries")
return doGenericTopQueries(pleaseGetBogusQueryRing);

if(cmd=="top-pub-bogus-queries")
return doGenericTopQueries(pleaseGetBogusQueryRing, getRegisteredName);


if(cmd=="top-servfail-remotes")
return doGenericTopRemotes(pleaseGetServfailRemotes);

if(cmd=="top-bogus-remotes")
return doGenericTopRemotes(pleaseGetBogusRemotes);

if(cmd=="top-largeanswer-remotes")
return doGenericTopRemotes(pleaseGetLargeAnswerRemotes);

Expand Down
32 changes: 19 additions & 13 deletions pdns/recpacketcache.cc
Expand Up @@ -45,7 +45,7 @@ static bool qrMatch(const DNSName& qname, uint16_t qtype, uint16_t qclass, const
return qname==rname && rtype == qtype && rclass == qclass;
}

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, 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)
{
for(auto iter = range.first ; iter != range.second ; ++ iter) {
// the possibility is VERY real that we get hits that are not right - birthday paradox
Expand All @@ -55,6 +55,7 @@ bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<Ha
*age = static_cast<uint32_t>(now - iter->d_creation);
*responsePacket = iter->d_packet;
responsePacket->replace(0, 2, queryPacket.c_str(), 2);
*valState = iter->d_vstate;

string::size_type i=sizeof(dnsheader);

Expand Down Expand Up @@ -94,17 +95,21 @@ bool RecursorPacketCache::checkResponseMatches(std::pair<packetCache_t::index<Ha
bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
std::string* responsePacket, uint32_t* age, uint32_t* qhash)
{
return getResponsePacket(tag, queryPacket, now, responsePacket, age, qhash, nullptr);
DNSName qname;
uint16_t qtype, qclass;
vState valState;
return getResponsePacket(tag, queryPacket, qname, &qtype, &qclass, now, responsePacket, age, &valState, qhash, 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)
{
return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, qhash, nullptr);
vState valState;
return getResponsePacket(tag, queryPacket, qname, qtype, qclass, now, responsePacket, age, &valState, qhash, 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, RecProtoBufMessage* protobufMessage)
std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
{
*qhash = canHashPacket(queryPacket, true);
const auto& idx = d_packetCache.get<HashTag>();
Expand All @@ -114,12 +119,11 @@ bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string&
d_misses++;
return false;
}

return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, protobufMessage);
return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, valState, protobufMessage);
}

bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
std::string* responsePacket, uint32_t* age, uint32_t* qhash, RecProtoBufMessage* protobufMessage)
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)
{
*qhash = canHashPacket(queryPacket, true);
const auto& idx = d_packetCache.get<HashTag>();
Expand All @@ -130,20 +134,20 @@ bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string&
return false;
}

uint16_t qtype, qclass;
DNSName qname(queryPacket.c_str(), queryPacket.length(), sizeof(dnsheader), false, &qtype, &qclass, 0);
qname = DNSName(queryPacket.c_str(), queryPacket.length(), sizeof(dnsheader), false, qtype, qclass, 0);

return checkResponseMatches(range, queryPacket, qname, qtype, qclass, now, responsePacket, age, protobufMessage);
return checkResponseMatches(range, queryPacket, qname, *qtype, *qclass, now, responsePacket, age, valState, protobufMessage);
}


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)
{
vState valState;
boost::optional<RecProtoBufMessage> pb(boost::none);
insertResponsePacket(tag, qhash, qname, qtype, qclass, responsePacket, now, ttl, pb);
insertResponsePacket(tag, qhash, qname, qtype, qclass, responsePacket, now, ttl, valState, 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 boost::optional<RecProtoBufMessage>& protobufMessage)
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)
{
auto& idx = d_packetCache.get<HashTag>();
auto range = idx.equal_range(tie(tag,qhash));
Expand All @@ -157,6 +161,7 @@ void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash,
iter->d_packet = responsePacket;
iter->d_ttd = now + ttl;
iter->d_creation = now;
iter->d_vstate = valState;
#ifdef HAVE_PROTOBUF
if (protobufMessage) {
iter->d_protobufMessage = *protobufMessage;
Expand All @@ -174,6 +179,7 @@ void RecursorPacketCache::insertResponsePacket(unsigned int tag, uint32_t qhash,
e.d_ttd = now+ttl;
e.d_creation = now;
e.d_tag = tag;
e.d_vstate = valState;
#ifdef HAVE_PROTOBUF
if (protobufMessage) {
e.d_protobufMessage = *protobufMessage;
Expand Down
10 changes: 6 additions & 4 deletions pdns/recpacketcache.hh
Expand Up @@ -33,6 +33,7 @@
#include <boost/multi_index/sequenced_index.hpp>

#include "packetcache.hh"
#include "validate.hh"

#ifdef HAVE_CONFIG_H
#include "config.h"
Expand All @@ -52,11 +53,11 @@ class RecursorPacketCache: public PacketCache
public:
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, time_t now, std::string* responsePacket, uint32_t* age, uint32_t* qhash, 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, 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, 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, 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 boost::optional<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, const vState& valState, 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);
Expand Down Expand Up @@ -86,6 +87,7 @@ private:
#endif
uint32_t d_qhash;
uint32_t d_tag;
mutable vState d_vstate;
inline bool operator<(const struct Entry& rhs) const;

time_t getTTD() const
Expand All @@ -105,7 +107,7 @@ private:

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, RecProtoBufMessage* protobufMessage);
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);

public:
void preRemoval(const Entry& entry)
Expand Down
13 changes: 13 additions & 0 deletions pdns/recursordist/docs/manpages/rec_control.1.rst
Expand Up @@ -183,15 +183,28 @@ top-servfail-queries
Shows the top-20 queries causing servfail responses. Statistics are over
the last 'stats-ringbuffer-entries' queries.

top-bogus-queries
Shows the top-20 queries causing bogus responses. Statistics are over
the last 'stats-ringbuffer-entries' queries.

top-pub-servfail-queries
Shows the top-20 queries causing servfail responses grouped by public
suffix list. Statistics are over the last 'stats-ringbuffer-entries'
queries.

top-pub-bogus-queries
Shows the top-20 queries causing bogus responses grouped by public
suffix list. Statistics are over the last 'stats-ringbuffer-entries'
queries.

top-servfail-remotes
Shows the top-20 most active remote hosts causing servfail responses.
Statistics are over the last 'stats-ringbuffer-entries' queries.

top-bogus-remotes
Shows the top-20 most active remote hosts causing bogus responses.
Statistics are over the last 'stats-ringbuffer-entries' queries.

trace-regex *REGEX*
Emit resolution trace for matching queries. Empty regex to disable trace.

Expand Down

0 comments on commit c5c9ae7

Please sign in to comment.