]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnswriter.cc
rename d_positions, remove unused "d_pos"
[thirdparty/pdns.git] / pdns / dnswriter.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
8c0139b0 25#include <boost/container/static_vector.hpp>
a0a276c2
BH
26#include "dnswriter.hh"
27#include "misc.hh"
28#include "dnsparser.hh"
8c0139b0 29
a6c51664 30#include <limits.h>
a0a276c2 31
c2f3be9d 32DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode)
fea4599a 33 : d_content(content), d_qname(qname), d_canonic(false), d_lowerCase(false)
a0a276c2
BH
34{
35 d_content.clear();
36 dnsheader dnsheader;
950cfe0f 37
a0a276c2 38 memset(&dnsheader, 0, sizeof(dnsheader));
8e97e9a3 39 dnsheader.id=0;
a0a276c2 40 dnsheader.qdcount=htons(1);
88c1bc50 41 dnsheader.opcode=opcode;
950cfe0f 42
a0a276c2 43 const uint8_t* ptr=(const uint8_t*)&dnsheader;
790e7c1b
BH
44 uint32_t len=d_content.size();
45 d_content.resize(len + sizeof(dnsheader));
46 uint8_t* dptr=(&*d_content.begin()) + len;
950cfe0f 47
790e7c1b 48 memcpy(dptr, ptr, sizeof(dnsheader));
2f4c3abb 49 d_stuff=0;
fea4599a 50 d_namepositions.reserve(16);
ad8fa726 51 xfrName(qname, false);
950cfe0f 52
790e7c1b 53 len=d_content.size();
678ce973 54 d_content.resize(len + d_record.size() + 4);
e984b465 55
790e7c1b
BH
56 ptr=&*d_record.begin();
57 dptr=(&*d_content.begin()) + len;
950cfe0f 58
790e7c1b 59 memcpy(dptr, ptr, d_record.size());
790e7c1b 60
678ce973 61 len+=d_record.size();
bca6643b
BH
62 d_record.clear();
63
a0a276c2 64 qtype=htons(qtype);
a0a276c2 65 qclass=htons(qclass);
e984b465
BH
66
67 vector<uint8_t>::iterator i=d_content.begin()+len; // this works around a gcc 3.4 bug
68 memcpy(&*i, &qtype, 2);
69 i+=2;
70 memcpy(&*i, &qclass, 2);
ea634573
BH
71
72 d_stuff=0xffff;
add935a2 73 d_truncatemarker=d_content.size();
0eabbd4b
AT
74 d_sor = 0;
75 d_rollbackmarker = 0;
76 d_recordttl = 0;
77 d_recordqtype = 0;
78 d_recordqclass = QClass::IN;
e693ff5a 79 d_recordplace = DNSResourceRecord::ANSWER;
a0a276c2
BH
80}
81
5a57d2ea 82dnsheader* DNSPacketWriter::getHeader()
8e97e9a3 83{
b346905c 84 return reinterpret_cast<dnsheader*>(&*d_content.begin());
8e97e9a3
BH
85}
86
e693ff5a 87void DNSPacketWriter::startRecord(const DNSName& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, DNSResourceRecord::Place place, bool compress)
a0a276c2 88{
950cfe0f 89 if(!d_record.empty())
a0a276c2 90 commit();
878435ce 91
a0a276c2
BH
92 d_recordqname=name;
93 d_recordqtype=qtype;
94 d_recordqclass=qclass;
878435ce 95 d_recordttl=ttl;
10321a98 96 d_recordplace=place;
878435ce 97
f4d26b4f 98 d_stuff = 0;
10321a98 99 d_rollbackmarker=d_content.size();
bca6643b 100
c2f3be9d 101 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
102 static unsigned char marker[2]={0xc0, 0x0c};
103 d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]);
a2ce25e4
BH
104 }
105 else {
ad8fa726 106 xfrName(d_recordqname, compress);
a2ce25e4
BH
107 d_content.insert(d_content.end(), d_record.begin(), d_record.end());
108 d_record.clear();
109 }
f4d26b4f 110
bca6643b 111 d_stuff = sizeof(dnsrecordheader); // this is needed to get compressed label offsets right, the dnsrecordheader will be interspersed
950cfe0f 112 d_sor=d_content.size() + d_stuff; // start of real record
878435ce 113}
a0a276c2 114
a683e8bd 115void DNSPacketWriter::addOpt(uint16_t udpsize, int extRCode, int Z, const vector<pair<uint16_t,string> >& options)
878435ce
BH
116{
117 uint32_t ttl=0;
705f31ae 118
009f9f55 119 EDNS0Record stuff;
878435ce
BH
120
121 stuff.extRCode=extRCode;
122 stuff.version=0;
123 stuff.Z=htons(Z);
7f7b8d55 124
a683e8bd 125 static_assert(sizeof(EDNS0Record) == sizeof(ttl), "sizeof(EDNS0Record) must match sizeof(ttl)");
878435ce 126 memcpy(&ttl, &stuff, sizeof(stuff));
8a63d3ce 127
878435ce 128 ttl=ntohl(ttl); // will be reversed later on
950cfe0f 129
12c06211 130 startRecord(g_rootdnsname, QType::OPT, ttl, udpsize, DNSResourceRecord::ADDITIONAL, false);
7f7b8d55
BH
131 for(optvect_t::const_iterator iter = options.begin(); iter != options.end(); ++iter) {
132 xfr16BitInt(iter->first);
133 xfr16BitInt(iter->second.length());
134 xfrBlob(iter->second);
950cfe0f 135 }
a0a276c2
BH
136}
137
341930bb
BH
138void DNSPacketWriter::xfr48BitInt(uint64_t val)
139{
140 unsigned char bytes[6];
a6e93c0f 141 uint16_t theLeft = htons((val >> 32)&0xffffU);
0407751c 142 uint32_t theRight = htonl(val & 0xffffffffU);
a683e8bd
RG
143 memcpy(bytes, (void*)&theLeft, sizeof(theLeft));
144 memcpy(bytes+2, (void*)&theRight, sizeof(theRight));
341930bb 145
a683e8bd 146 d_record.insert(d_record.end(), bytes, bytes + sizeof(bytes));
341930bb
BH
147}
148
149
a0a276c2
BH
150void DNSPacketWriter::xfr32BitInt(uint32_t val)
151{
ea634573
BH
152 int rval=htonl(val);
153 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
a0a276c2
BH
154 d_record.insert(d_record.end(), ptr, ptr+4);
155}
156
157void DNSPacketWriter::xfr16BitInt(uint16_t val)
158{
96aed220 159 uint16_t rval=htons(val);
ea634573 160 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
a0a276c2
BH
161 d_record.insert(d_record.end(), ptr, ptr+2);
162}
163
164void DNSPacketWriter::xfr8BitInt(uint8_t val)
165{
166 d_record.push_back(val);
167}
168
bac8f21b 169
950cfe0f 170/* input:
84e1142d 171 if lenField is true
bac8f21b
BH
172 "" -> 0
173 "blah" -> 4blah
174 "blah" "blah" -> output 4blah4blah
175 "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit)
176 "blah\"blah" -> 9blah"blah
177 "blah\97" -> 5blahb
84e1142d
PL
178
179 if lenField is false
180 "blah" -> blah
181 "blah\"blah" -> blah"blah
bac8f21b 182 */
84e1142d 183void DNSPacketWriter::xfrText(const string& text, bool, bool lenField)
a0a276c2 184{
bac8f21b 185 if(text.empty()) {
ef6a78d5 186 d_record.push_back(0);
bac8f21b
BH
187 return;
188 }
189 vector<string> segments = segmentDNSText(text);
ef7cd021 190 for(const string& str : segments) {
84e1142d
PL
191 if(lenField)
192 d_record.push_back(str.length());
bac8f21b 193 d_record.insert(d_record.end(), str.c_str(), str.c_str() + str.length());
ef6a78d5 194 }
a0a276c2
BH
195}
196
948a927f
PL
197void DNSPacketWriter::xfrUnquotedText(const string& text, bool lenField)
198{
199 if(text.empty()) {
200 d_record.push_back(0);
201 return;
202 }
203 if(lenField)
204 d_record.push_back(text.length());
205 d_record.insert(d_record.end(), text.c_str(), text.c_str() + text.length());
206}
207
802a93d0 208
209static constexpr bool l_verbose=false;
210uint16_t DNSPacketWriter::lookupName(const DNSName& name, uint16_t* matchLen)
a2ce25e4 211{
802a93d0 212 // iterate over the written labels, see if we find a match
213 const auto& raw = name.getStorage();
214
215 /* name might be a.root-servers.net, we need to be able to benefit from finding:
216 b.root-servers.net, or even:
217 b\xc0\x0c
218 */
219 unsigned int bestpos=0;
220 *matchLen=0;
221 boost::container::static_vector<uint16_t, 34> nvect, pvect;
222
223 for(auto riter= raw.cbegin(); riter < raw.cend(); ) {
224 if(!*riter)
a2ce25e4 225 break;
802a93d0 226 nvect.push_back(riter - raw.cbegin());
227 riter+=*riter+1;
228 }
229
230 if(l_verbose) {
231 cout<<"Input vector for lookup "<<name<<": ";
232 for(const auto n : nvect)
233 cout << n<<" ";
234 cout<<endl;
235 cout<<makeHexDump(string(raw.c_str(), raw.c_str()+raw.size()))<<endl;
236 }
a2ce25e4 237
802a93d0 238 if(l_verbose)
fea4599a 239 cout<<"Have "<<d_namepositions.size()<<" to ponder"<<endl;
802a93d0 240 int counter=1;
fea4599a 241 for(auto p : d_namepositions) {
802a93d0 242 vector<uint8_t>* source=0;
243 if(p < d_content.size())
244 source = &d_content;
245 else {
246 source = &d_record;
247 p-= (d_content.size() + d_stuff);
248
249 }
250 if(l_verbose) {
251 if(source == &d_content) {
252 DNSName pname((const char*)&(*source)[0], (*source).size(), p, true); // only for debugging
fea4599a 253 cout<<"Looking at '"<<pname<<"' in packet at position "<<p<<"/"<<(*source).size()<<", option "<<counter<<"/"<<d_namepositions.size()<<endl;
802a93d0 254 }
255 else
256 {
fea4599a 257 cout<<"Looking at *record* at position "<<p<<"/"<<(*source).size()<<", option "<<counter<<"/"<<d_namepositions.size()<<endl;
802a93d0 258 }
259 ++counter;
260 }
261 // memcmp here makes things _slower_
262 pvect.clear();
263 for(auto iter = (*source).cbegin() + p; iter < (*source).cend();) {
264
265 uint8_t c=*iter;
266 if(l_verbose)
267 cout<<"Found label length: "<<(int)c<<endl;
268 if(c & 0xc0) {
269
270 uint16_t npos = 0x100*(c & (~0xc0)) + *++iter;
271 iter = (*source).begin() + npos;
272 if(l_verbose)
273 cout<<"Is compressed label to newpos "<<npos<<", going there"<<endl;
274 // check against going forward here
275 continue;
276 }
277 if(!c)
278 break;
279 pvect.push_back(iter - (*source).cbegin());
280 iter+=*iter+1;
281 }
282 if(l_verbose) {
283 cout<<"Packet vector: "<<endl;
284 for(const auto n : pvect)
285 cout << n<<" ";
286 cout<<endl;
287 }
288 auto niter=nvect.crbegin(), piter=pvect.crbegin();
289 unsigned int cmatchlen=1;
290 for(; niter != nvect.crend() && piter != pvect.crend(); ++niter, ++piter) {
291 // niter is an offset in raw, pvect an offset in packet
292 uint8_t nlen = raw[*niter], plen=(*source)[*piter];
293 if(l_verbose)
294 cout<<"nlnen="<<(int)nlen<<", plen="<<(int)plen<<endl;
295 if(nlen != plen)
296 break;
297 if(strncasecmp(raw.c_str()+*niter+1, (const char*)&(*source)[*piter]+1, nlen)) {
298 if(l_verbose)
299 cout<<"Mismatch: "<<string(raw.c_str()+*niter+1, raw.c_str()+*niter+nlen+1)<< " != "<<string((const char*)&(*source)[*piter]+1, (const char*)&(*source)[*piter]+nlen+1)<<endl;
300 break;
301 }
302 cmatchlen+=nlen+1;
303 if(cmatchlen == raw.length()) { // have matched all of it, can't improve
304 if(l_verbose)
305 cout<<"Stopping search, matched whole name"<<endl;
306 *matchLen = cmatchlen;
307 return *piter;
308 }
309 }
310 if(piter != pvect.crbegin() && *matchLen < cmatchlen) {
311 *matchLen = cmatchlen;
312 bestpos=*--piter;
313 }
314 }
315 return bestpos;
316}
38e655b6 317
950cfe0f 318// this is the absolute hottest function in the pdns recursor
f21fc0aa 319void DNSPacketWriter::xfrName(const DNSName& name, bool compress, bool)
38e655b6 320{
802a93d0 321 if(l_verbose)
322 cout<<"Wants to write "<<name<<", compress="<<compress<<", canonic="<<d_canonic<<", LC="<<d_lowerCase<<endl;
323 if(d_canonic || d_lowerCase) // d_lowerCase implies canonic
f3f4938f
BH
324 compress=false;
325
802a93d0 326 if(name.empty() || name.isRoot()) { // for speed
c1d02c0d
BH
327 d_record.push_back(0);
328 return;
329 }
950cfe0f 330
802a93d0 331 uint16_t li=0;
332 uint16_t matchlen=0;
333 if(compress && (li=lookupName(name, &matchlen))) {
334 const auto& dns=name.getStorage();
335 if(l_verbose)
336 cout<<"Found a substring of "<<matchlen<<" bytes from the back, offset: "<<li<<", dnslen: "<<dns.size()<<endl;
337 // found a substring, if www.powerdns.com matched powerdns.com, we get back matchlen = 13
338
339 unsigned int pos=d_content.size() + d_record.size() + d_stuff;
340 if(pos < 16384 && matchlen != dns.size()) {
341 if(l_verbose)
342 cout<<"Inserting pos "<<pos<<" for "<<name<<" for compressed case"<<endl;
fea4599a 343 d_namepositions.push_back(pos);
bca6643b 344 }
2f4c3abb 345
802a93d0 346 if(l_verbose)
347 cout<<"Going to write unique part: '"<<makeHexDump(string(dns.c_str(), dns.c_str() + dns.size() - matchlen)) <<"'"<<endl;
348 d_record.insert(d_record.end(), (const unsigned char*)dns.c_str(), (const unsigned char*)dns.c_str() + dns.size() - matchlen);
349 uint16_t offset=li;
350 offset|=0xc000;
a2ce25e4 351
802a93d0 352 d_record.push_back((char)(offset >> 8));
353 d_record.push_back((char)(offset & 0xff));
bca6643b 354 }
802a93d0 355 else {
356 unsigned int pos=d_content.size() + d_record.size() + d_stuff;
357 if(l_verbose)
358 cout<<"Found nothing, we are at pos "<<pos<<", inserting whole name"<<endl;
359 if(pos < 16384) {
360 if(l_verbose)
361 cout<<"Inserting pos "<<pos<<" for "<<name<<" for uncompressed case"<<endl;
fea4599a 362 d_namepositions.push_back(pos);
802a93d0 363 }
5be843df 364
802a93d0 365 std::unique_ptr<DNSName> lc;
366 if(d_lowerCase)
367 lc = make_unique<DNSName>(name.makeLowerCase());
a0a276c2 368
802a93d0 369 const DNSName::string_t& raw = (lc ? *lc : name).getStorage();
370 if(l_verbose)
371 cout<<"Writing out the whole thing "<<makeHexDump(string(raw.c_str(), raw.c_str() + raw.length()))<<endl;
372 d_record.insert(d_record.end(), raw.c_str(), raw.c_str() + raw.size());
373 }
a0a276c2
BH
374}
375
06ffdc52 376void DNSPacketWriter::xfrBlob(const string& blob, int )
8c1c9170
BH
377{
378 const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str());
8c1c9170
BH
379 d_record.insert(d_record.end(), ptr, ptr+blob.size());
380}
381
2fe9d6f7
AT
382void DNSPacketWriter::xfrBlobNoSpaces(const string& blob, int )
383{
384 xfrBlob(blob);
385}
386
e4090157 387void DNSPacketWriter::xfrHexBlob(const string& blob, bool keepReading)
59a0f653
BH
388{
389 xfrBlob(blob);
390}
391
ea634573
BH
392void DNSPacketWriter::getRecords(string& records)
393{
394 records.assign(d_content.begin() + d_sor, d_content.end());
395}
a0a276c2 396
dffbaa08 397uint32_t DNSPacketWriter::size()
10321a98
BH
398{
399 return d_content.size() + d_stuff + d_record.size();
400}
401
402void DNSPacketWriter::rollback()
403{
404 d_content.resize(d_rollbackmarker);
405 d_record.clear();
406 d_stuff=0;
407}
408
add935a2
PD
409void DNSPacketWriter::truncate()
410{
411 d_content.resize(d_truncatemarker);
412 d_record.clear();
413 d_stuff=0;
414 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
415 dh->ancount = dh->nscount = dh->arcount = 0;
416}
417
a0a276c2
BH
418void DNSPacketWriter::commit()
419{
ea634573
BH
420 if(d_stuff==0xffff && (d_content.size()!=d_sor || !d_record.empty()))
421 throw MOADNSException("DNSPacketWriter::commit() called without startRecord ever having been called, but a record was added");
bca6643b 422 // build dnsrecordheader
a0a276c2
BH
423 struct dnsrecordheader drh;
424 drh.d_type=htons(d_recordqtype);
425 drh.d_class=htons(d_recordqclass);
878435ce 426 drh.d_ttl=htonl(d_recordttl);
a0a276c2 427 drh.d_clen=htons(d_record.size());
950cfe0f 428
bca6643b
BH
429 // and write out the header
430 const uint8_t* ptr=(const uint8_t*)&drh;
a0a276c2
BH
431 d_content.insert(d_content.end(), ptr, ptr+sizeof(drh));
432
10321a98
BH
433 d_stuff=0;
434
6f8b3628 435 // write out pending d_record
a0a276c2
BH
436 d_content.insert(d_content.end(), d_record.begin(), d_record.end());
437
10321a98
BH
438 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
439 switch(d_recordplace) {
e693ff5a
AT
440 case DNSResourceRecord::QUESTION:
441 dh->qdcount = htons(ntohs(dh->qdcount) + 1);
442 break;
443 case DNSResourceRecord::ANSWER:
10321a98
BH
444 dh->ancount = htons(ntohs(dh->ancount) + 1);
445 break;
e693ff5a 446 case DNSResourceRecord::AUTHORITY:
10321a98
BH
447 dh->nscount = htons(ntohs(dh->nscount) + 1);
448 break;
e693ff5a 449 case DNSResourceRecord::ADDITIONAL:
10321a98
BH
450 dh->arcount = htons(ntohs(dh->arcount) + 1);
451 break;
452 }
453
bca6643b 454 d_record.clear(); // clear d_record, ready for next record
a0a276c2 455}