]> granicus.if.org Git - pdns/commitdiff
edns subnet for the cache. All hooked up, untested.
authorbert hubert <bert.hubert@netherlabs.nl>
Tue, 10 Nov 2015 14:47:18 +0000 (15:47 +0100)
committerbert hubert <bert.hubert@netherlabs.nl>
Tue, 10 Nov 2015 14:47:18 +0000 (15:47 +0100)
pdns/ednssubnet.cc
pdns/ednssubnet.hh
pdns/iputils.hh
pdns/lwres.cc
pdns/lwres.hh
pdns/pdns_recursor.cc
pdns/recursor_cache.cc
pdns/recursor_cache.hh
pdns/syncres.cc
pdns/syncres.hh

index ed9084f2e4d2f39a25a9622dec6f0df2477fdade..b84be9f2ee551fbe333696f235a81f873f24d5b3 100644 (file)
@@ -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<Netmask> 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>(Netmask(trunc, bits));
+    }
+  }
+  return boost::optional<Netmask>();
+}
+
+void  parseEDNSSubnetWhitelist(const std::string& wlist)
+{
+  vector<string> parts;
+  stringtok(parts, wlist, ",;");
+  for(const auto& a : parts) {
+    try {
+      Netmask nm(a);
+      g_ednssubnets.addMask(nm);
+    }
+    catch(...) {
+      g_ednsdomains.add(DNSName(a));
+    }
+  }
+}
index 15c633bcf1da988fe6878f244ccd12a939f2634b..dbdd5c151594a2c2c62a7a697f52b41f2e7b809e 100644 (file)
 
 #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<Netmask> getEDNSSubnetMask(const ComboAddress& local, const DNSName&dn, const ComboAddress& rem);
+void  parseEDNSSubnetWhitelist(const std::string& wlist);
 #endif
index a461e0f08c555b571c26954cb27716276c6a297a..b035809ea79e2f6b6fb9d8b074be525305395610 100644 (file)
@@ -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();
index 1d30ca127a6200956a94c8ffec19bf6ac434ed64..dbe669b3cbbda95a407cd6e95590dae0cfadf4e6 100644 (file)
@@ -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<Netmask>& 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: "<<eo.source.toString()<<endl;
+      opts.push_back(make_pair(8, makeEDNSSubnetOptsString(eo)));
+      srcmask=boost::optional<Netmask>(); // 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: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
+           if(srcmask)
+             srcmask = reso.scope;
+         }
+       }
+
+      }
     }
         
     return 1;
index ec33c396c6135c8f5eda27cd001e0d31652c69e0..9afe0f203221d72efd7bd4bd8f4cdef58fd1e4fd 100644 (file)
@@ -66,6 +66,5 @@ public:
   bool d_haveEDNS;
 };
 
-int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, LWResult* res);
-
+int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res);
 #endif // PDNS_LWRES_HH
index 31354cff301f1a1a2dfe65a8bbd8d91f4a5ddc42..d4f394287d44b8073f9ce5cc2b1558fef82b32d0 100644 (file)
@@ -93,6 +93,9 @@ __thread addrringbuf_t* t_remotes, *t_servfailremotes, *t_largeanswerremotes;
 __thread boost::circular_buffer<pair<DNSName, uint16_t> >* t_queryring, *t_servfailqueryring;
 __thread shared_ptr<Regex>* t_traceRegex;
 
+NetmaskGroup g_ednssubnets;
+SuffixMatchNode g_ednsdomains;
+
 DNSFilterEngine g_dfe;
 
 RecursorControlChannel s_rcc; // only active in thread 0
@@ -2172,6 +2175,8 @@ int serviceMain(int argc, char*argv[])
   makeUDPServerSockets();
   makeTCPServerSockets();
 
+  parseEDNSSubnetWhitelist(::arg()["edns-subnet-whitelist"]);
+
   int forks;
   for(forks = 0; forks < ::arg().asNum("processes") - 1; ++forks) {
     if(!fork()) // we are child
@@ -2483,6 +2488,7 @@ int main(int argc, char **argv)
 //    ::arg().setSwitch( "disable-edns-ping", "Disable EDNSPing - EXPERIMENTAL, LEAVE DISABLED" )= "no";
     ::arg().setSwitch( "disable-edns", "Disable EDNS - EXPERIMENTAL, LEAVE DISABLED" )= "";
     ::arg().setSwitch( "disable-packetcache", "Disable packetcache" )= "no";
+    ::arg().set("edns-subnet-whitelist", "List of netmasks and domains that we should enable EDNS subnet for")="powerdns.com,82.94.213.34,2001:888:2000:1d::2";
     ::arg().setSwitch( "pdns-distributes-queries", "If PowerDNS itself should distribute queries over threads")="";
     ::arg().setSwitch( "root-nx-trust", "If set, believe that an NXDOMAIN from the root means the TLD does not exist")="no";
     ::arg().setSwitch( "any-to-tcp","Answer ANY queries with tc=1, shunting to TCP" )="no";
index 8ec82fc66275b6d0ef759a789b44a3368fd119be..ff1226e49fb2edbfcbc0beb03159907459b4142b 100644 (file)
@@ -30,7 +30,7 @@ unsigned int MemRecursorCache::bytes()
   return ret;
 }
 
-int MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures)
+int MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, vector<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* 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<DNSRe
   return true;
 }
 
-void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, bool auth)
+void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, bool auth, boost::optional<Netmask> ednsmask)
 {
+  if(ednsmask) {
+    cerr<<"This data is actually subnet mask specific!!"<<endl;
+  }
   d_cachecachevalid=false;
   boost::tuple<DNSName, uint16_t> 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()<<" -> '"<<content.begin()->d_content->getZoneRepresentation()<<"', auth="<<auth<<", ce.auth="<<ce.d_auth<<"\n";
 
-  ce.d_records.clear();
+  auto records = &ce.d_records;
+  if(ednsmask) {
+    records=nullptr; // there, we use nullptr!
+
+    /* so the logic here is.. if you update a specific mask, we'll update too. 
+       If you supply anything more generic, we'll store that next to your more
+       specific answers. 
+       If you supply more specific answers, we'll store those too. */
+    for(auto &p : ce.d_subnetspecific) {
+      if(p.first == *ednsmask) {
+       records = &p.second;
+       break;
+      }
+    }
+    if(records==nullptr) {
+      ce.d_subnetspecific.push_back({*ednsmask, CacheEntry::records_t()});
+      records = &ce.d_subnetspecific.rbegin()->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"<<endl;
-    ce.d_records.clear(); // clear non-auth data
+    records->clear(); // clear non-auth data
     ce.d_auth = true;
   }
 //  else cerr<<"\tNot nuking"<<endl;
@@ -162,36 +194,8 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt
   for(auto i=content.cbegin(); i != content.cend(); ++i) {
     // cerr<<"To store: "<<i->content<<" with ttl/ttd "<<i->ttl<<endl;
     ce.d_ttd=min(maxTTD, i->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<StoredRecord>::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="<<j->d_ttd - i->ttl<<endl;;
-            j->d_ttd=i->ttl;
-          }
-          else {
-            //~ cerr<<"\t\tNOT updating the ttl, old= " <<j->d_ttd - now <<", new: "<<i->ttl - now <<endl;
-          }
-        }
-      }
-      else {
-        //~ cerr<<"\t\tThere was no exact copy of this record, so adding & sorting\n";
-        ce.d_records.push_back(dr);
-        sort(ce.d_records.begin(), ce.d_records.end());
-       }
-    }
-    */
+    records->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);
index 020787391a6d63038581c116e2519ef29e8c0284..93d36bea1fcd233dbbd9ca0c6cba22059f71ef47 100644 (file)
@@ -32,9 +32,9 @@ public:
   }
   unsigned int size();
   unsigned int bytes();
-  int get(time_t, const DNSName &qname, const QType& qt, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=0);
+  int get(time_t, const DNSName &qname, const QType& qt, vector<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=0);
 
-  void replace(time_t, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, bool auth);
+  void replace(time_t, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, bool auth, boost::optional<Netmask> ednsmask=boost::optional<Netmask>());
   void doPrune(void);
   void doSlash(int perc);
   uint64_t doDump(int fd);
index d4a696776ea8acb20a8d7c74b7ded45f773eb684..f3537e3695f44525259de24899597482c26984d4 100644 (file)
@@ -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<Netmask>& 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 "<<ip.toString()<<" for '"<<domain.toString()<<"' (ret="<<ret<<"), no change in mode"<<endl;
@@ -393,7 +394,8 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
           const ComboAddress remoteIP = servers.front();
           LOG(prefix<<qname.toString()<<": forwarding query to hardcoded nameserver '"<< remoteIP.toStringWithPort()<<"' for zone '"<<authname.toString()<<"'"<<endl);
 
-          res=asyncresolveWrapper(remoteIP, qname, qtype.getCode(), false, false, &d_now, &lwr);
+         boost::optional<Netmask> 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<ComboAddress> SyncRes::getAddrs(const DNSName &qname, int depth, set<GetB
     if(done) {
       if(j==1 && s_doIPv6) { // we got an A record, see if we have some AAAA lying around
        vector<DNSRecord> 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<AAAARecordContent>(k->d_content)->getCA(53);
@@ -534,7 +536,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto
     LOG(prefix<<qname.toString()<<": Checking if we have NS in cache for '"<<subdomain.toString()<<"'"<<endl);
     vector<DNSRecord> 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<DNSRecord> 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<NSRecordContent>(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<<qname.toString()<<": NS (with ip, or non-glue) in cache for '"<<subdomain.toString()<<"' -> '"<<nrr->getNS()<<"'"<<endl);
             LOG(prefix<<qname.toString()<<": within bailiwick: "<< nrr->getNS().isPartOf(subdomain));
@@ -653,7 +655,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector
   LOG(prefix<<qname.toString()<<": Looking for CNAME cache hit of '"<<(qname.toString()+"|CNAME")<<"'"<<endl);
   vector<DNSRecord> cset;
   vector<std::shared_ptr<RRSIGRecordContent>> 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<DNSR
   bool found=false, expired=false;
   vector<std::shared_ptr<RRSIGRecordContent>> 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<<sqname.toString()<<": Found cache hit for "<<sqt.getName()<<": ");
     for(auto j=cset.cbegin() ; j != cset.cend() ; ++j) {
       LOG(j->d_content->getZoneRepresentation());
@@ -909,6 +911,7 @@ int SyncRes::doResolveAt(set<DNSName> nameservers, DNSName auth, bool flawedNSSe
       int resolveret;
       bool pierceDontQuery=false;
       bool sendRDQuery=false;
+      boost::optional<Netmask> ednsmask;
       LWResult lwr;
       if(tns->empty()) {
         LOG(prefix<<qname.toString()<<": Domain is out-of-band"<<endl);
@@ -994,10 +997,14 @@ int SyncRes::doResolveAt(set<DNSName> nameservers, DNSName auth, bool flawedNSSe
            if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, lwr.d_records, resolveret)) {
              LOG(prefix<<qname.toString()<<": query handled by Lua"<<endl);
            }
-           else
+           else {
+             ednsmask=getEDNSSubnetMask(d_requestor, qname, *remoteIP);
              resolveret=asyncresolveWrapper(*remoteIP, qname,  qtype.getCode(),
-                                           doTCP, sendRDQuery, &d_now, &lwr);    // <- we go out on the wire!
-
+                                            doTCP, sendRDQuery, &d_now, ednsmask, &lwr);    // <- we go out on the wire!
+             if(ednsmask && ednsmask->getBits()) {
+               cerr<<"Actually got something back.. "<<ednsmask->toString()<<endl;
+             }
+           }
             if(resolveret==-3)
              throw ImmediateServFailException("Query killed by policy");
 
@@ -1169,7 +1176,7 @@ int SyncRes::doResolveAt(set<DNSName> nameservers, DNSName auth, bool flawedNSSe
 
        //      cout<<"Have "<<i->second.records.size()<<" records and "<<i->second.signatures.size()<<" signatures for "<<i->first.first.toString();
        //      cout<<'|'<<DNSRecordContent::NumberToType(i->first.second.getCode())<<endl;
-        t_RC->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<DNSName> nsset;
       LOG(prefix<<qname.toString()<<": determining status after receiving this packet"<<endl);
index 618240ae73d90d7b46441710421d0aafc30d7ed6..5084e60ce2dbc6fb2f9b906d3c093bea7a3a04f0 100644 (file)
@@ -288,7 +288,7 @@ public:
   }
 
 
-  int asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, LWResult* res);
+  int asyncresolveWrapper(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res);
 
   static void doEDNSDumpAndClose(int fd);