]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
implement EDNS NSID call (RFC 5001), plus an experimental EDNS option ('ping')
authorBert Hubert <bert.hubert@netherlabs.nl>
Sun, 13 Jul 2008 20:21:54 +0000 (20:21 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Sun, 13 Jul 2008 20:21:54 +0000 (20:21 +0000)
git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@1232 d19b8d6e-7fed-0310-83ef-9ca221ded41b

12 files changed:
pdns/common_startup.cc
pdns/dnspacket.cc
pdns/dnspacket.hh
pdns/dnsparser.cc
pdns/dnsparser.hh
pdns/dnsrecords.cc
pdns/dnsrecords.hh
pdns/dnswriter.cc
pdns/dnswriter.hh
pdns/packethandler.cc
pdns/pdns_recursor.cc
pdns/receiver.cc

index 83d2e77963fd02f17b274455d9b68f8ca5e5398f..ad79d12d228339640c03cb0b37c8afe712ec4361 100644 (file)
@@ -104,7 +104,7 @@ void declareArguments()
   ::arg().set("negquery-cache-ttl","Seconds to store packets in the PacketCache")="60";
   ::arg().set("query-cache-ttl","Seconds to store packets in the PacketCache")="20";
   ::arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600";
-
+  ::arg().set("server-id", "Returned when queried for 'server.id' TXT or NSID, defaults to hostname")="";
   ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
   ::arg().set("soa-retry-default","Default SOA retry")="3600";
   ::arg().set("soa-expire-default","Default SOA expire")="604800";
@@ -123,8 +123,6 @@ void declareArguments()
   ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
 }
 
-
-
 void declareStats(void)
 {
   S.declare("udp-queries","Number of UDP queries received");
@@ -241,7 +239,7 @@ void *qthread(void *p)
     S.ringAccount("queries", P->qdomain+"/"+P->qtype.getName());
     S.ringAccount("remotes",P->getRemote());
 
-    if((P->d.opcode != Opcode::Notify) && PC.get(P,&cached)) { // short circuit - does the PacketCache recognize this question?
+    if((P->d.opcode != Opcode::Notify) && P->couldBeCached() && PC.get(P,&cached)) { // short circuit - does the PacketCache recognize this question?
       cached.setRemote(&P->remote);  // inlined
       cached.setSocket(P->getSocket());                               // inlined
       cached.spoofID(P->d.id);                                        // inlined 
index 782044020452c68e0cf6dd833c119cd88e7e4005..d44adaa76253a7a4bf605feda9c8c753fc9d49a0 100644 (file)
 #include "arguments.hh"
 #include "dnswriter.hh"
 #include "dnsparser.hh"
+#include "dnsrecords.hh"
 
 DNSPacket::DNSPacket() 
 {
   d_wrapped=false;
   d_compress=true;
   d_tcp=false;
+  d_wantsnsid=false;
 }
 
 string DNSPacket::getString()
@@ -88,6 +90,8 @@ DNSPacket::DNSPacket(const DNSPacket &orig)
   qclass=orig.qclass;
   qdomain=orig.qdomain;
   d_maxreplylen = orig.d_maxreplylen;
+  d_ednsping = orig.d_ednsping;
+  d_wantsnsid = orig.d_wantsnsid;
   rrs=orig.rrs;
 
   d_wrapped=orig.d_wrapped;
@@ -263,6 +267,11 @@ void DNSPacket::setCompress(bool compress)
   rrs.reserve(200);
 }
 
+bool DNSPacket::couldBeCached()
+{
+  return d_ednsping.empty() && !d_wantsnsid;
+}
+
 /** Must be called before attempting to access getData(). This function stuffs all resource
  *  records found in rrs into the data buffer. It also frees resource records queued for us.
  */
@@ -304,7 +313,16 @@ void DNSPacket::wrapup(void)
   pw.getHeader()->id=d.id;
   pw.getHeader()->rd=d.rd;
 
-  if(!rrs.empty()) {
+  DNSPacketWriter::optvect_t opts;
+  if(d_wantsnsid) {
+    opts.push_back(make_pair(3, ::arg()["server-id"]));
+  }
+
+  if(!d_ednsping.empty()) {
+    opts.push_back(make_pair(4, d_ednsping));
+  }
+
+  if(!rrs.empty() || !opts.empty()) {
     try {
       for(pos=rrs.begin(); pos < rrs.end(); ++pos) {
        // this needs to deal with the 'prio' mismatch!
@@ -320,6 +338,9 @@ void DNSPacket::wrapup(void)
        shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(pos->qtype.getCode(), 1, pos->content)); 
        drc->toPacket(pw);
       }
+      if(!opts.empty())
+       pw.addOpt(2800, 0, 0, opts);
+
       pw.commit();
     }
     catch(exception& e) {
@@ -383,6 +404,8 @@ DNSPacket *DNSPacket::replyPacket() const
   r->qdomain = qdomain;
   r->qtype = qtype;
   r->d_maxreplylen = d_maxreplylen;
+  r->d_ednsping = d_ednsping;
+  r->d_wantsnsid = d_wantsnsid;
   return r;
 }
 
@@ -402,20 +425,41 @@ int DNSPacket::parse(const char *mesg, int length)
 try
 {
   stringbuffer.assign(mesg,length); 
-
+  
   len=length;
   if(length < 12) { 
     L << Logger::Warning << "Ignoring packet: too short from "
       << getRemote() << endl;
     return -1;
   }
+
   MOADNSParser mdp(stringbuffer);
-  MOADNSParser::EDNSOpts edo;
-  if(mdp.getEDNSOpts(&edo)) {
+  EDNSOpts edo;
+
+  // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST!
+
+  d_wantsnsid=false;
+  d_ednsping.clear();
+
+  if(getEDNSOpts(mdp, &edo)) {
     d_maxreplylen=edo.d_packetsize;
+
+    for(vector<pair<uint16_t, string> >::const_iterator iter = edo.d_options.begin();
+       iter != edo.d_options.end(); 
+       ++iter) {
+      if(iter->first == 3) {// 'EDNS NSID'
+       d_wantsnsid=1;
+      }
+      else if(iter->first == 4) {// 'EDNS PING'
+       d_ednsping = iter->second;
+      }
+      else
+       ; // cerr<<"Have an option #"<<iter->first<<endl;
+    }
   }
-  else
+  else  {
     d_maxreplylen=512;
+  }
 
   memcpy((void *)&d,(const void *)stringbuffer.c_str(),12);
   qdomain=mdp.d_qname;
index 6d39f9cd6cf583e8c8c0a7b9c193f6a5533a1c64..05e43daf50300df838b7bfa6ebec8e3e3258471e 100644 (file)
@@ -139,6 +139,9 @@ public:
 
   void commitD(); //!< copies 'd' into the stringbuffer
   int getMaxReplyLen(); //!< retrieve the maximum length of the packet we should send in response
+
+  bool couldBeCached(); //!< returns 0 if this query should bypass the packet cache
+
   //////// DATA !
 
   ComboAddress remote;
@@ -162,6 +165,8 @@ private:
 
   string stringbuffer; // this is where everything lives 4
   int d_maxreplylen;
+  string d_ednsping;
+  bool d_wantsnsid;
   vector<DNSResourceRecord> rrs; // 4
 };
 
index 00c4f22fce0a7f8e1f37c0e28b9e9df65c7bc2c2..6a87a71980937d0b0e2ef6804d3419f5d7cd912a 100644 (file)
@@ -132,7 +132,9 @@ shared_ptr<DNSRecordContent> DNSRecordContent::unserialize(const string& qname,
 DNSRecordContent* DNSRecordContent::mastermake(const DNSRecord &dr, 
                                               PacketReader& pr)
 {
-  typemap_t::const_iterator i=getTypemap().find(make_pair(dr.d_class, dr.d_type));
+  uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
+
+  typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type));
   if(i==getTypemap().end() || !i->second) {
     return new UnknownRecordContent(dr, pr);
   }
@@ -257,24 +259,6 @@ void MOADNSParser::init(const char *packet, unsigned int len)
   }
 }
 
-bool MOADNSParser::getEDNSOpts(EDNSOpts* eo)
-{
-  if(d_header.arcount && !d_answers.empty() && d_answers.back().first.d_type == QType::OPT) {
-    eo->d_packetsize=d_answers.back().first.d_class;
-
-    EDNS0Record stuff;
-    uint32_t ttl=ntohl(d_answers.back().first.d_ttl);
-    memcpy(&stuff, &ttl, sizeof(stuff));
-
-    eo->d_extRCode=stuff.extRCode;
-    eo->d_version=stuff.version;
-    eo->d_Z=stuff.Z;
-
-    return true;
-  }
-  else
-    return false;
-}
 
 void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
 {
@@ -447,7 +431,10 @@ void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t&
 
 void PacketReader::xfrBlob(string& blob)
 {
-  blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
+  if(d_recordlen)
+    blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
+  else
+    blob.clear();
 
   d_pos = d_startrecordpos + d_recordlen;
 }
index 790e402ccd6c19e79d933e7291d1c842ec2cc18b..e9fb29252157ecb5c21579f97cf5ca96fed0a1c3 100644 (file)
@@ -298,16 +298,7 @@ public:
     return pr;
   }
 
-  struct EDNSOpts
-  {
-    uint16_t d_packetsize;
-    uint8_t d_extRCode, d_version;
-    uint16_t d_Z;
-  };
-
-  //! Convenience function that fills out EDNS0 options, and returns true if there are any
-  bool getEDNSOpts(EDNSOpts* eo);
-
+  
 private:
   void getDnsrecordheader(struct dnsrecordheader &ah);
   void init(const char *packet, unsigned int len);
index 08a0b95eca7e97c0d4ffd4114984993f0bff408f..8c59a769000caa835796265ebabc23ef34f2dded 100644 (file)
@@ -187,10 +187,28 @@ boilerplate_conv(RP, ns_t_rp,
                 );
 
 
-boilerplate_conv(OPT, ns_t_opt,
-                conv.xfrText(d_data)
+boilerplate_conv(OPT, ns_t_opt, 
+                  conv.xfrBlob(d_data)
                 );
 
+void OPTRecordContent::getData(vector<pair<uint16_t, string> >& options)
+{
+  string::size_type pos=0;
+  uint16_t code, len;
+  while(d_data.size() >= 4 + pos) {
+    code = 0xff * d_data[pos] + d_data[pos+1];
+    len = 0xff * d_data[pos+2] + d_data[pos+3];
+    pos+=4;
+
+    if(pos + len > d_data.size())
+      break;
+
+    string field(d_data.c_str() + pos, len);
+    pos+=len;
+
+    options.push_back(make_pair(code, field));
+  }
+}
 
 boilerplate_conv(TSIG, ns_t_tsig, 
                 conv.xfrLabel(d_algoName);
@@ -320,6 +338,33 @@ boilerplate_conv(DNSKEY, 48,
                 conv.xfrBlob(d_key);
                 )
 
+
+bool getEDNSOpts(const MOADNSParser& mdp, EDNSOpts* eo)
+{
+  if(mdp.d_header.arcount && !mdp.d_answers.empty() && 
+     mdp.d_answers.back().first.d_type == QType::OPT) {
+    eo->d_packetsize=mdp.d_answers.back().first.d_class;
+    
+    EDNS0Record stuff;
+    uint32_t ttl=ntohl(mdp.d_answers.back().first.d_ttl);
+    memcpy(&stuff, &ttl, sizeof(stuff));
+
+    eo->d_extRCode=stuff.extRCode;
+    eo->d_version=stuff.version;
+    eo->d_Z=stuff.Z;
+    
+    OPTRecordContent* orc = 
+      dynamic_cast<OPTRecordContent*>(mdp.d_answers.back().first.d_content.get());
+
+    orc->getData(eo->d_options);
+
+    return true;
+  }
+  else
+    return false;
+}
+
+
 void reportBasicTypes()
 {
   ARecordContent::report();
index 64728e3cc49d357da9546db14fc2345a04da65d7..e8b2968bb143e0af9457db51a704d5e082a21fc0 100644 (file)
@@ -197,7 +197,7 @@ class OPTRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(OPT)
-
+  void getData(vector<pair<uint16_t, string> > &opts);
 private:
   string d_data;
 };
@@ -422,6 +422,18 @@ void RNAME##RecordContent::xfrPacket(Convertor& conv)             \
   CONV;                                                           \
 }                                                                 \
 
+struct EDNSOpts
+{
+  uint16_t d_packetsize;
+  uint8_t d_extRCode, d_version;
+  uint16_t d_Z;
+  vector<pair<uint16_t, string> > d_options;
+};
+//! Convenience function that fills out EDNS0 options, and returns true if there are any
+
+class MOADNSParser;
+bool getEDNSOpts(const MOADNSParser& mdp, EDNSOpts* eo);
+
 void reportBasicTypes();
 void reportOtherTypes();
 void reportAllTypes();
index 5a7437e0a41b5ee16f4da262c2abdae02659f269..4befa4ff67b499cfa5efe3dd45ed2e3eb92d0150 100644 (file)
@@ -81,7 +81,7 @@ void DNSPacketWriter::startRecord(const string& name, uint16_t qtype, uint32_t t
   d_sor=d_content.size() + d_stuff; // start of real record 
 }
 
-void DNSPacketWriter::addOpt(int udpsize, int extRCode, int Z)
+void DNSPacketWriter::addOpt(int udpsize, int extRCode, int Z, const vector<pair<uint16_t,string> >& options)
 {
   uint32_t ttl=0;
 
@@ -90,12 +90,17 @@ void DNSPacketWriter::addOpt(int udpsize, int extRCode, int Z)
   stuff.extRCode=extRCode;
   stuff.version=0;
   stuff.Z=htons(Z);
-  
+
   memcpy(&ttl, &stuff, sizeof(stuff));
 
   ttl=ntohl(ttl); // will be reversed later on
   
   startRecord("", ns_t_opt, ttl, udpsize, ADDITIONAL);
+  for(optvect_t::const_iterator iter = options.begin(); iter != options.end(); ++iter) {
+    xfr16BitInt(iter->first);
+    xfr16BitInt(iter->second.length());
+    xfrBlob(iter->second);
+  } 
 }
 
 void DNSPacketWriter::xfr48BitInt(uint64_t val)
index 831539da07e1152f5e0063946daa6dc0abc84af2..e5d418d07c3111ca03caf22f863c3ef2ad3ef184 100644 (file)
@@ -53,7 +53,8 @@ public:
   void startRecord(const string& name, uint16_t qtype, uint32_t ttl=3600, uint16_t qclass=1, Place place=ANSWER);
 
   /** Shorthand way to add an Opt-record, for example for EDNS0 purposes */
-  void addOpt(int udpsize, int extRCode, int Z);
+  typedef vector<pair<uint16_t,std::string> > optvect_t;
+  void addOpt(int udpsize, int extRCode, int Z, const optvect_t& options=optvect_t());
 
   /** needs to be called after the last record is added, but can be called again and again later on. Is called internally by startRecord too.
       The content of the vector<> passed to the constructor is inconsistent until commit is called.
index 1ccf6c3ea87fdb39d9bb5962641c10f77334e9db..3ff12aa8b9086de670107523777179b7711a0351 100644 (file)
@@ -796,6 +796,9 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
       else noCache=true;
     }
     
+    if(!noCache)
+      noCache = !p->couldBeCached();
+
     string::size_type pos;
     
     DLOG(L<<"Nothing found so far for '"<<target<<"', do we even have authority over this domain?"<<endl);
@@ -924,8 +927,9 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
       return 0;
 
     r->wrapup(); // needed for inserting in cache
-    if(!noCache)
+    if(!noCache) {
       PC.insert(p,r); // in the packet cache
+    }
   }
   catch(DBException &e) {
     L<<Logger::Error<<"Database module reported condition which prevented lookup ("+e.reason+") sending out servfail"<<endl;
index 95122e14f14efee085a97e895e9e81dec322f335..2fb4ef2ac466b6fdcb5b5465f0c458d43ee3fda2 100644 (file)
@@ -511,8 +511,8 @@ void startDoResolve(void *p)
 
   try {
     uint16_t maxudpsize=512;
-    MOADNSParser::EDNSOpts edo;
-    if(dc->d_mdp.getEDNSOpts(&edo)) {
+    EDNSOpts edo;
+    if(getEDNSOpts(dc->d_mdp, &edo)) {
       maxudpsize=edo.d_packetsize;
     }
     
@@ -1995,7 +1995,7 @@ int main(int argc, char **argv)
     ::arg().set("hint-file", "If set, load root hints from this file")="";
     ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache")="0";
     ::arg().set("max-negative-ttl", "maximum number of seconds to keep a negative cached entry in memory")="3600";
-    ::arg().set("server-id", "Returned when queried for 'server.id' TXT, defaults to hostname")="";
+    ::arg().set("server-id", "Returned when queried for 'server.id' TXT or NSID, defaults to hostname")="";
     ::arg().set("remotes-ringbuffer-entries", "maximum number of packets to store statistics for")="0";
     ::arg().set("version-string", "string reported on version.pdns or version.bind")="PowerDNS Recursor "VERSION" $Id$";
     ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse")="127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10";
index 8ab7038c8121bb01a53cf463cb2f7d345b23e052..55d128f6a426697302534ed75e0835a2e0f4200b 100644 (file)
@@ -463,8 +463,6 @@ int main(int argc, char **argv)
       cerr<<"Fatal: non-option on the command line, perhaps a '--setting=123' statement missed the '='?"<<endl;
       exit(99);
     }
-
-
     
     if(::arg().mustDo("help")) {
       cerr<<"syntax:"<<endl<<endl;
@@ -500,6 +498,12 @@ int main(int argc, char **argv)
        daemonize();
     }
 
+    if(::arg()["server-id"].empty()) {
+      char tmp[128];
+      gethostname(tmp, sizeof(tmp)-1);
+      ::arg().set("server-id")=tmp;
+    }
+
     if(isGuarded(argv)) {
       L<<Logger::Warning<<"This is a guarded instance of pdns"<<endl;
       dl=new DynListener; // listens on stdin