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