From: bert hubert Date: Tue, 10 Nov 2015 14:47:18 +0000 (+0100) Subject: edns subnet for the cache. All hooked up, untested. X-Git-Tag: dnsdist-1.0.0-alpha1~230^2~8 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=376effcf776cfa2379f41b354fc08516731fd12c;p=pdns edns subnet for the cache. All hooked up, untested. --- diff --git a/pdns/ednssubnet.cc b/pdns/ednssubnet.cc index ed9084f2e..b84be9f2e 100644 --- a/pdns/ednssubnet.cc +++ b/pdns/ednssubnet.cc @@ -1,6 +1,6 @@ /* PowerDNS Versatile Database Driven Nameserver - Copyright (C) 2011 Netherlabs Computer Consulting BV + Copyright (C) 2015 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 @@ -92,3 +92,30 @@ string makeEDNSSubnetOptsString(const EDNSSubnetOpts& eso) return ret; } +boost::optional getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem) +{ + if(local.sin4.sin_family != AF_INET || local.sin4.sin_addr.s_addr) { // detect unset 'requestor' + if(g_ednsdomains.check(dn) || g_ednssubnets.match(rem)) { + int bits =local.sin4.sin_family == AF_INET ? 24 : 64; + ComboAddress trunc(local); + trunc.truncate(bits); + return boost::optional(Netmask(trunc, bits)); + } + } + return boost::optional(); +} + +void parseEDNSSubnetWhitelist(const std::string& wlist) +{ + vector parts; + stringtok(parts, wlist, ",;"); + for(const auto& a : parts) { + try { + Netmask nm(a); + g_ednssubnets.addMask(nm); + } + catch(...) { + g_ednsdomains.add(DNSName(a)); + } + } +} diff --git a/pdns/ednssubnet.hh b/pdns/ednssubnet.hh index 15c633bcf..dbdd5c151 100644 --- a/pdns/ednssubnet.hh +++ b/pdns/ednssubnet.hh @@ -24,6 +24,11 @@ #include "namespaces.hh" #include "iputils.hh" +#include "dnsname.hh" + +extern NetmaskGroup g_ednssubnets; +extern SuffixMatchNode g_ednsdomains; + struct EDNSSubnetOpts { @@ -33,5 +38,6 @@ struct EDNSSubnetOpts bool getEDNSSubnetOptsFromString(const string& options, EDNSSubnetOpts* eso); string makeEDNSSubnetOptsString(const EDNSSubnetOpts& eso); - +boost::optional getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem); +void parseEDNSSubnetWhitelist(const std::string& wlist); #endif diff --git a/pdns/iputils.hh b/pdns/iputils.hh index a461e0f08..b035809ea 100644 --- a/pdns/iputils.hh +++ b/pdns/iputils.hh @@ -386,12 +386,18 @@ public: return match(&ip); } - //! Add this Netmask to the list of possible matches + //! Add this string to the list of possible matches void addMask(const string &ip) { d_masks.push_back(Netmask(ip)); } + //! Add this Netmask to the list of possible matches + void addMask(const Netmask& nm) + { + d_masks.push_back(nm); + } + void clear() { d_masks.clear(); diff --git a/pdns/lwres.cc b/pdns/lwres.cc index 1d30ca127..dbe669b3c 100644 --- a/pdns/lwres.cc +++ b/pdns/lwres.cc @@ -53,7 +53,7 @@ /** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors Never throws! */ -int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, LWResult *lwr) +int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional& srcmask, LWResult *lwr) { int len; int bufsize=1500; @@ -69,9 +69,13 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d if(EDNS0Level && !doTCP) { DNSPacketWriter::optvect_t opts; - EDNSSubnetOpts eo; - eo.source = Netmask("2001:470:1f0b:27e:1850:ae41:cc31:7765"); - opts.push_back(make_pair(8, makeEDNSSubnetOptsString(eo))); + if(srcmask) { + EDNSSubnetOpts eo; + eo.source = *srcmask; + cout<<"Adding request mask: "<(); // this is also our return value + } pw.addOpt(1200, 0, EDNSOpts::DNSSECOK, opts); // 1200 bytes answer size pw.commit(); @@ -180,6 +184,18 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d EDNSOpts edo; if(EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) { lwr->d_haveEDNS = true; + + for(const auto& opt : edo.d_options) { + if(opt.first==8) { + EDNSSubnetOpts reso; + if(getEDNSSubnetOptsFromString(opt.second, &reso)) { + cerr<<"EDNS Subnet response: "<* res, vector>* signatures) +int MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, vector* res, const ComboAddress& who, vector>* signatures) { unsigned int ttd=0; // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n"; @@ -53,7 +53,16 @@ int MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, vec ) { ttd = i->d_ttd; - for(auto k=i->d_records.begin(); k != i->d_records.end(); ++k) { + auto records = &i->d_records; + if(!i->d_subnetspecific.empty()) { + for(const auto& p : i->d_subnetspecific) { + if(p.first.match(who)) { + records = &p.second; + break; + } + } + } + for(auto k=records->begin(); k != records->end(); ++k) { if(res) { DNSRecord dr; dr.d_name = qname; @@ -114,8 +123,11 @@ bool MemRecursorCache::attemptToRefreshNSTTL(const QType& qt, const vector& content, const vector>& signatures, bool auth) +void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, bool auth, boost::optional ednsmask) { + if(ednsmask) { + cerr<<"This data is actually subnet mask specific!!"< key=boost::make_tuple(qname, qt.getCode()); cache_t::iterator stored=d_cache.find(key); @@ -131,7 +143,27 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt // cerr<<"asked to store "<< qname<<"|"+qt.getName()<<" -> '"<d_content->getZoneRepresentation()<<"', auth="<second; + } + + } + records->clear(); if(!auth && ce.d_auth) { // unauth data came in, we have some auth data, but is it fresh? if(ce.d_ttd > now) { // we still have valid data, ignore unauth data @@ -152,7 +184,7 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt // make sure that we CAN refresh the root if(auth && ((qname == DNSName()) || !attemptToRefreshNSTTL(qt, content, ce) ) ) { // cerr<<"\tGot auth data, and it was not refresh attempt of an unchanged NS set, nuking storage"<clear(); // clear non-auth data ce.d_auth = true; } // else cerr<<"\tNot nuking"<content<<" with ttl/ttd "<ttl<d_ttl); // XXX this does weird things if TTLs differ in the set - ce.d_records.push_back(i->d_content); - - /* - else { - range=equal_range(ce.d_records.begin(), ce.d_records.end(), dr); - - if(range.first != range.second) { - // cerr<<"\t\tMay need to modify TTL of stored record\n"; - for(vector::iterator j=range.first ; j!=range.second; ++j) { - // see http://mailman.powerdns.com/pipermail/pdns-users/2006-May/003413.html - if(j->d_ttd > (unsigned int) now && i->ttl > j->d_ttd && qt.getCode()==QType::NS && auth) { // don't allow auth servers to *raise* TTL of an NS record - //~ cerr<<"\t\tNot doing so, trying to raise TTL NS\n"; - continue; - } - if(i->ttl > j->d_ttd || (auth) ) { // authoritative packets can override the TTL to be lower - //~ cerr<<"\t\tUpdating the ttl, diff="<d_ttd - i->ttl<d_ttd=i->ttl; - } - else { - //~ cerr<<"\t\tNOT updating the ttl, old= " <d_ttd - now <<", new: "<ttl - now <push_back(i->d_content); + // there was code here that did things with TTL and auth. Unsure if it was good. XXX } d_cache.replace(stored, ce); diff --git a/pdns/recursor_cache.hh b/pdns/recursor_cache.hh index 020787391..93d36bea1 100644 --- a/pdns/recursor_cache.hh +++ b/pdns/recursor_cache.hh @@ -32,9 +32,9 @@ public: } unsigned int size(); unsigned int bytes(); - int get(time_t, const DNSName &qname, const QType& qt, vector* res, vector>* signatures=0); + int get(time_t, const DNSName &qname, const QType& qt, vector* res, const ComboAddress& who, vector>* signatures=0); - void replace(time_t, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, bool auth); + void replace(time_t, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, bool auth, boost::optional ednsmask=boost::optional()); void doPrune(void); void doSlash(int perc); uint64_t doDump(int fd); diff --git a/pdns/syncres.cc b/pdns/syncres.cc index d4a696776..f3537e369 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -46,6 +46,7 @@ #include "dnsparser.hh" #include "dns_random.hh" #include "lock.hh" +#include "ednssubnet.hh" #include "cachecleaner.hh" __thread SyncRes::StaticStorage* t_sstorage; @@ -288,7 +289,7 @@ void SyncRes::doEDNSDumpAndClose(int fd) fclose(fp); } -int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, LWResult* res) +int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional& srcmask, LWResult* res) { /* what is your QUEST? the goal is to get as many remotes as possible on the highest level of EDNS support @@ -335,7 +336,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, EDNSLevel = 0; } - ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, res); + ret=asyncresolve(ip, domain, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, res); if(ret == 0 || ret < 0) { // cerr<< (ret < 0 ? "Transport error" : "Timeout")<<" for query to "< nm; + res=asyncresolveWrapper(remoteIP, qname, qtype.getCode(), false, false, &d_now, nm, &lwr); // filter out the good stuff from lwr.result() for(const auto& rec : lwr.d_records) { @@ -485,7 +487,7 @@ vector SyncRes::getAddrs(const DNSName &qname, int depth, set cset; - if(t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), &cset) > 0) { + if(t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), &cset, d_requestor) > 0) { for(auto k=cset.cbegin();k!=cset.cend();++k) { if(k->d_ttl > (unsigned int)d_now.tv_sec ) { ComboAddress ca=std::dynamic_pointer_cast(k->d_content)->getCA(53); @@ -534,7 +536,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto LOG(prefix< ns; *flawedNSSet = false; - if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), &ns) > 0) { + if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), &ns, d_requestor) > 0) { for(auto k=ns.cbegin();k!=ns.cend(); ++k) { if(k->d_ttl > (unsigned int)d_now.tv_sec ) { vector aset; @@ -542,7 +544,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto const DNSRecord& dr=*k; auto nrr = std::dynamic_pointer_cast(dr.d_content); if(!nrr->getNS().isPartOf(subdomain) || t_RC->get(d_now.tv_sec, nrr->getNS(), s_doIPv6 ? QType(QType::ADDR) : QType(QType::A), - doLog() ? &aset : 0) > 5) { + doLog() ? &aset : 0, d_requestor) > 5) { bestns.push_back(dr); LOG(prefix< '"<getNS()<<"'"<getNS().isPartOf(subdomain)); @@ -653,7 +655,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector LOG(prefix< cset; vector> signatures; - if(t_RC->get(d_now.tv_sec, qname,QType(QType::CNAME), &cset, &signatures) > 0) { + if(t_RC->get(d_now.tv_sec, qname,QType(QType::CNAME), &cset, d_requestor, &signatures) > 0) { for(auto j=cset.cbegin() ; j != cset.cend() ; ++j) { if(j->d_ttl>(unsigned int) d_now.tv_sec) { @@ -760,7 +762,7 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const QType &qtype, vector> signatures; uint32_t ttl=0; - if(t_RC->get(d_now.tv_sec, sqname, sqt, &cset, d_doDNSSEC ? &signatures : 0) > 0) { + if(t_RC->get(d_now.tv_sec, sqname, sqt, &cset, d_requestor, d_doDNSSEC ? &signatures : 0) > 0) { LOG(prefix<d_content->getZoneRepresentation()); @@ -909,6 +911,7 @@ int SyncRes::doResolveAt(set nameservers, DNSName auth, bool flawedNSSe int resolveret; bool pierceDontQuery=false; bool sendRDQuery=false; + boost::optional ednsmask; LWResult lwr; if(tns->empty()) { LOG(prefix< nameservers, DNSName auth, bool flawedNSSe if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, lwr.d_records, resolveret)) { LOG(prefix<getBits()) { + cerr<<"Actually got something back.. "<toString()< nameservers, DNSName auth, bool flawedNSSe // cout<<"Have "<second.records.size()<<" records and "<second.signatures.size()<<" signatures for "<first.first.toString(); // cout<<'|'<first.second.getCode())<replace(d_now.tv_sec, i->first.first, i->first.second, i->second.records, i->second.signatures, lwr.d_aabit); + t_RC->replace(d_now.tv_sec, i->first.first, i->first.second, i->second.records, i->second.signatures, lwr.d_aabit, ednsmask); } set nsset; LOG(prefix<& srcmask, LWResult* res); static void doEDNSDumpAndClose(int fd);