]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnswriter.cc
Merge pull request #7903 from Habbie/dnsdist-doc-nits
[thirdparty/pdns.git] / pdns / dnswriter.cc
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 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <boost/version.hpp>
26 #if BOOST_VERSION >= 105400
27 #include <boost/container/static_vector.hpp>
28 #endif
29 #include "dnswriter.hh"
30 #include "misc.hh"
31 #include "dnsparser.hh"
32
33 #include <limits.h>
34
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
44 DNSPacketWriter::DNSPacketWriter(vector<uint8_t>& content, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode)
45 : d_content(content), d_qname(qname), d_canonic(false), d_lowerCase(false)
46 {
47 d_content.clear();
48 dnsheader dnsheader;
49
50 memset(&dnsheader, 0, sizeof(dnsheader));
51 dnsheader.id=0;
52 dnsheader.qdcount=htons(1);
53 dnsheader.opcode=opcode;
54
55 const uint8_t* ptr=(const uint8_t*)&dnsheader;
56 uint32_t len=d_content.size();
57 d_content.resize(len + sizeof(dnsheader));
58 uint8_t* dptr=(&*d_content.begin()) + len;
59
60 memcpy(dptr, ptr, sizeof(dnsheader));
61 d_namepositions.reserve(16);
62 xfrName(qname, false);
63 xfr16BitInt(qtype);
64 xfr16BitInt(qclass);
65
66 d_truncatemarker=d_content.size();
67 d_sor = 0;
68 d_rollbackmarker = 0;
69 }
70
71 dnsheader* DNSPacketWriter::getHeader()
72 {
73 return reinterpret_cast<dnsheader*>(&*d_content.begin());
74 }
75
76
77 void DNSPacketWriter::startRecord(const DNSName& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, DNSResourceRecord::Place place, bool compress)
78 {
79 commit();
80 d_rollbackmarker=d_content.size();
81
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
83 static unsigned char marker[2]={0xc0, 0x0c};
84 d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]);
85 }
86 else {
87 xfrName(name, compress);
88 }
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
95 }
96
97 void DNSPacketWriter::addOpt(const uint16_t udpsize, const uint16_t extRCode, const uint16_t ednsFlags, const optvect_t& options, const uint8_t version)
98 {
99 uint32_t ttl=0;
100
101 EDNS0Record stuff;
102
103 stuff.version = version;
104 stuff.extFlags = htons(ednsFlags);
105
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 */
112 // XXX Should be check for extRCode > 1<<12 ?
113 stuff.extRCode = extRCode>>4;
114 if (extRCode != 0) { // As this trumps the existing RCODE
115 getHeader()->rcode = extRCode;
116 }
117
118 static_assert(sizeof(EDNS0Record) == sizeof(ttl), "sizeof(EDNS0Record) must match sizeof(ttl)");
119 memcpy(&ttl, &stuff, sizeof(stuff));
120
121 ttl=ntohl(ttl); // will be reversed later on
122
123 startRecord(g_rootdnsname, QType::OPT, ttl, udpsize, DNSResourceRecord::ADDITIONAL, false);
124 for(auto const &option : options) {
125 xfr16BitInt(option.first);
126 xfr16BitInt(option.second.length());
127 xfrBlob(option.second);
128 }
129 }
130
131 void DNSPacketWriter::xfr48BitInt(uint64_t val)
132 {
133 unsigned char bytes[6];
134 uint16_t theLeft = htons((val >> 32)&0xffffU);
135 uint32_t theRight = htonl(val & 0xffffffffU);
136 memcpy(bytes, (void*)&theLeft, sizeof(theLeft));
137 memcpy(bytes+2, (void*)&theRight, sizeof(theRight));
138
139 d_content.insert(d_content.end(), bytes, bytes + sizeof(bytes));
140 }
141
142
143 void DNSPacketWriter::xfr32BitInt(uint32_t val)
144 {
145 uint32_t rval=htonl(val);
146 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
147 d_content.insert(d_content.end(), ptr, ptr+4);
148 }
149
150 void DNSPacketWriter::xfr16BitInt(uint16_t val)
151 {
152 uint16_t rval=htons(val);
153 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
154 d_content.insert(d_content.end(), ptr, ptr+2);
155 }
156
157 void DNSPacketWriter::xfr8BitInt(uint8_t val)
158 {
159 d_content.push_back(val);
160 }
161
162
163 /* input:
164 if lenField is true
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
171
172 if lenField is false
173 "blah" -> blah
174 "blah\"blah" -> blah"blah
175 */
176 void DNSPacketWriter::xfrText(const string& text, bool, bool lenField)
177 {
178 if(text.empty()) {
179 d_content.push_back(0);
180 return;
181 }
182 vector<string> segments = segmentDNSText(text);
183 for(const string& str : segments) {
184 if(lenField)
185 d_content.push_back(str.length());
186 d_content.insert(d_content.end(), str.c_str(), str.c_str() + str.length());
187 }
188 }
189
190 void DNSPacketWriter::xfrUnquotedText(const string& text, bool lenField)
191 {
192 if(text.empty()) {
193 d_content.push_back(0);
194 return;
195 }
196 if(lenField)
197 d_content.push_back(text.length());
198 d_content.insert(d_content.end(), text.c_str(), text.c_str() + text.length());
199 }
200
201
202 static constexpr bool l_verbose=false;
203 uint16_t DNSPacketWriter::lookupName(const DNSName& name, uint16_t* matchLen)
204 {
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;
214 #if BOOST_VERSION >= 105400
215 boost::container::static_vector<uint16_t, 34> nvect, pvect;
216 #else
217 vector<uint16_t> nvect, pvect;
218 #endif
219
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;
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 }
241
242 if(l_verbose)
243 cout<<"Have "<<d_namepositions.size()<<" to ponder"<<endl;
244 int counter=1;
245 for(auto p : d_namepositions) {
246 if(l_verbose) {
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;
250 ++counter;
251 }
252 // memcmp here makes things _slower_
253 pvect.clear();
254 try {
255 for(auto iter = d_content.cbegin() + p; iter < d_content.cend();) {
256 uint8_t c=*iter;
257 if(l_verbose)
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;
271 }
272 }
273 catch(std::bad_alloc& ba) {
274 if(l_verbose)
275 cout<<"Domain "<<name<<" too large to compress"<<endl;
276 continue;
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
288 uint8_t nlen = raw[*niter], plen=d_content[*piter];
289 if(l_verbose)
290 cout<<"nlnen="<<(int)nlen<<", plen="<<(int)plen<<endl;
291 if(nlen != plen)
292 break;
293 if(strncasecmp(raw.c_str()+*niter+1, (const char*)&d_content[*piter]+1, nlen)) {
294 if(l_verbose)
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;
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 }
313 // this is the absolute hottest function in the pdns recursor
314 void DNSPacketWriter::xfrName(const DNSName& name, bool compress, bool)
315 {
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
319 compress=false;
320
321 if(name.empty() || name.isRoot()) { // for speed
322 d_content.push_back(0);
323 return;
324 }
325
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
334 unsigned int pos=d_content.size();
335 if(pos < 16384 && matchlen != dns.size()) {
336 if(l_verbose)
337 cout<<"Inserting pos "<<pos<<" for "<<name<<" for compressed case"<<endl;
338 d_namepositions.push_back(pos);
339 }
340
341 if(l_verbose)
342 cout<<"Going to write unique part: '"<<makeHexDump(string(dns.c_str(), dns.c_str() + dns.size() - matchlen)) <<"'"<<endl;
343 d_content.insert(d_content.end(), (const unsigned char*)dns.c_str(), (const unsigned char*)dns.c_str() + dns.size() - matchlen);
344 uint16_t offset=li;
345 offset|=0xc000;
346
347 d_content.push_back((char)(offset >> 8));
348 d_content.push_back((char)(offset & 0xff));
349 }
350 else {
351 unsigned int pos=d_content.size();
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;
357 d_namepositions.push_back(pos);
358 }
359
360 std::unique_ptr<DNSName> lc;
361 if(d_lowerCase)
362 lc = make_unique<DNSName>(name.makeLowerCase());
363
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;
367 d_content.insert(d_content.end(), raw.c_str(), raw.c_str() + raw.size());
368 }
369 }
370
371 void DNSPacketWriter::xfrBlob(const string& blob, int )
372 {
373 const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str());
374 d_content.insert(d_content.end(), ptr, ptr+blob.size());
375 }
376
377 void DNSPacketWriter::xfrBlobNoSpaces(const string& blob, int )
378 {
379 xfrBlob(blob);
380 }
381
382 void DNSPacketWriter::xfrHexBlob(const string& blob, bool keepReading)
383 {
384 xfrBlob(blob);
385 }
386
387 // call __before commit__
388 void DNSPacketWriter::getRecordPayload(string& records)
389 {
390 records.assign(d_content.begin() + d_sor, d_content.end());
391 }
392
393 uint32_t DNSPacketWriter::size()
394 {
395 return d_content.size();
396 }
397
398 void DNSPacketWriter::rollback()
399 {
400 d_content.resize(d_rollbackmarker);
401 d_sor = 0;
402 }
403
404 void DNSPacketWriter::truncate()
405 {
406 d_content.resize(d_truncatemarker);
407 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
408 dh->ancount = dh->nscount = dh->arcount = 0;
409 }
410
411 void DNSPacketWriter::commit()
412 {
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;
419 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
420 switch(d_recordplace) {
421 case DNSResourceRecord::QUESTION:
422 dh->qdcount = htons(ntohs(dh->qdcount) + 1);
423 break;
424 case DNSResourceRecord::ANSWER:
425 dh->ancount = htons(ntohs(dh->ancount) + 1);
426 break;
427 case DNSResourceRecord::AUTHORITY:
428 dh->nscount = htons(ntohs(dh->nscount) + 1);
429 break;
430 case DNSResourceRecord::ADDITIONAL:
431 dh->arcount = htons(ntohs(dh->arcount) + 1);
432 break;
433 }
434
435 }