]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnswriter.cc
clarify
[thirdparty/pdns.git] / pdns / dnswriter.cc
CommitLineData
a0a276c2
BH
1#include "dnswriter.hh"
2#include "misc.hh"
3#include "dnsparser.hh"
bac8f21b 4#include <boost/foreach.hpp>
a6c51664 5#include <limits.h>
a0a276c2 6
88c1bc50 7DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const string& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode)
7f5bf0ba 8 : d_pos(0), d_content(content), d_qname(qname), d_qtype(qtype), d_qclass(qclass), d_canonic(false), d_lowerCase(false)
a0a276c2
BH
9{
10 d_content.clear();
11 dnsheader dnsheader;
12
13 memset(&dnsheader, 0, sizeof(dnsheader));
8e97e9a3 14 dnsheader.id=0;
a0a276c2 15 dnsheader.qdcount=htons(1);
88c1bc50 16 dnsheader.opcode=opcode;
a0a276c2
BH
17
18 const uint8_t* ptr=(const uint8_t*)&dnsheader;
790e7c1b
BH
19 uint32_t len=d_content.size();
20 d_content.resize(len + sizeof(dnsheader));
21 uint8_t* dptr=(&*d_content.begin()) + len;
2f4c3abb 22
790e7c1b 23 memcpy(dptr, ptr, sizeof(dnsheader));
2f4c3abb 24 d_stuff=0;
b59d34f7 25
bca6643b 26 xfrLabel(qname, false);
790e7c1b
BH
27
28 len=d_content.size();
678ce973 29 d_content.resize(len + d_record.size() + 4);
e984b465 30
790e7c1b
BH
31 ptr=&*d_record.begin();
32 dptr=(&*d_content.begin()) + len;
33
34 memcpy(dptr, ptr, d_record.size());
790e7c1b 35
678ce973 36 len+=d_record.size();
bca6643b
BH
37 d_record.clear();
38
a0a276c2 39 qtype=htons(qtype);
a0a276c2 40 qclass=htons(qclass);
e984b465
BH
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);
ea634573
BH
46
47 d_stuff=0xffff;
a2ce25e4 48 d_labelmap.reserve(16);
a0a276c2
BH
49}
50
5a57d2ea 51dnsheader* DNSPacketWriter::getHeader()
8e97e9a3
BH
52{
53 return (dnsheader*)&*d_content.begin();
54}
55
f4d26b4f 56void DNSPacketWriter::startRecord(const string& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, Place place, bool compress)
a0a276c2
BH
57{
58 if(!d_record.empty())
59 commit();
878435ce 60
a0a276c2
BH
61 d_recordqname=name;
62 d_recordqtype=qtype;
63 d_recordqclass=qclass;
878435ce 64 d_recordttl=ttl;
10321a98 65 d_recordplace=place;
878435ce 66
f4d26b4f 67 d_stuff = 0;
10321a98 68 d_rollbackmarker=d_content.size();
bca6643b 69
f4d26b4f 70 if(compress && pdns_iequals(d_qname, d_recordqname)) { // don't do the whole label compression thing if we *know* we can get away with "see question"
6f724e8b
PD
71 static unsigned char marker[2]={0xc0, 0x0c};
72 d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]);
a2ce25e4
BH
73 }
74 else {
f4d26b4f 75 xfrLabel(d_recordqname, compress);
a2ce25e4
BH
76 d_content.insert(d_content.end(), d_record.begin(), d_record.end());
77 d_record.clear();
78 }
f4d26b4f 79
bca6643b 80 d_stuff = sizeof(dnsrecordheader); // this is needed to get compressed label offsets right, the dnsrecordheader will be interspersed
ea634573 81 d_sor=d_content.size() + d_stuff; // start of real record
878435ce 82}
a0a276c2 83
7f7b8d55 84void DNSPacketWriter::addOpt(int udpsize, int extRCode, int Z, const vector<pair<uint16_t,string> >& options)
878435ce
BH
85{
86 uint32_t ttl=0;
705f31ae 87
009f9f55 88 EDNS0Record stuff;
878435ce
BH
89
90 stuff.extRCode=extRCode;
91 stuff.version=0;
92 stuff.Z=htons(Z);
7f7b8d55 93
878435ce 94 memcpy(&ttl, &stuff, sizeof(stuff));
8a63d3ce 95
878435ce
BH
96 ttl=ntohl(ttl); // will be reversed later on
97
98 startRecord("", ns_t_opt, ttl, udpsize, ADDITIONAL);
7f7b8d55
BH
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 }
a0a276c2
BH
104}
105
341930bb
BH
106void DNSPacketWriter::xfr48BitInt(uint64_t val)
107{
108 unsigned char bytes[6];
a6e93c0f 109 uint16_t theLeft = htons((val >> 32)&0xffffU);
0407751c
BH
110 uint32_t theRight = htonl(val & 0xffffffffU);
111 memcpy(bytes, (void*)&theLeft, 2);
112 memcpy(bytes+2, (void*)&theRight, 4);
341930bb
BH
113
114 d_record.insert(d_record.end(), bytes, bytes + 6);
115}
116
117
a0a276c2
BH
118void DNSPacketWriter::xfr32BitInt(uint32_t val)
119{
ea634573
BH
120 int rval=htonl(val);
121 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
a0a276c2
BH
122 d_record.insert(d_record.end(), ptr, ptr+4);
123}
124
125void DNSPacketWriter::xfr16BitInt(uint16_t val)
126{
96aed220 127 uint16_t rval=htons(val);
ea634573 128 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
a0a276c2
BH
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
bac8f21b
BH
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 */
ef6a78d5 146void DNSPacketWriter::xfrText(const string& text, bool)
a0a276c2 147{
bac8f21b 148 if(text.empty()) {
ef6a78d5 149 d_record.push_back(0);
bac8f21b
BH
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());
ef6a78d5 156 }
a0a276c2
BH
157}
158
a2ce25e4
BH
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)
ec6480f3 163 if(pdns_iequals(ret->first ,label))
a2ce25e4
BH
164 break;
165 return ret;
166}
167
9bd9588f 168//! tokenize a label into parts, the parts describe a begin offset and an end offset
213f6de6 169bool labeltokUnescape(labelparts_t& parts, const string& label)
38e655b6
BH
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
a2ce25e4 194// this is the absolute hottest function in the pdns recursor
7f5bf0ba 195void DNSPacketWriter::xfrLabel(const string& Label, bool compress)
a0a276c2 196{
7f5bf0ba 197 string label = d_lowerCase ? toLower(Label) : Label;
213f6de6 198 labelparts_t parts;
c1d02c0d 199
f3f4938f
BH
200 if(d_canonic)
201 compress=false;
202
203 string::size_type labellen = label.size();
204 if(labellen==1 && label[0]=='.') { // otherwise we encode '..'
c1d02c0d
BH
205 d_record.push_back(0);
206 return;
207 }
38e655b6 208 bool unescaped=labeltokUnescape(parts, label);
bca6643b 209
e5bad90b
BH
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;
38e655b6 212 string chopped;
f3f4938f
BH
213 bool deDot = labellen && (label[labellen-1]=='.'); // make sure we don't store trailing dots in the labelmap
214
213f6de6 215 for(labelparts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
f3f4938f
BH
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
a2ce25e4 221 lmap_t::iterator li=d_labelmap.end();
8c3149f2 222 // see if we've written out this domain before
f3f4938f 223 // cerr<<"Searching for compression pointer to '"<<chopped<<"', "<<d_labelmap.size()<<" cmp-records"<<endl;
a2ce25e4 224 if(compress && (li=find(d_labelmap, chopped))!=d_labelmap.end()) {
f3f4938f 225 // cerr<<"\tFound a compression pointer to '"<<chopped<<"': "<<li->second<<endl;
bca6643b
BH
226 uint16_t offset=li->second;
227 offset|=0xc000;
8c3149f2
BH
228 d_record.push_back((char)(offset >> 8));
229 d_record.push_back((char)(offset & 0xff));
bca6643b
BH
230 goto out; // skip trailing 0 in case of compression
231 }
2f4c3abb 232
f3f4938f
BH
233 if(li==d_labelmap.end() && pos< 16384) {
234 // cerr<<"\tStoring a compression pointer to '"<<chopped<<"': "<<pos<<endl;
a2ce25e4 235 d_labelmap.push_back(make_pair(chopped, pos)); // if untrue, we need to count - also, don't store offsets > 16384, won't work
f3f4938f 236 }
a2ce25e4 237
38e655b6
BH
238 if(unescaped) {
239 string part(label.c_str() + i -> first, i->second - i->first);
27ff60a3 240 // FIXME: this relies on the semi-canonical escaped output from getLabelFromContent
646cbea6 241 boost::replace_all(part, "\\.", ".");
7ecd3576 242 boost::replace_all(part, "\\032", " ");
3d40879b 243 boost::replace_all(part, "\\\\", "\\");
213f6de6
BH
244 if(part.size() > 255)
245 throw MOADNSException("DNSPacketWriter::xfrLabel() tried to write an overly large label");
38e655b6
BH
246 d_record.push_back(part.size());
247 unsigned int len=d_record.size();
248 d_record.resize(len + part.size());
249
250 memcpy(((&*d_record.begin()) + len), part.c_str(), part.size());
4957a608 251 pos+=(part.size())+1;
38e655b6
BH
252 }
253 else {
91567562
PD
254 char labelsize=(char)(i->second - i->first);
255 if(!labelsize) // empty label in the middle of name
256 throw MOADNSException("DNSPacketWriter::xfrLabel() found empty label in the middle of name");
257 d_record.push_back(labelsize);
38e655b6
BH
258 unsigned int len=d_record.size();
259 d_record.resize(len + i->second - i->first);
260 memcpy(((&*d_record.begin()) + len), label.c_str() + i-> first, i->second - i->first);
261 pos+=(i->second - i->first)+1;
262 }
bca6643b 263 }
8c3149f2 264 d_record.push_back(0);
a0a276c2 265
bca6643b 266 out:;
a0a276c2
BH
267}
268
06ffdc52 269void DNSPacketWriter::xfrBlob(const string& blob, int )
8c1c9170
BH
270{
271 const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str());
8c1c9170
BH
272 d_record.insert(d_record.end(), ptr, ptr+blob.size());
273}
274
e4090157 275void DNSPacketWriter::xfrHexBlob(const string& blob, bool keepReading)
59a0f653
BH
276{
277 xfrBlob(blob);
278}
279
ea634573
BH
280void DNSPacketWriter::getRecords(string& records)
281{
282 records.assign(d_content.begin() + d_sor, d_content.end());
283}
a0a276c2 284
dffbaa08 285uint32_t DNSPacketWriter::size()
10321a98
BH
286{
287 return d_content.size() + d_stuff + d_record.size();
288}
289
290void DNSPacketWriter::rollback()
291{
292 d_content.resize(d_rollbackmarker);
293 d_record.clear();
294 d_stuff=0;
295}
296
a0a276c2
BH
297void DNSPacketWriter::commit()
298{
ea634573
BH
299 if(d_stuff==0xffff && (d_content.size()!=d_sor || !d_record.empty()))
300 throw MOADNSException("DNSPacketWriter::commit() called without startRecord ever having been called, but a record was added");
bca6643b 301 // build dnsrecordheader
a0a276c2
BH
302 struct dnsrecordheader drh;
303 drh.d_type=htons(d_recordqtype);
304 drh.d_class=htons(d_recordqclass);
878435ce 305 drh.d_ttl=htonl(d_recordttl);
a0a276c2 306 drh.d_clen=htons(d_record.size());
10321a98 307
bca6643b
BH
308 // and write out the header
309 const uint8_t* ptr=(const uint8_t*)&drh;
a0a276c2
BH
310 d_content.insert(d_content.end(), ptr, ptr+sizeof(drh));
311
10321a98
BH
312 d_stuff=0;
313
6f8b3628 314 // write out pending d_record
a0a276c2
BH
315 d_content.insert(d_content.end(), d_record.begin(), d_record.end());
316
10321a98
BH
317 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
318 switch(d_recordplace) {
319 case ANSWER:
320 dh->ancount = htons(ntohs(dh->ancount) + 1);
321 break;
322 case AUTHORITY:
323 dh->nscount = htons(ntohs(dh->nscount) + 1);
324 break;
325 case ADDITIONAL:
326 dh->arcount = htons(ntohs(dh->arcount) + 1);
327 break;
328 }
329
bca6643b 330 d_record.clear(); // clear d_record, ready for next record
a0a276c2
BH
331}
332
333
334
335
336
337