]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnswriter.cc
Add some notes explaining why some validations are not relevant in the dnstap case.
[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
b8cd1d8f
PL
25#include <boost/version.hpp>
26#if BOOST_VERSION >= 105400
8c0139b0 27#include <boost/container/static_vector.hpp>
b8cd1d8f 28#endif
a0a276c2
BH
29#include "dnswriter.hh"
30#include "misc.hh"
31#include "dnsparser.hh"
8c0139b0 32
a6c51664 33#include <limits.h>
a0a276c2 34
e636cab2 35/* d_content: <---- d_stuff ---->
36 v d_truncatemarker
37 dnsheader | qname | qtype | qclass | {recordname| dnsrecordheader | record }
38 ^ d_rollbackmarker ^ d_sor
39
40
41*/
42
43
c2f3be9d 44DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode)
fea4599a 45 : d_content(content), d_qname(qname), d_canonic(false), d_lowerCase(false)
a0a276c2
BH
46{
47 d_content.clear();
48 dnsheader dnsheader;
950cfe0f 49
a0a276c2 50 memset(&dnsheader, 0, sizeof(dnsheader));
8e97e9a3 51 dnsheader.id=0;
a0a276c2 52 dnsheader.qdcount=htons(1);
88c1bc50 53 dnsheader.opcode=opcode;
950cfe0f 54
a0a276c2 55 const uint8_t* ptr=(const uint8_t*)&dnsheader;
790e7c1b
BH
56 uint32_t len=d_content.size();
57 d_content.resize(len + sizeof(dnsheader));
58 uint8_t* dptr=(&*d_content.begin()) + len;
950cfe0f 59
790e7c1b 60 memcpy(dptr, ptr, sizeof(dnsheader));
fea4599a 61 d_namepositions.reserve(16);
ad8fa726 62 xfrName(qname, false);
e636cab2 63 xfr16BitInt(qtype);
64 xfr16BitInt(qclass);
950cfe0f 65
add935a2 66 d_truncatemarker=d_content.size();
0eabbd4b
AT
67 d_sor = 0;
68 d_rollbackmarker = 0;
a0a276c2
BH
69}
70
5a57d2ea 71dnsheader* DNSPacketWriter::getHeader()
8e97e9a3 72{
b346905c 73 return reinterpret_cast<dnsheader*>(&*d_content.begin());
8e97e9a3
BH
74}
75
e636cab2 76
e693ff5a 77void DNSPacketWriter::startRecord(const DNSName& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, DNSResourceRecord::Place place, bool compress)
a0a276c2 78{
e636cab2 79 commit();
10321a98 80 d_rollbackmarker=d_content.size();
bca6643b 81
2d6c4fcb 82 if(compress && !name.isRoot() && d_qname==name) { // 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
83 static unsigned char marker[2]={0xc0, 0x0c};
84 d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]);
a2ce25e4
BH
85 }
86 else {
e636cab2 87 xfrName(name, compress);
a2ce25e4 88 }
e636cab2 89 xfr16BitInt(qtype);
90 xfr16BitInt(qclass);
91 xfr32BitInt(ttl);
92 xfr16BitInt(0); // this will be the record size
93 d_recordplace = place;
94 d_sor=d_content.size(); // this will remind us where to stuff the record size
878435ce 95}
a0a276c2 96
d6c335ab 97void DNSPacketWriter::addOpt(const uint16_t udpsize, const uint16_t extRCode, const uint16_t ednsFlags, const optvect_t& options, const uint8_t version)
878435ce
BH
98{
99 uint32_t ttl=0;
705f31ae 100
009f9f55 101 EDNS0Record stuff;
878435ce 102
d6c335ab
PL
103 stuff.version = version;
104 stuff.extFlags = htons(ednsFlags);
7f7b8d55 105
f1db0de2
PL
106 /* RFC 6891 section 4 on the Extended RCode wire format
107 * EXTENDED-RCODE
108 * Forms the upper 8 bits of extended 12-bit RCODE (together with the
109 * 4 bits defined in [RFC1035]. Note that EXTENDED-RCODE value 0
110 * indicates that an unextended RCODE is in use (values 0 through 15).
111 */
d6c335ab 112 // XXX Should be check for extRCode > 1<<12 ?
f1db0de2 113 stuff.extRCode = extRCode>>4;
d0983bff 114 if (extRCode != 0) { // As this trumps the existing RCODE
f1db0de2
PL
115 getHeader()->rcode = extRCode;
116 }
117
a683e8bd 118 static_assert(sizeof(EDNS0Record) == sizeof(ttl), "sizeof(EDNS0Record) must match sizeof(ttl)");
878435ce 119 memcpy(&ttl, &stuff, sizeof(stuff));
8a63d3ce 120
878435ce 121 ttl=ntohl(ttl); // will be reversed later on
950cfe0f 122
12c06211 123 startRecord(g_rootdnsname, QType::OPT, ttl, udpsize, DNSResourceRecord::ADDITIONAL, false);
d6c335ab
PL
124 for(auto const &option : options) {
125 xfr16BitInt(option.first);
126 xfr16BitInt(option.second.length());
127 xfrBlob(option.second);
950cfe0f 128 }
a0a276c2
BH
129}
130
341930bb
BH
131void DNSPacketWriter::xfr48BitInt(uint64_t val)
132{
133 unsigned char bytes[6];
a6e93c0f 134 uint16_t theLeft = htons((val >> 32)&0xffffU);
0407751c 135 uint32_t theRight = htonl(val & 0xffffffffU);
a683e8bd
RG
136 memcpy(bytes, (void*)&theLeft, sizeof(theLeft));
137 memcpy(bytes+2, (void*)&theRight, sizeof(theRight));
341930bb 138
e636cab2 139 d_content.insert(d_content.end(), bytes, bytes + sizeof(bytes));
341930bb
BH
140}
141
142
a0a276c2
BH
143void DNSPacketWriter::xfr32BitInt(uint32_t val)
144{
47698274 145 uint32_t rval=htonl(val);
ea634573 146 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
e636cab2 147 d_content.insert(d_content.end(), ptr, ptr+4);
a0a276c2
BH
148}
149
150void DNSPacketWriter::xfr16BitInt(uint16_t val)
151{
96aed220 152 uint16_t rval=htons(val);
ea634573 153 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
e636cab2 154 d_content.insert(d_content.end(), ptr, ptr+2);
a0a276c2
BH
155}
156
157void DNSPacketWriter::xfr8BitInt(uint8_t val)
158{
e636cab2 159 d_content.push_back(val);
a0a276c2
BH
160}
161
bac8f21b 162
950cfe0f 163/* input:
84e1142d 164 if lenField is true
bac8f21b
BH
165 "" -> 0
166 "blah" -> 4blah
167 "blah" "blah" -> output 4blah4blah
168 "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit)
169 "blah\"blah" -> 9blah"blah
170 "blah\97" -> 5blahb
84e1142d
PL
171
172 if lenField is false
173 "blah" -> blah
174 "blah\"blah" -> blah"blah
bac8f21b 175 */
84e1142d 176void DNSPacketWriter::xfrText(const string& text, bool, bool lenField)
a0a276c2 177{
bac8f21b 178 if(text.empty()) {
e636cab2 179 d_content.push_back(0);
bac8f21b
BH
180 return;
181 }
182 vector<string> segments = segmentDNSText(text);
ef7cd021 183 for(const string& str : segments) {
84e1142d 184 if(lenField)
e636cab2 185 d_content.push_back(str.length());
186 d_content.insert(d_content.end(), str.c_str(), str.c_str() + str.length());
ef6a78d5 187 }
a0a276c2
BH
188}
189
948a927f
PL
190void DNSPacketWriter::xfrUnquotedText(const string& text, bool lenField)
191{
192 if(text.empty()) {
e636cab2 193 d_content.push_back(0);
948a927f
PL
194 return;
195 }
196 if(lenField)
e636cab2 197 d_content.push_back(text.length());
198 d_content.insert(d_content.end(), text.c_str(), text.c_str() + text.length());
948a927f
PL
199}
200
802a93d0 201
202static constexpr bool l_verbose=false;
203uint16_t DNSPacketWriter::lookupName(const DNSName& name, uint16_t* matchLen)
a2ce25e4 204{
802a93d0 205 // iterate over the written labels, see if we find a match
206 const auto& raw = name.getStorage();
207
208 /* name might be a.root-servers.net, we need to be able to benefit from finding:
209 b.root-servers.net, or even:
210 b\xc0\x0c
211 */
212 unsigned int bestpos=0;
213 *matchLen=0;
b8cd1d8f 214#if BOOST_VERSION >= 105400
802a93d0 215 boost::container::static_vector<uint16_t, 34> nvect, pvect;
b8cd1d8f
PL
216#else
217 vector<uint16_t> nvect, pvect;
218#endif
802a93d0 219
98cda0a9 220 try {
221 for(auto riter= raw.cbegin(); riter < raw.cend(); ) {
222 if(!*riter)
223 break;
224 nvect.push_back(riter - raw.cbegin());
225 riter+=*riter+1;
226 }
227 }
228 catch(std::bad_alloc& ba) {
229 if(l_verbose)
230 cout<<"Domain "<<name<<" too large to compress"<<endl;
231 return 0;
802a93d0 232 }
233
234 if(l_verbose) {
235 cout<<"Input vector for lookup "<<name<<": ";
236 for(const auto n : nvect)
237 cout << n<<" ";
238 cout<<endl;
239 cout<<makeHexDump(string(raw.c_str(), raw.c_str()+raw.size()))<<endl;
240 }
a2ce25e4 241
802a93d0 242 if(l_verbose)
fea4599a 243 cout<<"Have "<<d_namepositions.size()<<" to ponder"<<endl;
802a93d0 244 int counter=1;
fea4599a 245 for(auto p : d_namepositions) {
802a93d0 246 if(l_verbose) {
e636cab2 247 cout<<"Pos: "<<p<<", "<<d_content.size()<<endl;
248 DNSName pname((const char*)&d_content[0], d_content.size(), p, true); // only for debugging
249 cout<<"Looking at '"<<pname<<"' in packet at position "<<p<<"/"<<d_content.size()<<", option "<<counter<<"/"<<d_namepositions.size()<<endl;
802a93d0 250 ++counter;
251 }
252 // memcmp here makes things _slower_
253 pvect.clear();
98cda0a9 254 try {
255 for(auto iter = d_content.cbegin() + p; iter < d_content.cend();) {
256 uint8_t c=*iter;
802a93d0 257 if(l_verbose)
98cda0a9 258 cout<<"Found label length: "<<(int)c<<endl;
259 if(c & 0xc0) {
260 uint16_t npos = 0x100*(c & (~0xc0)) + *++iter;
261 iter = d_content.begin() + npos;
262 if(l_verbose)
263 cout<<"Is compressed label to newpos "<<npos<<", going there"<<endl;
264 // check against going forward here
265 continue;
266 }
267 if(!c)
268 break;
269 pvect.push_back(iter - d_content.cbegin());
270 iter+=*iter+1;
802a93d0 271 }
98cda0a9 272 }
273 catch(std::bad_alloc& ba) {
274 if(l_verbose)
275 cout<<"Domain "<<name<<" too large to compress"<<endl;
276 continue;
802a93d0 277 }
278 if(l_verbose) {
279 cout<<"Packet vector: "<<endl;
280 for(const auto n : pvect)
281 cout << n<<" ";
282 cout<<endl;
283 }
284 auto niter=nvect.crbegin(), piter=pvect.crbegin();
285 unsigned int cmatchlen=1;
286 for(; niter != nvect.crend() && piter != pvect.crend(); ++niter, ++piter) {
287 // niter is an offset in raw, pvect an offset in packet
e636cab2 288 uint8_t nlen = raw[*niter], plen=d_content[*piter];
802a93d0 289 if(l_verbose)
290 cout<<"nlnen="<<(int)nlen<<", plen="<<(int)plen<<endl;
291 if(nlen != plen)
292 break;
e636cab2 293 if(strncasecmp(raw.c_str()+*niter+1, (const char*)&d_content[*piter]+1, nlen)) {
802a93d0 294 if(l_verbose)
e636cab2 295 cout<<"Mismatch: "<<string(raw.c_str()+*niter+1, raw.c_str()+*niter+nlen+1)<< " != "<<string((const char*)&d_content[*piter]+1, (const char*)&d_content[*piter]+nlen+1)<<endl;
802a93d0 296 break;
297 }
298 cmatchlen+=nlen+1;
299 if(cmatchlen == raw.length()) { // have matched all of it, can't improve
300 if(l_verbose)
301 cout<<"Stopping search, matched whole name"<<endl;
302 *matchLen = cmatchlen;
303 return *piter;
304 }
305 }
306 if(piter != pvect.crbegin() && *matchLen < cmatchlen) {
307 *matchLen = cmatchlen;
308 bestpos=*--piter;
309 }
310 }
311 return bestpos;
312}
950cfe0f 313// this is the absolute hottest function in the pdns recursor
f21fc0aa 314void DNSPacketWriter::xfrName(const DNSName& name, bool compress, bool)
38e655b6 315{
802a93d0 316 if(l_verbose)
317 cout<<"Wants to write "<<name<<", compress="<<compress<<", canonic="<<d_canonic<<", LC="<<d_lowerCase<<endl;
318 if(d_canonic || d_lowerCase) // d_lowerCase implies canonic
f3f4938f
BH
319 compress=false;
320
802a93d0 321 if(name.empty() || name.isRoot()) { // for speed
e636cab2 322 d_content.push_back(0);
c1d02c0d
BH
323 return;
324 }
950cfe0f 325
802a93d0 326 uint16_t li=0;
327 uint16_t matchlen=0;
328 if(compress && (li=lookupName(name, &matchlen))) {
329 const auto& dns=name.getStorage();
330 if(l_verbose)
331 cout<<"Found a substring of "<<matchlen<<" bytes from the back, offset: "<<li<<", dnslen: "<<dns.size()<<endl;
332 // found a substring, if www.powerdns.com matched powerdns.com, we get back matchlen = 13
333
e636cab2 334 unsigned int pos=d_content.size();
802a93d0 335 if(pos < 16384 && matchlen != dns.size()) {
336 if(l_verbose)
337 cout<<"Inserting pos "<<pos<<" for "<<name<<" for compressed case"<<endl;
fea4599a 338 d_namepositions.push_back(pos);
bca6643b 339 }
2f4c3abb 340
802a93d0 341 if(l_verbose)
342 cout<<"Going to write unique part: '"<<makeHexDump(string(dns.c_str(), dns.c_str() + dns.size() - matchlen)) <<"'"<<endl;
e636cab2 343 d_content.insert(d_content.end(), (const unsigned char*)dns.c_str(), (const unsigned char*)dns.c_str() + dns.size() - matchlen);
802a93d0 344 uint16_t offset=li;
345 offset|=0xc000;
a2ce25e4 346
e636cab2 347 d_content.push_back((char)(offset >> 8));
348 d_content.push_back((char)(offset & 0xff));
bca6643b 349 }
802a93d0 350 else {
e636cab2 351 unsigned int pos=d_content.size();
802a93d0 352 if(l_verbose)
353 cout<<"Found nothing, we are at pos "<<pos<<", inserting whole name"<<endl;
354 if(pos < 16384) {
355 if(l_verbose)
356 cout<<"Inserting pos "<<pos<<" for "<<name<<" for uncompressed case"<<endl;
fea4599a 357 d_namepositions.push_back(pos);
802a93d0 358 }
5be843df 359
802a93d0 360 std::unique_ptr<DNSName> lc;
361 if(d_lowerCase)
362 lc = make_unique<DNSName>(name.makeLowerCase());
a0a276c2 363
802a93d0 364 const DNSName::string_t& raw = (lc ? *lc : name).getStorage();
365 if(l_verbose)
366 cout<<"Writing out the whole thing "<<makeHexDump(string(raw.c_str(), raw.c_str() + raw.length()))<<endl;
e636cab2 367 d_content.insert(d_content.end(), raw.c_str(), raw.c_str() + raw.size());
802a93d0 368 }
a0a276c2
BH
369}
370
06ffdc52 371void DNSPacketWriter::xfrBlob(const string& blob, int )
8c1c9170
BH
372{
373 const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str());
e636cab2 374 d_content.insert(d_content.end(), ptr, ptr+blob.size());
8c1c9170
BH
375}
376
2fe9d6f7
AT
377void DNSPacketWriter::xfrBlobNoSpaces(const string& blob, int )
378{
379 xfrBlob(blob);
380}
381
e4090157 382void DNSPacketWriter::xfrHexBlob(const string& blob, bool keepReading)
59a0f653
BH
383{
384 xfrBlob(blob);
385}
386
e636cab2 387// call __before commit__
388void DNSPacketWriter::getRecordPayload(string& records)
ea634573
BH
389{
390 records.assign(d_content.begin() + d_sor, d_content.end());
391}
a0a276c2 392
dffbaa08 393uint32_t DNSPacketWriter::size()
10321a98 394{
e636cab2 395 return d_content.size();
10321a98
BH
396}
397
398void DNSPacketWriter::rollback()
399{
400 d_content.resize(d_rollbackmarker);
4bb75371 401 d_sor = 0;
10321a98
BH
402}
403
add935a2
PD
404void DNSPacketWriter::truncate()
405{
406 d_content.resize(d_truncatemarker);
add935a2
PD
407 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
408 dh->ancount = dh->nscount = dh->arcount = 0;
409}
410
a0a276c2
BH
411void DNSPacketWriter::commit()
412{
e636cab2 413 if(!d_sor)
414 return;
415 uint16_t rlen = d_content.size() - d_sor;
416 d_content[d_sor-2]=rlen >> 8;
417 d_content[d_sor-1]=rlen & 0xff;
418 d_sor=0;
10321a98
BH
419 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
420 switch(d_recordplace) {
e693ff5a
AT
421 case DNSResourceRecord::QUESTION:
422 dh->qdcount = htons(ntohs(dh->qdcount) + 1);
423 break;
424 case DNSResourceRecord::ANSWER:
10321a98
BH
425 dh->ancount = htons(ntohs(dh->ancount) + 1);
426 break;
e693ff5a 427 case DNSResourceRecord::AUTHORITY:
10321a98
BH
428 dh->nscount = htons(ntohs(dh->nscount) + 1);
429 break;
e693ff5a 430 case DNSResourceRecord::ADDITIONAL:
10321a98
BH
431 dh->arcount = htons(ntohs(dh->arcount) + 1);
432 break;
433 }
434
a0a276c2 435}