root/trunk/pdns/pdns/dnsparser.cc @ 1050

Revision 1050, 12.3 KB (checked in by ahu, 6 years ago)

speed up storage of NS records

Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2005 - 2006  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2 as
7    published by the Free Software Foundation
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17*/
18
19#include "dnsparser.hh"
20#include "dnswriter.hh"
21#include <boost/lexical_cast.hpp>
22
23using namespace boost;
24
25class UnknownRecordContent : public DNSRecordContent
26{
27public:
28  UnknownRecordContent(const DNSRecord& dr, PacketReader& pr) 
29    : DNSRecordContent(dr.d_type), d_dr(dr)
30  {
31    pr.copyRecord(d_record, dr.d_clen);
32  }
33
34  UnknownRecordContent(const string& zone) : DNSRecordContent(0)
35  {
36    d_record.insert(d_record.end(), zone.begin(), zone.end());
37  }
38 
39  string getZoneRepresentation() const
40  {
41    ostringstream str;
42    str<<"\\# "<<(unsigned int)d_record.size()<<" ";
43    char hex[4];
44    for(size_t n=0; n<d_record.size(); ++n) {
45      snprintf(hex,sizeof(hex)-1, "%02x", d_record.at(n));
46      str << hex;
47    }
48    return str.str();
49  }
50 
51  void toPacket(DNSPacketWriter& pw)
52  {
53    string tmp((char*)&*d_record.begin(), d_record.size());
54    vector<string> parts;
55    stringtok(parts, tmp);
56    if(parts.size()!=3)
57      throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+lexical_cast<string>(parts.size())+": "+tmp );
58    const string& relevant=parts[2];
59    unsigned int total=atoi(parts[1].c_str());
60    if(relevant.size()!=2*total)
61      throw runtime_error("invalid unknown record");
62    string out;
63    out.reserve(total+1);
64    for(unsigned int n=0; n < total; ++n) {
65      int c;
66      sscanf(relevant.c_str()+2*n, "%02x", &c);
67      out.append(1, (char)c);
68    }
69    pw.xfrBlob(out);
70  }
71private:
72  DNSRecord d_dr;
73  vector<uint8_t> d_record;
74};
75
76static const string EncodeDNSLabel(const string& input) 
77{ 
78  typedef vector<string> parts_t; 
79  parts_t parts; 
80  stringtok(parts,input,".");                   
81  string ret; 
82  for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { 
83    ret.append(1,(char)i->length()); 
84    ret.append(*i); 
85  } 
86  ret.append(1,(char)0); 
87  return ret; 
88} 
89
90shared_ptr<DNSRecordContent> DNSRecordContent::unserialize(const string& qname, uint16_t qtype, const string& serialized)
91{
92  dnsheader dnsheader;
93  memset(&dnsheader, 0, sizeof(dnsheader));
94  dnsheader.qdcount=htons(1);
95  dnsheader.ancount=htons(1);
96
97  vector<uint8_t> packet; // build pseudo packet
98
99  /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
100
101  string encoded=EncodeDNSLabel(qname);
102
103  packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size());
104
105  uint16_t pos=0;
106
107  memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader);
108
109  char tmp[6]="\x0" "\x0\x1" "\x0\x1"; // root question for ns_t_a
110  memcpy(&packet[pos], &tmp, 5); pos+=5;
111
112  memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size();
113
114  struct dnsrecordheader drh;
115  drh.d_type=htons(qtype);
116  drh.d_class=htons(1);
117  drh.d_ttl=0;
118  drh.d_clen=htons(serialized.size());
119
120  memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh);
121  memcpy(&packet[pos], serialized.c_str(), serialized.size()); pos+=(uint16_t)serialized.size();
122
123  MOADNSParser mdp((char*)&*packet.begin(), (unsigned int)packet.size());
124  shared_ptr<DNSRecordContent> ret= mdp.d_answers.begin()->first.d_content;
125  ret->header.d_type=ret->d_qtype;
126  ret->label=mdp.d_answers.begin()->first.d_label;
127  ret->header.d_ttl=mdp.d_answers.begin()->first.d_ttl;
128  return ret;
129}
130
131DNSRecordContent* DNSRecordContent::mastermake(const DNSRecord &dr, 
132                                               PacketReader& pr)
133{
134  typemap_t::const_iterator i=getTypemap().find(make_pair(dr.d_class, dr.d_type));
135  if(i==getTypemap().end() || !i->second) {
136    return new UnknownRecordContent(dr, pr);
137  }
138
139  return i->second(dr, pr);
140}
141
142DNSRecordContent* DNSRecordContent::mastermake(uint16_t qtype, uint16_t qclass,
143                                               const string& content)
144{
145  zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype));
146  if(i==getZmakermap().end()) {
147    return new UnknownRecordContent(content);
148  }
149
150  return i->second(content);
151}
152
153
154DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
155{
156  static DNSRecordContent::typemap_t typemap;
157  return typemap;
158}
159
160DNSRecordContent::namemap_t& DNSRecordContent::getNamemap()
161{
162  static DNSRecordContent::namemap_t namemap;
163  return namemap;
164}
165
166DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
167{
168  static DNSRecordContent::zmakermap_t zmakermap;
169  return zmakermap;
170}
171
172
173
174void MOADNSParser::init(const char *packet, unsigned int len)
175{
176  if(len < sizeof(dnsheader))
177    throw MOADNSException("Packet shorter than minimal header");
178 
179  memcpy(&d_header, packet, sizeof(dnsheader));
180
181  if(d_header.opcode!=0 && d_header.opcode != 4) // notification
182    throw MOADNSException("Can't parse non-query packet with opcode="+ lexical_cast<string>(d_header.opcode));
183
184  d_header.qdcount=ntohs(d_header.qdcount);
185  d_header.ancount=ntohs(d_header.ancount);
186  d_header.nscount=ntohs(d_header.nscount);
187  d_header.arcount=ntohs(d_header.arcount);
188 
189  uint16_t contentlen=len-sizeof(dnsheader);
190
191  d_content.resize(contentlen);
192  copy(packet+sizeof(dnsheader), packet+len, d_content.begin());
193 
194  unsigned int n=0;
195
196  PacketReader pr(d_content);
197  bool validPacket=false;
198  try {
199    for(n=0;n < d_header.qdcount; ++n) {
200      d_qname=pr.getLabel();
201      d_qtype=pr.get16BitInt();
202      d_qclass=pr.get16BitInt();
203    }
204
205    struct dnsrecordheader ah;
206    vector<unsigned char> record;
207    validPacket=true;
208    for(n=0;n < d_header.ancount + d_header.nscount + d_header.arcount; ++n) {
209      DNSRecord dr;
210     
211      if(n < d_header.ancount)
212        dr.d_place=DNSRecord::Answer;
213      else if(n < d_header.ancount + d_header.nscount)
214        dr.d_place=DNSRecord::Nameserver;
215      else 
216        dr.d_place=DNSRecord::Additional;
217     
218      string label=pr.getLabel();
219     
220      pr.getDnsrecordheader(ah);
221      dr.d_ttl=ah.d_ttl;
222      dr.d_type=ah.d_type;
223      dr.d_class=ah.d_class;
224     
225      dr.d_label=label;
226      dr.d_clen=ah.d_clen;
227
228      dr.d_content=boost::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr, pr));
229      d_answers.push_back(make_pair(dr, pr.d_pos));
230    }
231
232#if 0   
233    if(pr.d_pos!=contentlen) {
234      throw MOADNSException("Packet ("+d_qname+"|#"+lexical_cast<string>(d_qtype)+") has trailing garbage ("+ lexical_cast<string>(pr.d_pos) + " < " +
235                            lexical_cast<string>(contentlen) + ")");
236    }
237#endif
238  }
239  catch(out_of_range &re) {
240    if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
241      if(n < d_header.ancount) {
242        d_header.ancount=n; d_header.nscount = d_header.arcount = 0;
243      }
244      else if(n < d_header.ancount + d_header.nscount) {
245        d_header.nscount = n - d_header.ancount; d_header.arcount=0;
246      }
247      else {
248        d_header.arcount = n - d_header.ancount - d_header.nscount;
249      }
250    }
251    else {
252      throw MOADNSException("Error parsing packet of "+lexical_cast<string>(len)+" bytes (rd="+
253                            lexical_cast<string>(d_header.rd)+
254                            "), out of bounds: "+string(re.what()));
255    }
256  }
257}
258
259bool MOADNSParser::getEDNSOpts(EDNSOpts* eo)
260{
261  if(d_header.arcount && !d_answers.empty()) {
262    eo->d_packetsize=d_answers.back().first.d_class;
263
264    EDNS0Record stuff;
265    uint32_t ttl=ntohl(d_answers.back().first.d_ttl);
266    memcpy(&stuff, &ttl, sizeof(stuff));
267
268    eo->d_extRCode=stuff.extRCode;
269    eo->d_version=stuff.version;
270    eo->d_Z=stuff.Z;
271
272    return true;
273  }
274  else
275    return false;
276}
277
278void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
279{
280  unsigned int n;
281  unsigned char *p=reinterpret_cast<unsigned char*>(&ah);
282 
283  for(n=0; n < sizeof(dnsrecordheader); ++n) 
284    p[n]=d_content.at(d_pos++);
285 
286  ah.d_type=ntohs(ah.d_type);
287  ah.d_class=ntohs(ah.d_class);
288  ah.d_clen=ntohs(ah.d_clen);
289  ah.d_ttl=ntohl(ah.d_ttl);
290
291  d_startrecordpos=d_pos; // needed for getBlob later on
292  d_recordlen=ah.d_clen;
293}
294
295
296void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len)
297{
298  dest.resize(len);
299  if(!len)
300    return;
301
302  for(uint16_t n=0;n<len;++n) {
303    dest.at(n)=d_content.at(d_pos++);
304  }
305}
306
307void PacketReader::copyRecord(unsigned char* dest, uint16_t len)
308{
309  if(d_pos + len > d_content.size())
310    throw MOADNSException("Attempt to copy outside of packet");
311
312  memcpy(dest, &d_content.at(d_pos), len);
313  d_pos+=len;
314}
315
316
317uint32_t PacketReader::get32BitInt()
318{
319  uint32_t ret=0;
320  ret+=d_content.at(d_pos++);
321  ret<<=8;
322  ret+=d_content.at(d_pos++);
323  ret<<=8;
324  ret+=d_content.at(d_pos++);
325  ret<<=8;
326  ret+=d_content.at(d_pos++);
327 
328  return ret;
329}
330
331
332uint16_t PacketReader::get16BitInt()
333{
334  return get16BitInt(d_content, d_pos);
335}
336
337uint16_t PacketReader::get16BitInt(const vector<unsigned char>&content, uint16_t& pos)
338{
339  uint16_t ret=0;
340  ret+=content.at(pos++);
341  ret<<=8;
342  ret+=content.at(pos++);
343 
344  return ret;
345}
346
347uint8_t PacketReader::get8BitInt()
348{
349  return d_content.at(d_pos++);
350}
351
352
353string PacketReader::getLabel(unsigned int recurs)
354{
355  string ret;
356  ret.reserve(40);
357  getLabelFromContent(d_content, d_pos, ret, recurs++);
358  return ret;
359}
360
361static string txtEscape(const string &name)
362{
363  string ret;
364
365  for(string::const_iterator i=name.begin();i!=name.end();++i)
366    if(*i=='"' || *i=='\\'){
367      ret += '\\';
368      ret += *i;
369    }
370    else
371      ret += *i;
372  return ret;
373}
374
375// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
376string PacketReader::getText(bool multi)
377{
378  string ret;
379  ret.reserve(40);
380  while(d_pos < d_startrecordpos + d_recordlen ) {
381    if(!ret.empty()) {
382      ret.append(1,' ');
383    }
384    unsigned char labellen=d_content.at(d_pos++);
385   
386    ret.append(1,'"');
387    string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1);
388   
389    ret.append(txtEscape(val)); // the end is one beyond the packet
390    ret.append(1,'"');
391    d_pos+=labellen;
392    if(!multi)
393      break;
394  }
395
396  return ret;
397}
398
399
400void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs) 
401{
402  if(recurs > 10)
403    throw MOADNSException("Loop");
404
405  for(;;) {
406    unsigned char labellen=content.at(frompos++);
407
408    if(!labellen) {
409      if(ret.empty())
410        ret.append(1,'.');
411      break;
412    }
413    if((labellen & 0xc0) == 0xc0) {
414      uint16_t offset=256*(labellen & ~0xc0) + (unsigned int)content.at(frompos++) - sizeof(dnsheader);
415      //        cout<<"This is an offset, need to go to: "<<offset<<endl;
416      return getLabelFromContent(content, offset, ret, ++recurs);
417    }
418    else {
419      // XXX FIXME THIS MIGHT BE VERY SLOW!
420      ret.reserve(ret.size() + labellen + 2);
421      for(string::size_type n = 0 ; n < labellen; ++n, frompos++) {
422        if(content.at(frompos)=='.')
423          ret.append(1, '\\');
424        ret.append(1, content[frompos]);
425      }
426      ret.append(1,'.');
427    }
428  }
429}
430
431void PacketReader::xfrBlob(string& blob)
432{
433  blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
434
435  d_pos = d_startrecordpos + d_recordlen;
436}
437
438void PacketReader::xfrHexBlob(string& blob)
439{
440  xfrBlob(blob);
441}
442
443string simpleCompress(const string& label, const string& root)
444{
445  typedef vector<pair<unsigned int, unsigned int> > parts_t;
446  parts_t parts;
447  vstringtok(parts, label, ".");
448  string ret;
449  ret.reserve(label.size()+4);
450  for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
451    if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + i->first, label.length() - i->first)) {
452      const char rootptr[2]={0xc0,0x11};
453      ret.append(rootptr, 2);
454      return ret;
455    }
456    ret.append(1, (char)(i->second - i->first));
457    ret.append(label.c_str() + i->first, i->second - i->first);
458  }
459  ret.append(1, (char)0);
460  return ret;
461}
462
463
464void simpleExpandTo(const string& label, unsigned int frompos, string& ret)
465{
466  unsigned int labellen=0;
467  while((labellen=label.at(frompos++))) {
468    ret.append(label.c_str()+frompos, labellen);
469    ret.append(1,'.');
470    frompos+=labellen;
471  }
472}
Note: See TracBrowser for help on using the browser.