]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnswriter.cc
rec: allow exception to proxy protocal usage for specific listen addresses
[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 ---->
bb85386d 36 v d_truncatemarker
e636cab2 37 dnsheader | qname | qtype | qclass | {recordname| dnsrecordheader | record }
bb85386d
FM
38 ^ d_rollbackmarker ^ d_sor
39
e636cab2 40
41*/
42
43
32fbb2ab 44template <typename Container> GenericDNSPacketWriter<Container>::GenericDNSPacketWriter(Container& 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;
9bbcf03a
RG
56 d_content.reserve(sizeof(dnsheader) + qname.wirelength() + sizeof(qtype) + sizeof(qclass));
57 d_content.resize(sizeof(dnsheader));
58 uint8_t* dptr=(&*d_content.begin());
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
32fbb2ab 71template <typename Container> dnsheader* GenericDNSPacketWriter<Container>::getHeader()
8e97e9a3 72{
b346905c 73 return reinterpret_cast<dnsheader*>(&*d_content.begin());
8e97e9a3
BH
74}
75
e636cab2 76
32fbb2ab 77template <typename Container> void GenericDNSPacketWriter<Container>::startRecord(const DNSName& name, uint16_t qtype, uint32_t ttl, uint16_t qclass, DNSResourceRecord::Place place, bool compress)
a0a276c2 78{
02e7763f 79 d_compress = compress;
e636cab2 80 commit();
10321a98 81 d_rollbackmarker=d_content.size();
bca6643b 82
2d6c4fcb 83 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
84 static unsigned char marker[2]={0xc0, 0x0c};
85 d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]);
a2ce25e4
BH
86 }
87 else {
e636cab2 88 xfrName(name, compress);
a2ce25e4 89 }
e636cab2 90 xfr16BitInt(qtype);
91 xfr16BitInt(qclass);
92 xfr32BitInt(ttl);
93 xfr16BitInt(0); // this will be the record size
94 d_recordplace = place;
95 d_sor=d_content.size(); // this will remind us where to stuff the record size
878435ce 96}
a0a276c2 97
32fbb2ab 98template <typename Container> void GenericDNSPacketWriter<Container>::addOpt(const uint16_t udpsize, const uint16_t extRCode, const uint16_t ednsFlags, const optvect_t& options, const uint8_t version)
878435ce
BH
99{
100 uint32_t ttl=0;
705f31ae 101
009f9f55 102 EDNS0Record stuff;
878435ce 103
d6c335ab
PL
104 stuff.version = version;
105 stuff.extFlags = htons(ednsFlags);
7f7b8d55 106
f1db0de2
PL
107 /* RFC 6891 section 4 on the Extended RCode wire format
108 * EXTENDED-RCODE
109 * Forms the upper 8 bits of extended 12-bit RCODE (together with the
110 * 4 bits defined in [RFC1035]. Note that EXTENDED-RCODE value 0
111 * indicates that an unextended RCODE is in use (values 0 through 15).
112 */
d6c335ab 113 // XXX Should be check for extRCode > 1<<12 ?
f1db0de2 114 stuff.extRCode = extRCode>>4;
d0983bff 115 if (extRCode != 0) { // As this trumps the existing RCODE
f1db0de2
PL
116 getHeader()->rcode = extRCode;
117 }
118
a683e8bd 119 static_assert(sizeof(EDNS0Record) == sizeof(ttl), "sizeof(EDNS0Record) must match sizeof(ttl)");
878435ce 120 memcpy(&ttl, &stuff, sizeof(stuff));
8a63d3ce 121
878435ce 122 ttl=ntohl(ttl); // will be reversed later on
950cfe0f 123
12c06211 124 startRecord(g_rootdnsname, QType::OPT, ttl, udpsize, DNSResourceRecord::ADDITIONAL, false);
d6c335ab
PL
125 for(auto const &option : options) {
126 xfr16BitInt(option.first);
127 xfr16BitInt(option.second.length());
128 xfrBlob(option.second);
950cfe0f 129 }
a0a276c2
BH
130}
131
32fbb2ab 132template <typename Container> void GenericDNSPacketWriter<Container>::xfr48BitInt(uint64_t val)
341930bb 133{
26512612 134 std::array<unsigned char, 6> bytes;
a6e93c0f 135 uint16_t theLeft = htons((val >> 32)&0xffffU);
0407751c 136 uint32_t theRight = htonl(val & 0xffffffffU);
26512612
RG
137 memcpy(&bytes[0], (void*)&theLeft, sizeof(theLeft));
138 memcpy(&bytes[2], (void*)&theRight, sizeof(theRight));
341930bb 139
26512612 140 d_content.insert(d_content.end(), bytes.begin(), bytes.end());
341930bb
BH
141}
142
d97a781e 143template <typename Container> void GenericDNSPacketWriter<Container>::xfrNodeOrLocatorID(const NodeOrLocatorID& val)
786ed0ff 144{
2c510d90 145 d_content.insert(d_content.end(), val.content, val.content + sizeof(val.content));
786ed0ff 146}
341930bb 147
32fbb2ab 148template <typename Container> void GenericDNSPacketWriter<Container>::xfr32BitInt(uint32_t val)
a0a276c2 149{
47698274 150 uint32_t rval=htonl(val);
ea634573 151 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
e636cab2 152 d_content.insert(d_content.end(), ptr, ptr+4);
a0a276c2
BH
153}
154
32fbb2ab 155template <typename Container> void GenericDNSPacketWriter<Container>::xfr16BitInt(uint16_t val)
a0a276c2 156{
96aed220 157 uint16_t rval=htons(val);
ea634573 158 uint8_t* ptr=reinterpret_cast<uint8_t*>(&rval);
e636cab2 159 d_content.insert(d_content.end(), ptr, ptr+2);
a0a276c2
BH
160}
161
32fbb2ab 162template <typename Container> void GenericDNSPacketWriter<Container>::xfr8BitInt(uint8_t val)
a0a276c2 163{
e636cab2 164 d_content.push_back(val);
a0a276c2
BH
165}
166
bac8f21b 167
950cfe0f 168/* input:
84e1142d 169 if lenField is true
bac8f21b
BH
170 "" -> 0
171 "blah" -> 4blah
172 "blah" "blah" -> output 4blah4blah
173 "verylongstringlongerthan256....characters" \xffverylongstring\x23characters (autosplit)
174 "blah\"blah" -> 9blah"blah
175 "blah\97" -> 5blahb
84e1142d
PL
176
177 if lenField is false
178 "blah" -> blah
179 "blah\"blah" -> blah"blah
bac8f21b 180 */
32fbb2ab 181template <typename Container> void GenericDNSPacketWriter<Container>::xfrText(const string& text, bool, bool lenField)
a0a276c2 182{
bac8f21b 183 if(text.empty()) {
e636cab2 184 d_content.push_back(0);
bac8f21b
BH
185 return;
186 }
187 vector<string> segments = segmentDNSText(text);
ef7cd021 188 for(const string& str : segments) {
84e1142d 189 if(lenField)
e636cab2 190 d_content.push_back(str.length());
191 d_content.insert(d_content.end(), str.c_str(), str.c_str() + str.length());
ef6a78d5 192 }
a0a276c2
BH
193}
194
32fbb2ab 195template <typename Container> void GenericDNSPacketWriter<Container>::xfrUnquotedText(const string& text, bool lenField)
948a927f
PL
196{
197 if(text.empty()) {
e636cab2 198 d_content.push_back(0);
948a927f
PL
199 return;
200 }
201 if(lenField)
e636cab2 202 d_content.push_back(text.length());
203 d_content.insert(d_content.end(), text.c_str(), text.c_str() + text.length());
948a927f
PL
204}
205
802a93d0 206
207static constexpr bool l_verbose=false;
43f038d7 208static constexpr uint16_t maxCompressionOffset=16384;
32fbb2ab 209template <typename Container> uint16_t GenericDNSPacketWriter<Container>::lookupName(const DNSName& name, uint16_t* matchLen)
a2ce25e4 210{
802a93d0 211 // iterate over the written labels, see if we find a match
212 const auto& raw = name.getStorage();
213
214 /* name might be a.root-servers.net, we need to be able to benefit from finding:
215 b.root-servers.net, or even:
bb85386d 216 b\xc0\x0c
802a93d0 217 */
218 unsigned int bestpos=0;
219 *matchLen=0;
b8cd1d8f 220#if BOOST_VERSION >= 105400
802a93d0 221 boost::container::static_vector<uint16_t, 34> nvect, pvect;
b8cd1d8f
PL
222#else
223 vector<uint16_t> nvect, pvect;
224#endif
802a93d0 225
98cda0a9 226 try {
227 for(auto riter= raw.cbegin(); riter < raw.cend(); ) {
228 if(!*riter)
229 break;
230 nvect.push_back(riter - raw.cbegin());
231 riter+=*riter+1;
232 }
233 }
234 catch(std::bad_alloc& ba) {
235 if(l_verbose)
236 cout<<"Domain "<<name<<" too large to compress"<<endl;
237 return 0;
802a93d0 238 }
bb85386d 239
802a93d0 240 if(l_verbose) {
241 cout<<"Input vector for lookup "<<name<<": ";
bb85386d 242 for(const auto n : nvect)
802a93d0 243 cout << n<<" ";
244 cout<<endl;
245 cout<<makeHexDump(string(raw.c_str(), raw.c_str()+raw.size()))<<endl;
246 }
a2ce25e4 247
802a93d0 248 if(l_verbose)
fea4599a 249 cout<<"Have "<<d_namepositions.size()<<" to ponder"<<endl;
802a93d0 250 int counter=1;
fea4599a 251 for(auto p : d_namepositions) {
802a93d0 252 if(l_verbose) {
e636cab2 253 cout<<"Pos: "<<p<<", "<<d_content.size()<<endl;
254 DNSName pname((const char*)&d_content[0], d_content.size(), p, true); // only for debugging
255 cout<<"Looking at '"<<pname<<"' in packet at position "<<p<<"/"<<d_content.size()<<", option "<<counter<<"/"<<d_namepositions.size()<<endl;
802a93d0 256 ++counter;
257 }
258 // memcmp here makes things _slower_
259 pvect.clear();
98cda0a9 260 try {
261 for(auto iter = d_content.cbegin() + p; iter < d_content.cend();) {
262 uint8_t c=*iter;
802a93d0 263 if(l_verbose)
98cda0a9 264 cout<<"Found label length: "<<(int)c<<endl;
265 if(c & 0xc0) {
266 uint16_t npos = 0x100*(c & (~0xc0)) + *++iter;
267 iter = d_content.begin() + npos;
268 if(l_verbose)
269 cout<<"Is compressed label to newpos "<<npos<<", going there"<<endl;
270 // check against going forward here
271 continue;
272 }
273 if(!c)
274 break;
d26fa315 275 auto offset = iter - d_content.cbegin();
43f038d7 276 if (offset >= maxCompressionOffset) break; // compression pointers cannot point here
d26fa315 277 pvect.push_back(offset);
98cda0a9 278 iter+=*iter+1;
802a93d0 279 }
98cda0a9 280 }
281 catch(std::bad_alloc& ba) {
282 if(l_verbose)
283 cout<<"Domain "<<name<<" too large to compress"<<endl;
284 continue;
802a93d0 285 }
286 if(l_verbose) {
287 cout<<"Packet vector: "<<endl;
bb85386d 288 for(const auto n : pvect)
802a93d0 289 cout << n<<" ";
290 cout<<endl;
291 }
292 auto niter=nvect.crbegin(), piter=pvect.crbegin();
293 unsigned int cmatchlen=1;
294 for(; niter != nvect.crend() && piter != pvect.crend(); ++niter, ++piter) {
295 // niter is an offset in raw, pvect an offset in packet
e636cab2 296 uint8_t nlen = raw[*niter], plen=d_content[*piter];
802a93d0 297 if(l_verbose)
298 cout<<"nlnen="<<(int)nlen<<", plen="<<(int)plen<<endl;
299 if(nlen != plen)
300 break;
e636cab2 301 if(strncasecmp(raw.c_str()+*niter+1, (const char*)&d_content[*piter]+1, nlen)) {
802a93d0 302 if(l_verbose)
e636cab2 303 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 304 break;
305 }
306 cmatchlen+=nlen+1;
307 if(cmatchlen == raw.length()) { // have matched all of it, can't improve
308 if(l_verbose)
309 cout<<"Stopping search, matched whole name"<<endl;
310 *matchLen = cmatchlen;
311 return *piter;
312 }
313 }
314 if(piter != pvect.crbegin() && *matchLen < cmatchlen) {
315 *matchLen = cmatchlen;
316 bestpos=*--piter;
317 }
318 }
319 return bestpos;
320}
950cfe0f 321// this is the absolute hottest function in the pdns recursor
32fbb2ab 322template <typename Container> void GenericDNSPacketWriter<Container>::xfrName(const DNSName& name, bool compress, bool)
38e655b6 323{
802a93d0 324 if(l_verbose)
325 cout<<"Wants to write "<<name<<", compress="<<compress<<", canonic="<<d_canonic<<", LC="<<d_lowerCase<<endl;
326 if(d_canonic || d_lowerCase) // d_lowerCase implies canonic
f3f4938f
BH
327 compress=false;
328
802a93d0 329 if(name.empty() || name.isRoot()) { // for speed
e636cab2 330 d_content.push_back(0);
c1d02c0d
BH
331 return;
332 }
950cfe0f 333
802a93d0 334 uint16_t li=0;
335 uint16_t matchlen=0;
43f038d7 336 if(d_compress && compress && (li=lookupName(name, &matchlen)) && li < maxCompressionOffset) {
bb85386d 337 const auto& dns=name.getStorage();
802a93d0 338 if(l_verbose)
339 cout<<"Found a substring of "<<matchlen<<" bytes from the back, offset: "<<li<<", dnslen: "<<dns.size()<<endl;
340 // found a substring, if www.powerdns.com matched powerdns.com, we get back matchlen = 13
341
e636cab2 342 unsigned int pos=d_content.size();
43f038d7 343 if(pos < maxCompressionOffset && matchlen != dns.size()) {
802a93d0 344 if(l_verbose)
345 cout<<"Inserting pos "<<pos<<" for "<<name<<" for compressed case"<<endl;
fea4599a 346 d_namepositions.push_back(pos);
bca6643b 347 }
2f4c3abb 348
802a93d0 349 if(l_verbose)
350 cout<<"Going to write unique part: '"<<makeHexDump(string(dns.c_str(), dns.c_str() + dns.size() - matchlen)) <<"'"<<endl;
e636cab2 351 d_content.insert(d_content.end(), (const unsigned char*)dns.c_str(), (const unsigned char*)dns.c_str() + dns.size() - matchlen);
802a93d0 352 uint16_t offset=li;
353 offset|=0xc000;
a2ce25e4 354
e636cab2 355 d_content.push_back((char)(offset >> 8));
356 d_content.push_back((char)(offset & 0xff));
bca6643b 357 }
802a93d0 358 else {
e636cab2 359 unsigned int pos=d_content.size();
802a93d0 360 if(l_verbose)
361 cout<<"Found nothing, we are at pos "<<pos<<", inserting whole name"<<endl;
43f038d7 362 if(pos < maxCompressionOffset) {
802a93d0 363 if(l_verbose)
364 cout<<"Inserting pos "<<pos<<" for "<<name<<" for uncompressed case"<<endl;
fea4599a 365 d_namepositions.push_back(pos);
802a93d0 366 }
5be843df 367
802a93d0 368 std::unique_ptr<DNSName> lc;
369 if(d_lowerCase)
370 lc = make_unique<DNSName>(name.makeLowerCase());
a0a276c2 371
802a93d0 372 const DNSName::string_t& raw = (lc ? *lc : name).getStorage();
373 if(l_verbose)
374 cout<<"Writing out the whole thing "<<makeHexDump(string(raw.c_str(), raw.c_str() + raw.length()))<<endl;
e636cab2 375 d_content.insert(d_content.end(), raw.c_str(), raw.c_str() + raw.size());
802a93d0 376 }
a0a276c2
BH
377}
378
32fbb2ab 379template <typename Container> void GenericDNSPacketWriter<Container>::xfrBlob(const string& blob, int )
8c1c9170
BH
380{
381 const uint8_t* ptr=reinterpret_cast<const uint8_t*>(blob.c_str());
e636cab2 382 d_content.insert(d_content.end(), ptr, ptr+blob.size());
8c1c9170
BH
383}
384
651f3a09
RG
385template <typename Container> void GenericDNSPacketWriter<Container>::xfrBlob(const std::vector<uint8_t>& blob)
386{
387 d_content.insert(d_content.end(), blob.begin(), blob.end());
388}
389
32fbb2ab 390template <typename Container> void GenericDNSPacketWriter<Container>::xfrBlobNoSpaces(const string& blob, int )
2fe9d6f7
AT
391{
392 xfrBlob(blob);
393}
394
d73de874 395template <typename Container> void GenericDNSPacketWriter<Container>::xfrHexBlob(const string& blob, bool /* keepReading */)
59a0f653
BH
396{
397 xfrBlob(blob);
398}
399
32fbb2ab 400template <typename Container> void GenericDNSPacketWriter<Container>::xfrSvcParamKeyVals(const std::set<SvcParam> &kvs)
373914dc
PL
401{
402 for (auto const &param : kvs) {
403 // Key first!
404 xfr16BitInt(param.getKey());
405
406 switch (param.getKey())
407 {
408 case SvcParam::mandatory:
409 xfr16BitInt(2 * param.getMandatory().size());
410 for (auto const &m: param.getMandatory()) {
411 xfr16BitInt(m);
412 }
413 break;
414 case SvcParam::alpn:
415 {
416 uint16_t totalSize = param.getALPN().size(); // All 1 octet size headers for each value
417 for (auto const &a : param.getALPN()) {
418 totalSize += a.length();
419 }
420 xfr16BitInt(totalSize);
421 for (auto const &a : param.getALPN()) {
422 xfrUnquotedText(a, true); // will add the 1-byte length field
423 }
424 break;
425 }
426 case SvcParam::no_default_alpn:
427 xfr16BitInt(0); // no size :)
428 break;
429 case SvcParam::port:
430 xfr16BitInt(2); // size
431 xfr16BitInt(param.getPort());
432 break;
433 case SvcParam::ipv4hint:
434 xfr16BitInt(param.getIPHints().size() * 4); // size
dacf2931 435 for (const auto& a: param.getIPHints()) {
373914dc
PL
436 xfrCAWithoutPort(param.getKey(), a);
437 }
438 break;
439 case SvcParam::ipv6hint:
440 xfr16BitInt(param.getIPHints().size() * 16); // size
b59b334d 441 for (const auto& a: param.getIPHints()) {
373914dc
PL
442 xfrCAWithoutPort(param.getKey(), a);
443 }
444 break;
4f254e34
PL
445 case SvcParam::ech:
446 xfr16BitInt(param.getECH().size()); // size
447 xfrBlobNoSpaces(param.getECH());
373914dc
PL
448 break;
449 default:
450 xfr16BitInt(param.getValue().size());
b1a048a9 451 xfrBlob(param.getValue());
373914dc
PL
452 break;
453 }
454 }
455}
456
e636cab2 457// call __before commit__
32fbb2ab 458template <typename Container> void GenericDNSPacketWriter<Container>::getRecordPayload(string& records)
ea634573
BH
459{
460 records.assign(d_content.begin() + d_sor, d_content.end());
461}
a0a276c2 462
53604c2a 463template <typename Container> uint32_t GenericDNSPacketWriter<Container>::size() const
10321a98 464{
e636cab2 465 return d_content.size();
10321a98
BH
466}
467
32fbb2ab 468template <typename Container> void GenericDNSPacketWriter<Container>::rollback()
10321a98
BH
469{
470 d_content.resize(d_rollbackmarker);
4bb75371 471 d_sor = 0;
10321a98
BH
472}
473
32fbb2ab 474template <typename Container> void GenericDNSPacketWriter<Container>::truncate()
add935a2
PD
475{
476 d_content.resize(d_truncatemarker);
add935a2
PD
477 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
478 dh->ancount = dh->nscount = dh->arcount = 0;
479}
480
32fbb2ab 481template <typename Container> void GenericDNSPacketWriter<Container>::commit()
a0a276c2 482{
e636cab2 483 if(!d_sor)
484 return;
485 uint16_t rlen = d_content.size() - d_sor;
486 d_content[d_sor-2]=rlen >> 8;
487 d_content[d_sor-1]=rlen & 0xff;
488 d_sor=0;
10321a98
BH
489 dnsheader* dh=reinterpret_cast<dnsheader*>( &*d_content.begin());
490 switch(d_recordplace) {
e693ff5a
AT
491 case DNSResourceRecord::QUESTION:
492 dh->qdcount = htons(ntohs(dh->qdcount) + 1);
493 break;
494 case DNSResourceRecord::ANSWER:
10321a98
BH
495 dh->ancount = htons(ntohs(dh->ancount) + 1);
496 break;
e693ff5a 497 case DNSResourceRecord::AUTHORITY:
10321a98
BH
498 dh->nscount = htons(ntohs(dh->nscount) + 1);
499 break;
e693ff5a 500 case DNSResourceRecord::ADDITIONAL:
10321a98
BH
501 dh->arcount = htons(ntohs(dh->arcount) + 1);
502 break;
503 }
504
a0a276c2 505}
32fbb2ab 506
53604c2a
RG
507template <typename Container> size_t GenericDNSPacketWriter<Container>::getSizeWithOpts(const optvect_t& options) const
508{
509 size_t result = size() + /* root */ 1 + DNS_TYPE_SIZE + DNS_CLASS_SIZE + DNS_TTL_SIZE + DNS_RDLENGTH_SIZE;
510
511 for(auto const &option : options) {
512 result += 4;
513 result += option.second.size();
514 }
515
516 return result;
517}
518
32fbb2ab
RG
519template class GenericDNSPacketWriter<std::vector<uint8_t>>;
520#include "noinitvector.hh"
521template class GenericDNSPacketWriter<PacketBuffer>;