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