From: Bert Hubert Date: Sun, 13 Jul 2008 20:21:54 +0000 (+0000) Subject: implement EDNS NSID call (RFC 5001), plus an experimental EDNS option ('ping') X-Git-Tag: rec-3.2~290 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7f7b8d5572dc70408feb2f15874ed11fd9e59988;p=thirdparty%2Fpdns.git implement EDNS NSID call (RFC 5001), plus an experimental EDNS option ('ping') git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@1232 d19b8d6e-7fed-0310-83ef-9ca221ded41b --- diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc index 83d2e77963..ad79d12d22 100644 --- a/pdns/common_startup.cc +++ b/pdns/common_startup.cc @@ -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 diff --git a/pdns/dnspacket.cc b/pdns/dnspacket.cc index 7820440204..d44adaa762 100644 --- a/pdns/dnspacket.cc +++ b/pdns/dnspacket.cc @@ -38,12 +38,14 @@ #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 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 >::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 #"<first< rrs; // 4 }; diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc index 00c4f22fce..6a87a71980 100644 --- a/pdns/dnsparser.cc +++ b/pdns/dnsparser.cc @@ -132,7 +132,9 @@ shared_ptr 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& 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; } diff --git a/pdns/dnsparser.hh b/pdns/dnsparser.hh index 790e402ccd..e9fb292521 100644 --- a/pdns/dnsparser.hh +++ b/pdns/dnsparser.hh @@ -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); diff --git a/pdns/dnsrecords.cc b/pdns/dnsrecords.cc index 08a0b95eca..8c59a76900 100644 --- a/pdns/dnsrecords.cc +++ b/pdns/dnsrecords.cc @@ -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 >& 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(mdp.d_answers.back().first.d_content.get()); + + orc->getData(eo->d_options); + + return true; + } + else + return false; +} + + void reportBasicTypes() { ARecordContent::report(); diff --git a/pdns/dnsrecords.hh b/pdns/dnsrecords.hh index 64728e3cc4..e8b2968bb1 100644 --- a/pdns/dnsrecords.hh +++ b/pdns/dnsrecords.hh @@ -197,7 +197,7 @@ class OPTRecordContent : public DNSRecordContent { public: includeboilerplate(OPT) - + void getData(vector > &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 > 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(); diff --git a/pdns/dnswriter.cc b/pdns/dnswriter.cc index 5a7437e0a4..4befa4ff67 100644 --- a/pdns/dnswriter.cc +++ b/pdns/dnswriter.cc @@ -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 >& 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) diff --git a/pdns/dnswriter.hh b/pdns/dnswriter.hh index 831539da07..e5d418d07c 100644 --- a/pdns/dnswriter.hh +++ b/pdns/dnswriter.hh @@ -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 > 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. diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index 1ccf6c3ea8..3ff12aa8b9 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -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 '"<wrapup(); // needed for inserting in cache - if(!noCache) + if(!noCache) { PC.insert(p,r); // in the packet cache + } } catch(DBException &e) { L<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"; diff --git a/pdns/receiver.cc b/pdns/receiver.cc index 8ab7038c81..55d128f6a4 100644 --- a/pdns/receiver.cc +++ b/pdns/receiver.cc @@ -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 '='?"<