root/trunk/pdns/pdns/dnswriter.cc

Revision 2972, 9.5 KB (checked in by peter, 6 months ago)

make DNSPacketWriter::xfrLabel error out on empty labels in names

Line 
1#include "dnswriter.hh"
2#include "misc.hh"
3#include "dnsparser.hh"
4#include <boost/foreach.hpp>
5#include <limits.h>
6
7DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const string& qname, uint16_t  qtype, uint16_t qclass, uint8_t opcode)
8  : d_pos(0), d_content(content), d_qname(qname), d_qtype(qtype), d_qclass(qclass), d_canonic(false), d_lowerCase(false)
9{
10  d_content.clear();
11  dnsheader dnsheader;
12 
13  memset(&dnsheader, 0, sizeof(dnsheader));
14  dnsheader.id=0;
15  dnsheader.qdcount=htons(1);
16  dnsheader.opcode=opcode;
17 
18  const uint8_t* ptr=(const uint8_t*)&dnsheader;
19  uint32_t len=d_content.size();
20  d_content.resize(len + sizeof(dnsheader));
21  uint8_t* dptr=(&*d_content.begin()) + len;
22 
23  memcpy(dptr, ptr, sizeof(dnsheader));
24  d_stuff=0;
25
26  xfrLabel(qname, false);
27 
28  len=d_content.size();
29  d_content.resize(len + d_record.size() + 4);
30
31  ptr=&*d_record.begin();
32  dptr=(&*d_content.begin()) + len;
33 
34  memcpy(dptr, ptr, d_record.size());
35
36  len+=d_record.size();
37  d_record.clear();
38
39  qtype=htons(qtype);
40  qclass=htons(qclass);
41
42  vector<uint8_t>::iterator i=d_content.begin()+len; // this works around a gcc 3.4 bug
43  memcpy(&*i, &qtype, 2);
44  i+=2;
45  memcpy(&*i, &qclass, 2);
46
47  d_stuff=0xffff;
48  d_labelmap.reserve(16);
49}
50
51dnsheader* DNSPacketWriter::getHeader()
52{
53  return (dnsheader*)&*d_content.begin();
54}
55
56void DNSPacketWriter::startRecord(const string& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, Place place)
57{
58  if(!d_record.empty()) 
59    commit();
60
61  d_recordqname=name;
62  d_recordqtype=qtype;
63  d_recordqclass=qclass;
64  d_recordttl=ttl;
65  d_recordplace=place;
66
67  d_stuff = 0; 
68  d_rollbackmarker=d_content.size();
69
70  if(pdns_iequals(d_qname, d_recordqname)) {  // don't do the whole label compression thing if we *know* we can get away with "see question"
71    static char marker[2]={0xc0, 0x0c};
72    d_content.insert(d_content.end(), &marker[0], &marker[2]);
73  }
74  else {
75    xfrLabel(d_recordqname, true);
76    d_content.insert(d_content.end(), d_record.begin(), d_record.end());
77    d_record.clear();
78  }
79     
80  d_stuff = sizeof(dnsrecordheader); // this is needed to get compressed label offsets right, the dnsrecordheader will be interspersed
81  d_sor=d_content.size() + d_stuff; // start of real record
82}
83
84void DNSPacketWriter::addOpt(int udpsize, int extRCode, int Z, const vector<pair<uint16_t,string> >& options)
85{
86  uint32_t ttl=0;
87
88  EDNS0Record stuff;
89
90  stuff.extRCode=extRCode;
91  stuff.version=0;
92  stuff.Z=htons(Z);
93
94  memcpy(&ttl, &stuff, sizeof(stuff));
95
96  ttl=ntohl(ttl); // will be reversed later on
97 
98  startRecord("", ns_t_opt, ttl, udpsize, ADDITIONAL);
99  for(optvect_t::const_iterator iter = options.begin(); iter != options.end(); ++iter) {
100    xfr16BitInt(iter->first);
101    xfr16BitInt(iter->second.length());
102    xfrBlob(iter->second);
103  } 
104}
105
106void DNSPacketWriter::xfr48BitInt(uint64_t val)
107{
108  unsigned char bytes[6];
109  uint16_t theLeft = htons((val >> 32)&0xffffU);
110  uint32_t theRight = htonl(val & 0xffffffffU);
111  memcpy(bytes, (void*)&theLeft, 2);
112  memcpy(bytes+2, (void*)&theRight, 4);
113
114  d_record.insert(d_record.end(), bytes, bytes + 6);
115}
116
117
118void DNSPacketWriter::xfr32BitInt(uint32_t val)
119{
120  int rval=htonl(val);
121  uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
122  d_record.insert(d_record.end(), ptr, ptr+4);
123}
124
125void DNSPacketWriter::xfr16BitInt(uint16_t val)
126{
127  uint16_t rval=htons(val);
128  uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
129  d_record.insert(d_record.end(), ptr, ptr+2);
130}
131
132void DNSPacketWriter::xfr8BitInt(uint8_t val)
133{
134  d_record.push_back(val);
135}
136
137
138/* input:
139  "" -> 0
140  "blah" -> 4blah
141  "blah" "blah" -> output 4blah4blah
142  "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit)
143  "blah\"blah" -> 9blah"blah
144  "blah\97" -> 5blahb
145  */
146void DNSPacketWriter::xfrText(const string& text, bool)
147{
148  if(text.empty()) {
149    d_record.push_back(0);
150    return;
151  }
152  vector<string> segments = segmentDNSText(text);
153  BOOST_FOREACH(const string& str, segments) {
154    d_record.push_back(str.length());
155    d_record.insert(d_record.end(), str.c_str(), str.c_str() + str.length());
156  }
157}
158
159DNSPacketWriter::lmap_t::iterator find(DNSPacketWriter::lmap_t& lmap, const string& label)
160{
161  DNSPacketWriter::lmap_t::iterator ret;
162  for(ret=lmap.begin(); ret != lmap.end(); ++ret)
163    if(pdns_iequals(ret->first ,label))
164      break;
165  return ret;
166}
167
168//! tokenize a label into parts, the parts describe a begin offset and an end offset
169bool labeltokUnescape(labelparts_t& parts, const string& label)
170{
171  string::size_type epos = label.size(), lpos(0), pos;
172  bool unescapedSomething = false;
173  const char* ptr=label.c_str();
174
175  parts.clear();
176
177  for(pos = 0 ; pos < epos; ++pos) {
178    if(ptr[pos]=='\\') {
179      pos++;
180      unescapedSomething = true;
181      continue;
182    }
183    if(ptr[pos]=='.') {
184      parts.push_back(make_pair(lpos, pos));
185      lpos=pos+1;
186    }
187  }
188 
189  if(lpos < pos)
190    parts.push_back(make_pair(lpos, pos));
191  return unescapedSomething;
192}
193
194// this is the absolute hottest function in the pdns recursor
195void DNSPacketWriter::xfrLabel(const string& Label, bool compress)
196{
197  string label = d_lowerCase ? toLower(Label) : Label;
198  labelparts_t parts;
199
200  if(d_canonic)
201    compress=false;
202
203  string::size_type labellen = label.size();
204  if(labellen==1 && label[0]=='.') { // otherwise we encode '..'
205    d_record.push_back(0);
206    return;
207  }
208  bool unescaped=labeltokUnescape(parts, label); 
209 
210  // d_stuff is amount of stuff that is yet to be written out - the dnsrecordheader for example
211  unsigned int pos=d_content.size() + d_record.size() + d_stuff; 
212  string chopped;
213  bool deDot = labellen && (label[labellen-1]=='.'); // make sure we don't store trailing dots in the labelmap
214
215  for(labelparts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
216    if(deDot)
217      chopped.assign(label.c_str() + i->first, labellen - i->first -1);
218    else
219      chopped.assign(label.c_str() + i->first);
220
221    lmap_t::iterator li=d_labelmap.end();
222    // see if we've written out this domain before
223    //    cerr<<"Searching for compression pointer to '"<<chopped<<"', "<<d_labelmap.size()<<" cmp-records"<<endl;
224    if(compress && (li=find(d_labelmap, chopped))!=d_labelmap.end()) {   
225      //      cerr<<"\tFound a compression pointer to '"<<chopped<<"': "<<li->second<<endl;
226      uint16_t offset=li->second;
227      offset|=0xc000;
228      d_record.push_back((char)(offset >> 8));
229      d_record.push_back((char)(offset & 0xff));
230      goto out;                                 // skip trailing 0 in case of compression
231    }
232
233    if(li==d_labelmap.end() && pos< 16384) {
234      //      cerr<<"\tStoring a compression pointer to '"<<chopped<<"': "<<pos<<endl;
235      d_labelmap.push_back(make_pair(chopped, pos));                       //  if untrue, we need to count - also, don't store offsets > 16384, won't work
236    }
237
238    if(unescaped) {
239      string part(label.c_str() + i -> first, i->second - i->first);
240      boost::replace_all(part, "\\.", ".");
241      boost::replace_all(part, "\\032", " ");
242      boost::replace_all(part, "\\\\", "\\"); 
243      if(part.size() > 255)
244          throw MOADNSException("DNSPacketWriter::xfrLabel() tried to write an overly large label");
245      d_record.push_back(part.size());
246      unsigned int len=d_record.size();
247      d_record.resize(len + part.size());
248
249      memcpy(((&*d_record.begin()) + len), part.c_str(), part.size());
250      pos+=(part.size())+1;                     
251    }
252    else {
253      char labelsize=(char)(i->second - i->first);
254      if(!labelsize) // empty label in the middle of name
255        throw MOADNSException("DNSPacketWriter::xfrLabel() found empty label in the middle of name");
256      d_record.push_back(labelsize);
257      unsigned int len=d_record.size();
258      d_record.resize(len + i->second - i->first);
259      memcpy(((&*d_record.begin()) + len), label.c_str() + i-> first, i->second - i->first);
260      pos+=(i->second - i->first)+1;
261    }
262  }
263  d_record.push_back(0);
264
265 out:;
266}
267
268void DNSPacketWriter::xfrBlob(const string& blob, int  )
269{
270  const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str());
271  d_record.insert(d_record.end(), ptr, ptr+blob.size());
272}
273
274void DNSPacketWriter::xfrHexBlob(const string& blob, bool keepReading)
275{
276  xfrBlob(blob);
277}
278
279void DNSPacketWriter::getRecords(string& records)
280{
281  records.assign(d_content.begin() + d_sor, d_content.end());
282}
283
284uint32_t DNSPacketWriter::size()
285{
286  return d_content.size() + d_stuff + d_record.size();
287}
288
289void DNSPacketWriter::rollback()
290{
291  d_content.resize(d_rollbackmarker);
292  d_record.clear();
293  d_stuff=0;
294}
295
296void DNSPacketWriter::commit()
297{
298  if(d_stuff==0xffff && (d_content.size()!=d_sor || !d_record.empty()))
299    throw MOADNSException("DNSPacketWriter::commit() called without startRecord ever having been called, but a record was added");
300  // build dnsrecordheader
301  struct dnsrecordheader drh;
302  drh.d_type=htons(d_recordqtype);
303  drh.d_class=htons(d_recordqclass);
304  drh.d_ttl=htonl(d_recordttl);
305  drh.d_clen=htons(d_record.size());
306 
307  // and write out the header
308  const uint8_t* ptr=(const uint8_t*)&drh;
309  d_content.insert(d_content.end(), ptr, ptr+sizeof(drh));
310
311  d_stuff=0;
312
313  // write out pending d_record
314  d_content.insert(d_content.end(), d_record.begin(), d_record.end());
315
316  dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
317  switch(d_recordplace) {
318  case ANSWER:
319    dh->ancount = htons(ntohs(dh->ancount) + 1);
320    break;
321  case AUTHORITY:
322    dh->nscount = htons(ntohs(dh->nscount) + 1);
323    break;
324  case ADDITIONAL:
325    dh->arcount = htons(ntohs(dh->arcount) + 1);
326    break;
327  }
328
329  d_record.clear();   // clear d_record, ready for next record
330}
331
332
333
334
335
336
Note: See TracBrowser for help on using the browser.