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