"Fossies" - the Fresh Open Source Software Archive

Member "pdns-auth-4.2.0/pdns/dnswriter.cc" (27 Aug 2019, 13684 Bytes) of package /linux/misc/dns/pdns-auth-4.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "dnswriter.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.13_vs_4.2.0.

    1 /*
    2  * This file is part of PowerDNS or dnsdist.
    3  * Copyright -- PowerDNS.COM B.V. and its contributors
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of version 2 of the GNU General Public License as
    7  * published by the Free Software Foundation.
    8  *
    9  * In addition, for the avoidance of any doubt, permission is granted to
   10  * link this program with OpenSSL and to (re)distribute the binaries
   11  * produced as the result of such linking.
   12  *
   13  * This program is distributed in the hope that it will be useful,
   14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  * GNU General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU General Public License
   19  * along with this program; if not, write to the Free Software
   20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   21  */
   22 #ifdef HAVE_CONFIG_H
   23 #include "config.h"
   24 #endif
   25 #include <boost/version.hpp>
   26 #if BOOST_VERSION >= 105400
   27 #include <boost/container/static_vector.hpp>
   28 #endif
   29 #include "dnswriter.hh"
   30 #include "misc.hh"
   31 #include "dnsparser.hh"
   32 
   33 #include <limits.h>
   34 
   35 /* d_content:                                      <---- d_stuff ---->
   36                                       v d_truncatemarker  
   37    dnsheader | qname | qtype | qclass | {recordname| dnsrecordheader | record }
   38                                         ^ d_rollbackmarker           ^ d_sor 
   39     
   40 
   41 */
   42 
   43 
   44 DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const DNSName& qname, uint16_t  qtype, uint16_t qclass, uint8_t opcode)
   45   : d_content(content), d_qname(qname), d_canonic(false), d_lowerCase(false)
   46 {
   47   d_content.clear();
   48   dnsheader dnsheader;
   49 
   50   memset(&dnsheader, 0, sizeof(dnsheader));
   51   dnsheader.id=0;
   52   dnsheader.qdcount=htons(1);
   53   dnsheader.opcode=opcode;
   54 
   55   const uint8_t* ptr=(const uint8_t*)&dnsheader;
   56   uint32_t len=d_content.size();
   57   d_content.resize(len + sizeof(dnsheader));
   58   uint8_t* dptr=(&*d_content.begin()) + len;
   59 
   60   memcpy(dptr, ptr, sizeof(dnsheader));
   61   d_namepositions.reserve(16);
   62   xfrName(qname, false);
   63   xfr16BitInt(qtype);
   64   xfr16BitInt(qclass);
   65 
   66   d_truncatemarker=d_content.size();
   67   d_sor = 0;
   68   d_rollbackmarker = 0;
   69 }
   70 
   71 dnsheader* DNSPacketWriter::getHeader()
   72 {
   73   return reinterpret_cast<dnsheader*>(&*d_content.begin());
   74 }
   75 
   76 
   77 void DNSPacketWriter::startRecord(const DNSName& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, DNSResourceRecord::Place place, bool compress)
   78 {
   79   d_compress = compress;
   80   commit();
   81   d_rollbackmarker=d_content.size();
   82 
   83   if(compress && !name.isRoot() && d_qname==name) {  // don't do the whole label compression thing if we *know* we can get away with "see question" - except when compressing the root
   84     static unsigned char marker[2]={0xc0, 0x0c};
   85     d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]);
   86   }
   87   else {
   88     xfrName(name, compress);
   89   }
   90   xfr16BitInt(qtype);
   91   xfr16BitInt(qclass);
   92   xfr32BitInt(ttl);
   93   xfr16BitInt(0); // this will be the record size
   94   d_recordplace = place;
   95   d_sor=d_content.size(); // this will remind us where to stuff the record size
   96 }
   97 
   98 void DNSPacketWriter::addOpt(const uint16_t udpsize, const uint16_t extRCode, const uint16_t ednsFlags, const optvect_t& options, const uint8_t version)
   99 {
  100   uint32_t ttl=0;
  101 
  102   EDNS0Record stuff;
  103 
  104   stuff.version = version;
  105   stuff.extFlags = htons(ednsFlags);
  106 
  107   /* RFC 6891 section 4 on the Extended RCode wire format
  108    *    EXTENDED-RCODE
  109    *        Forms the upper 8 bits of extended 12-bit RCODE (together with the
  110    *        4 bits defined in [RFC1035].  Note that EXTENDED-RCODE value 0
  111    *        indicates that an unextended RCODE is in use (values 0 through 15).
  112    */
  113   // XXX Should be check for extRCode > 1<<12 ?
  114   stuff.extRCode = extRCode>>4;
  115   if (extRCode != 0) { // As this trumps the existing RCODE
  116     getHeader()->rcode = extRCode;
  117   }
  118 
  119   static_assert(sizeof(EDNS0Record) == sizeof(ttl), "sizeof(EDNS0Record) must match sizeof(ttl)");
  120   memcpy(&ttl, &stuff, sizeof(stuff));
  121 
  122   ttl=ntohl(ttl); // will be reversed later on
  123 
  124   startRecord(g_rootdnsname, QType::OPT, ttl, udpsize, DNSResourceRecord::ADDITIONAL, false);
  125   for(auto const &option : options) {
  126     xfr16BitInt(option.first);
  127     xfr16BitInt(option.second.length());
  128     xfrBlob(option.second);
  129   }
  130 }
  131 
  132 void DNSPacketWriter::xfr48BitInt(uint64_t val)
  133 {
  134   unsigned char bytes[6];
  135   uint16_t theLeft = htons((val >> 32)&0xffffU);
  136   uint32_t theRight = htonl(val & 0xffffffffU);
  137   memcpy(bytes, (void*)&theLeft, sizeof(theLeft));
  138   memcpy(bytes+2, (void*)&theRight, sizeof(theRight));
  139 
  140   d_content.insert(d_content.end(), bytes, bytes + sizeof(bytes));
  141 }
  142 
  143 
  144 void DNSPacketWriter::xfr32BitInt(uint32_t val)
  145 {
  146   uint32_t rval=htonl(val);
  147   uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
  148   d_content.insert(d_content.end(), ptr, ptr+4);
  149 }
  150 
  151 void DNSPacketWriter::xfr16BitInt(uint16_t val)
  152 {
  153   uint16_t rval=htons(val);
  154   uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
  155   d_content.insert(d_content.end(), ptr, ptr+2);
  156 }
  157 
  158 void DNSPacketWriter::xfr8BitInt(uint8_t val)
  159 {
  160   d_content.push_back(val);
  161 }
  162 
  163 
  164 /* input:
  165  if lenField is true
  166   "" -> 0
  167   "blah" -> 4blah
  168   "blah" "blah" -> output 4blah4blah
  169   "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit)
  170   "blah\"blah" -> 9blah"blah
  171   "blah\97" -> 5blahb
  172 
  173  if lenField is false
  174   "blah" -> blah
  175   "blah\"blah" -> blah"blah
  176   */
  177 void DNSPacketWriter::xfrText(const string& text, bool, bool lenField)
  178 {
  179   if(text.empty()) {
  180     d_content.push_back(0);
  181     return;
  182   }
  183   vector<string> segments = segmentDNSText(text);
  184   for(const string& str :  segments) {
  185     if(lenField)
  186       d_content.push_back(str.length());
  187     d_content.insert(d_content.end(), str.c_str(), str.c_str() + str.length());
  188   }
  189 }
  190 
  191 void DNSPacketWriter::xfrUnquotedText(const string& text, bool lenField)
  192 {
  193   if(text.empty()) {
  194     d_content.push_back(0);
  195     return;
  196   }
  197   if(lenField)
  198     d_content.push_back(text.length());
  199   d_content.insert(d_content.end(), text.c_str(), text.c_str() + text.length());
  200 }
  201 
  202 
  203 static constexpr bool l_verbose=false;
  204 static constexpr uint16_t maxCompressionOffset=16384;
  205 uint16_t DNSPacketWriter::lookupName(const DNSName& name, uint16_t* matchLen)
  206 {
  207   // iterate over the written labels, see if we find a match
  208   const auto& raw = name.getStorage();
  209 
  210   /* name might be a.root-servers.net, we need to be able to benefit from finding:
  211      b.root-servers.net, or even:
  212      b\xc0\x0c 
  213   */
  214   unsigned int bestpos=0;
  215   *matchLen=0;
  216 #if BOOST_VERSION >= 105400
  217   boost::container::static_vector<uint16_t, 34> nvect, pvect;
  218 #else
  219   vector<uint16_t> nvect, pvect;
  220 #endif
  221 
  222   try {
  223     for(auto riter= raw.cbegin(); riter < raw.cend(); ) {
  224       if(!*riter)
  225         break;
  226       nvect.push_back(riter - raw.cbegin());
  227       riter+=*riter+1;
  228     }
  229   }
  230   catch(std::bad_alloc& ba) {
  231     if(l_verbose)
  232       cout<<"Domain "<<name<<" too large to compress"<<endl;
  233     return 0;
  234   }
  235   
  236   if(l_verbose) {
  237     cout<<"Input vector for lookup "<<name<<": ";
  238     for(const auto n : nvect) 
  239       cout << n<<" ";
  240     cout<<endl;
  241     cout<<makeHexDump(string(raw.c_str(), raw.c_str()+raw.size()))<<endl;
  242   }
  243 
  244   if(l_verbose)
  245     cout<<"Have "<<d_namepositions.size()<<" to ponder"<<endl;
  246   int counter=1;
  247   for(auto p : d_namepositions) {
  248     if(l_verbose) {
  249       cout<<"Pos: "<<p<<", "<<d_content.size()<<endl;
  250       DNSName pname((const char*)&d_content[0], d_content.size(), p, true); // only for debugging
  251       cout<<"Looking at '"<<pname<<"' in packet at position "<<p<<"/"<<d_content.size()<<", option "<<counter<<"/"<<d_namepositions.size()<<endl;
  252       ++counter;
  253     }
  254     // memcmp here makes things _slower_
  255     pvect.clear();
  256     try {
  257       for(auto iter = d_content.cbegin() + p; iter < d_content.cend();) {
  258         uint8_t c=*iter;
  259         if(l_verbose)
  260           cout<<"Found label length: "<<(int)c<<endl;
  261         if(c & 0xc0) {
  262           uint16_t npos = 0x100*(c & (~0xc0)) + *++iter;
  263           iter = d_content.begin() + npos;
  264           if(l_verbose)
  265             cout<<"Is compressed label to newpos "<<npos<<", going there"<<endl;
  266           // check against going forward here
  267           continue;
  268         }
  269         if(!c)
  270           break;
  271         auto offset = iter - d_content.cbegin();
  272         if (offset >= maxCompressionOffset) break; // compression pointers cannot point here
  273         pvect.push_back(offset);
  274         iter+=*iter+1;
  275       }
  276     }
  277     catch(std::bad_alloc& ba) {
  278       if(l_verbose)
  279         cout<<"Domain "<<name<<" too large to compress"<<endl;
  280       continue;
  281     }
  282     if(l_verbose) {
  283       cout<<"Packet vector: "<<endl;
  284       for(const auto n : pvect) 
  285         cout << n<<" ";
  286       cout<<endl;
  287     }
  288     auto niter=nvect.crbegin(), piter=pvect.crbegin();
  289     unsigned int cmatchlen=1;
  290     for(; niter != nvect.crend() && piter != pvect.crend(); ++niter, ++piter) {
  291       // niter is an offset in raw, pvect an offset in packet
  292       uint8_t nlen = raw[*niter], plen=d_content[*piter];
  293       if(l_verbose)
  294         cout<<"nlnen="<<(int)nlen<<", plen="<<(int)plen<<endl;
  295       if(nlen != plen)
  296         break;
  297       if(strncasecmp(raw.c_str()+*niter+1, (const char*)&d_content[*piter]+1, nlen)) {
  298         if(l_verbose)
  299           cout<<"Mismatch: "<<string(raw.c_str()+*niter+1, raw.c_str()+*niter+nlen+1)<< " != "<<string((const char*)&d_content[*piter]+1, (const char*)&d_content[*piter]+nlen+1)<<endl;
  300         break;
  301       }
  302       cmatchlen+=nlen+1;
  303       if(cmatchlen == raw.length()) { // have matched all of it, can't improve
  304         if(l_verbose)
  305           cout<<"Stopping search, matched whole name"<<endl;
  306         *matchLen = cmatchlen;
  307         return *piter;
  308       }
  309     }
  310     if(piter != pvect.crbegin() && *matchLen < cmatchlen) {
  311       *matchLen = cmatchlen;
  312       bestpos=*--piter;
  313     }
  314   }
  315   return bestpos;
  316 }
  317 // this is the absolute hottest function in the pdns recursor
  318 void DNSPacketWriter::xfrName(const DNSName& name, bool compress, bool)
  319 {
  320   if(l_verbose)
  321     cout<<"Wants to write "<<name<<", compress="<<compress<<", canonic="<<d_canonic<<", LC="<<d_lowerCase<<endl;
  322   if(d_canonic || d_lowerCase)   // d_lowerCase implies canonic
  323     compress=false;
  324 
  325   if(name.empty() || name.isRoot()) { // for speed
  326     d_content.push_back(0);
  327     return;
  328   }
  329 
  330   uint16_t li=0;
  331   uint16_t matchlen=0;
  332   if(d_compress && compress && (li=lookupName(name, &matchlen)) && li < maxCompressionOffset) {
  333     const auto& dns=name.getStorage(); 
  334     if(l_verbose)
  335       cout<<"Found a substring of "<<matchlen<<" bytes from the back, offset: "<<li<<", dnslen: "<<dns.size()<<endl;
  336     // found a substring, if www.powerdns.com matched powerdns.com, we get back matchlen = 13
  337 
  338     unsigned int pos=d_content.size();
  339     if(pos < maxCompressionOffset && matchlen != dns.size()) {
  340       if(l_verbose)
  341         cout<<"Inserting pos "<<pos<<" for "<<name<<" for compressed case"<<endl;
  342       d_namepositions.push_back(pos);
  343     }
  344 
  345     if(l_verbose)
  346       cout<<"Going to write unique part: '"<<makeHexDump(string(dns.c_str(), dns.c_str() + dns.size() - matchlen)) <<"'"<<endl;
  347     d_content.insert(d_content.end(), (const unsigned char*)dns.c_str(), (const unsigned char*)dns.c_str() + dns.size() - matchlen);
  348     uint16_t offset=li;
  349     offset|=0xc000;
  350 
  351     d_content.push_back((char)(offset >> 8));
  352     d_content.push_back((char)(offset & 0xff));
  353   }
  354   else {
  355     unsigned int pos=d_content.size();
  356     if(l_verbose)
  357       cout<<"Found nothing, we are at pos "<<pos<<", inserting whole name"<<endl;
  358     if(pos < maxCompressionOffset) {
  359       if(l_verbose)
  360         cout<<"Inserting pos "<<pos<<" for "<<name<<" for uncompressed case"<<endl;
  361       d_namepositions.push_back(pos);
  362     }
  363 
  364     std::unique_ptr<DNSName> lc;
  365     if(d_lowerCase)
  366       lc = make_unique<DNSName>(name.makeLowerCase());
  367 
  368     const DNSName::string_t& raw = (lc ? *lc : name).getStorage();
  369     if(l_verbose)
  370       cout<<"Writing out the whole thing "<<makeHexDump(string(raw.c_str(),  raw.c_str() + raw.length()))<<endl;
  371     d_content.insert(d_content.end(), raw.c_str(), raw.c_str() + raw.size());
  372   }
  373 }
  374 
  375 void DNSPacketWriter::xfrBlob(const string& blob, int  )
  376 {
  377   const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str());
  378   d_content.insert(d_content.end(), ptr, ptr+blob.size());
  379 }
  380 
  381 void DNSPacketWriter::xfrBlobNoSpaces(const string& blob, int  )
  382 {
  383   xfrBlob(blob);
  384 }
  385 
  386 void DNSPacketWriter::xfrHexBlob(const string& blob, bool keepReading)
  387 {
  388   xfrBlob(blob);
  389 }
  390 
  391 // call __before commit__
  392 void DNSPacketWriter::getRecordPayload(string& records)
  393 {
  394   records.assign(d_content.begin() + d_sor, d_content.end());
  395 }
  396 
  397 uint32_t DNSPacketWriter::size()
  398 {
  399   return d_content.size();
  400 }
  401 
  402 void DNSPacketWriter::rollback()
  403 {
  404   d_content.resize(d_rollbackmarker);
  405   d_sor = 0;
  406 }
  407 
  408 void DNSPacketWriter::truncate()
  409 {
  410   d_content.resize(d_truncatemarker);
  411   dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
  412   dh->ancount = dh->nscount = dh->arcount = 0;
  413 }
  414 
  415 void DNSPacketWriter::commit()
  416 {
  417   if(!d_sor)
  418     return;
  419   uint16_t rlen = d_content.size() - d_sor;
  420   d_content[d_sor-2]=rlen >> 8;
  421   d_content[d_sor-1]=rlen & 0xff;
  422   d_sor=0;
  423   dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
  424   switch(d_recordplace) {
  425   case DNSResourceRecord::QUESTION:
  426     dh->qdcount = htons(ntohs(dh->qdcount) + 1);
  427     break;
  428   case DNSResourceRecord::ANSWER:
  429     dh->ancount = htons(ntohs(dh->ancount) + 1);
  430     break;
  431   case DNSResourceRecord::AUTHORITY:
  432     dh->nscount = htons(ntohs(dh->nscount) + 1);
  433     break;
  434   case DNSResourceRecord::ADDITIONAL:
  435     dh->arcount = htons(ntohs(dh->arcount) + 1);
  436     break;
  437   }
  438 
  439 }