]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsrecords.cc
Standardize license text in all PDNS files
[thirdparty/pdns.git] / pdns / dnsrecords.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 "utility.hh"
26 #include "dnsrecords.hh"
27 #include "iputils.hh"
28
29
30 void DNSResourceRecord::setContent(const string &cont) {
31 content = cont;
32 switch(qtype.getCode()) {
33 case QType::SRV:
34 case QType::MX:
35 if (content.size() >= 2 && *(content.rbegin()+1) == ' ')
36 return;
37 case QType::CNAME:
38 case QType::DNAME:
39 case QType::NS:
40 case QType::PTR:
41 if(!content.empty())
42 boost::erase_tail(content, 1);
43 }
44 }
45
46 string DNSResourceRecord::getZoneRepresentation(bool noDot) const {
47 ostringstream ret;
48 switch(qtype.getCode()) {
49 case QType::SRV:
50 case QType::MX:
51 case QType::CNAME:
52 case QType::DNAME:
53 case QType::NS:
54 case QType::PTR:
55 if (*(content.rbegin()) != '.') {
56 ret<<content;
57 if(!noDot)
58 ret<<".";
59 }
60 break;
61 default:
62 ret<<content;
63 break;
64 }
65 return ret.str();
66 }
67
68 bool DNSResourceRecord::operator==(const DNSResourceRecord& rhs)
69 {
70 string lcontent=toLower(content);
71 string rcontent=toLower(rhs.content);
72
73 return
74 tie(qname, qtype, lcontent, ttl) ==
75 tie(rhs.qname, rhs.qtype, rcontent, rhs.ttl);
76 }
77
78 DNSResourceRecord::DNSResourceRecord(const DNSRecord &p) {
79 auth=true;
80 qclass = p.d_class;
81 disabled=false;
82 qname = p.d_name;
83 d_place = p.d_place;
84 // if(!qname.empty())
85 // boost::erase_tail(qname, 1); // strip .
86
87 qtype = p.d_type;
88 ttl = p.d_ttl;
89 setContent(p.d_content->getZoneRepresentation());
90 last_modified = 0;
91 signttl = 0;
92 domain_id = -1;
93 qclass = p.d_class;
94 d_place = p.d_place;
95 scopeMask = 0;
96 }
97
98 boilerplate_conv(A, QType::A, conv.xfrIP(d_ip));
99
100 ARecordContent::ARecordContent(uint32_t ip)
101 {
102 d_ip = ip;
103 }
104
105 ARecordContent::ARecordContent(const ComboAddress& ca)
106 {
107 d_ip = ca.sin4.sin_addr.s_addr;
108 }
109
110 AAAARecordContent::AAAARecordContent(const ComboAddress& ca)
111 {
112 d_ip6.assign((const char*)ca.sin6.sin6_addr.s6_addr, 16);
113 }
114
115
116 ComboAddress ARecordContent::getCA(int port) const
117 {
118 ComboAddress ret;
119 ret.sin4.sin_family=AF_INET;
120 ret.sin4.sin_port=htons(port);
121 memcpy(&ret.sin4.sin_addr.s_addr, &d_ip, sizeof(ret.sin4.sin_addr.s_addr));
122 return ret;
123 }
124
125 ComboAddress AAAARecordContent::getCA(int port) const
126 {
127 ComboAddress ret;
128 memset(&ret, 0, sizeof(ret));
129
130 ret.sin4.sin_family=AF_INET6;
131 ret.sin6.sin6_port = htons(port);
132 memcpy(&ret.sin6.sin6_addr.s6_addr, d_ip6.c_str(), sizeof(ret.sin6.sin6_addr.s6_addr));
133 return ret;
134 }
135
136
137 void ARecordContent::doRecordCheck(const DNSRecord& dr)
138 {
139 if(dr.d_clen!=4)
140 throw MOADNSException("Wrong size for A record ("+std::to_string(dr.d_clen)+")");
141 }
142
143 boilerplate_conv(AAAA, QType::AAAA, conv.xfrIP6(d_ip6); );
144
145 boilerplate_conv(NS, QType::NS, conv.xfrName(d_content, true));
146 boilerplate_conv(PTR, QType::PTR, conv.xfrName(d_content, true));
147 boilerplate_conv(CNAME, QType::CNAME, conv.xfrName(d_content, true));
148 boilerplate_conv(ALIAS, QType::ALIAS, conv.xfrName(d_content, true));
149 boilerplate_conv(DNAME, QType::DNAME, conv.xfrName(d_content));
150 boilerplate_conv(MR, QType::MR, conv.xfrName(d_alias, true));
151 boilerplate_conv(MINFO, QType::MINFO, conv.xfrName(d_rmailbx, true); conv.xfrName(d_emailbx, true));
152 boilerplate_conv(TXT, QType::TXT, conv.xfrText(d_text, true));
153 boilerplate_conv(SPF, 99, conv.xfrText(d_text, true));
154 boilerplate_conv(HINFO, QType::HINFO, conv.xfrText(d_cpu); conv.xfrText(d_host));
155
156 boilerplate_conv(RP, QType::RP,
157 conv.xfrName(d_mbox);
158 conv.xfrName(d_info)
159 );
160
161
162 boilerplate_conv(OPT, QType::OPT,
163 conv.xfrBlob(d_data)
164 );
165
166 void OPTRecordContent::getData(vector<pair<uint16_t, string> >& options)
167 {
168 string::size_type pos=0;
169 uint16_t code, len;
170 while(d_data.size() >= 4 + pos) {
171 code = 256 * (unsigned char)d_data[pos] + (unsigned char)d_data[pos+1];
172 len = 256 * (unsigned char)d_data[pos+2] + (unsigned char)d_data[pos+3];
173 pos+=4;
174
175 if(pos + len > d_data.size())
176 break;
177
178 string field(d_data.c_str() + pos, len);
179 pos+=len;
180 options.push_back(make_pair(code, field));
181 }
182 }
183
184 boilerplate_conv(TSIG, QType::TSIG,
185 conv.xfrName(d_algoName);
186 conv.xfr48BitInt(d_time);
187 conv.xfr16BitInt(d_fudge);
188 uint16_t size=d_mac.size();
189 conv.xfr16BitInt(size);
190 if (size>0) conv.xfrBlobNoSpaces(d_mac, size);
191 conv.xfr16BitInt(d_origID);
192 conv.xfr16BitInt(d_eRcode);
193 size=d_otherData.size();
194 conv.xfr16BitInt(size);
195 if (size>0) conv.xfrBlobNoSpaces(d_otherData, size);
196 );
197
198 MXRecordContent::MXRecordContent(uint16_t preference, const DNSName& mxname): d_preference(preference), d_mxname(mxname)
199 {
200 }
201
202 boilerplate_conv(MX, QType::MX,
203 conv.xfr16BitInt(d_preference);
204 conv.xfrName(d_mxname, true);
205 )
206
207 boilerplate_conv(KX, QType::KX,
208 conv.xfr16BitInt(d_preference);
209 conv.xfrName(d_exchanger, false);
210 )
211
212 boilerplate_conv(IPSECKEY, QType::IPSECKEY,
213 conv.xfr8BitInt(d_preference);
214 conv.xfr8BitInt(d_gatewaytype);
215 conv.xfr8BitInt(d_algorithm);
216
217 // now we need to determine values
218 switch(d_gatewaytype) {
219 case 0: // NO KEY
220 break;
221 case 1: // IPv4 GW
222 conv.xfrIP(d_ip4);
223 break;
224 case 2: // IPv6 GW
225 conv.xfrIP6(d_ip6);
226 break;
227 case 3: // DNS label
228 conv.xfrName(d_gateway, false);
229 break;
230 default:
231 throw MOADNSException("Parsing record content: invalid gateway type");
232 };
233
234 switch(d_algorithm) {
235 case 0:
236 break;
237 case 1:
238 case 2:
239 conv.xfrBlob(d_publickey);
240 break;
241 default:
242 throw MOADNSException("Parsing record content: invalid algorithm type");
243 }
244 )
245
246 boilerplate_conv(DHCID, 49,
247 conv.xfrBlob(d_content);
248 )
249
250
251 boilerplate_conv(AFSDB, QType::AFSDB,
252 conv.xfr16BitInt(d_subtype);
253 conv.xfrName(d_hostname);
254 )
255
256
257 boilerplate_conv(NAPTR, QType::NAPTR,
258 conv.xfr16BitInt(d_order); conv.xfr16BitInt(d_preference);
259 conv.xfrText(d_flags); conv.xfrText(d_services); conv.xfrText(d_regexp);
260 conv.xfrName(d_replacement);
261 )
262
263
264 SRVRecordContent::SRVRecordContent(uint16_t preference, uint16_t weight, uint16_t port, const DNSName& target)
265 : d_weight(weight), d_port(port), d_target(target), d_preference(preference)
266 {}
267
268 boilerplate_conv(SRV, QType::SRV,
269 conv.xfr16BitInt(d_preference); conv.xfr16BitInt(d_weight); conv.xfr16BitInt(d_port);
270 conv.xfrName(d_target);
271 )
272
273 SOARecordContent::SOARecordContent(const DNSName& mname, const DNSName& rname, const struct soatimes& st)
274 : d_mname(mname), d_rname(rname)
275 {
276 d_st=st;
277 }
278
279 boilerplate_conv(SOA, QType::SOA,
280 conv.xfrName(d_mname, true);
281 conv.xfrName(d_rname, true);
282 conv.xfr32BitInt(d_st.serial);
283 conv.xfr32BitInt(d_st.refresh);
284 conv.xfr32BitInt(d_st.retry);
285 conv.xfr32BitInt(d_st.expire);
286 conv.xfr32BitInt(d_st.minimum);
287 );
288 #undef KEY
289 boilerplate_conv(KEY, QType::KEY,
290 conv.xfr16BitInt(d_flags);
291 conv.xfr8BitInt(d_protocol);
292 conv.xfr8BitInt(d_algorithm);
293 conv.xfrBlob(d_certificate);
294 );
295
296 boilerplate_conv(CERT, 37,
297 conv.xfr16BitInt(d_type);
298 if (d_type == 0) throw MOADNSException("CERT type 0 is reserved");
299
300 conv.xfr16BitInt(d_tag);
301 conv.xfr8BitInt(d_algorithm);
302 conv.xfrBlob(d_certificate);
303 )
304
305 boilerplate_conv(TLSA, 52,
306 conv.xfr8BitInt(d_certusage);
307 conv.xfr8BitInt(d_selector);
308 conv.xfr8BitInt(d_matchtype);
309 conv.xfrHexBlob(d_cert, true);
310 )
311
312 boilerplate_conv(OPENPGPKEY, 61,
313 conv.xfrBlob(d_keyring);
314 )
315
316 DSRecordContent::DSRecordContent() {}
317 boilerplate_conv(DS, 43,
318 conv.xfr16BitInt(d_tag);
319 conv.xfr8BitInt(d_algorithm);
320 conv.xfr8BitInt(d_digesttype);
321 conv.xfrHexBlob(d_digest, true); // keep reading across spaces
322 )
323
324 CDSRecordContent::CDSRecordContent() {}
325 boilerplate_conv(CDS, 59,
326 conv.xfr16BitInt(d_tag);
327 conv.xfr8BitInt(d_algorithm);
328 conv.xfr8BitInt(d_digesttype);
329 conv.xfrHexBlob(d_digest, true); // keep reading across spaces
330 )
331
332 DLVRecordContent::DLVRecordContent() {}
333 boilerplate_conv(DLV,32769 ,
334 conv.xfr16BitInt(d_tag);
335 conv.xfr8BitInt(d_algorithm);
336 conv.xfr8BitInt(d_digesttype);
337 conv.xfrHexBlob(d_digest, true); // keep reading across spaces
338 )
339
340
341 boilerplate_conv(SSHFP, 44,
342 conv.xfr8BitInt(d_algorithm);
343 conv.xfr8BitInt(d_fptype);
344 conv.xfrHexBlob(d_fingerprint, true);
345 )
346
347 boilerplate_conv(RRSIG, 46,
348 conv.xfrType(d_type);
349 conv.xfr8BitInt(d_algorithm);
350 conv.xfr8BitInt(d_labels);
351 conv.xfr32BitInt(d_originalttl);
352 conv.xfrTime(d_sigexpire);
353 conv.xfrTime(d_siginception);
354 conv.xfr16BitInt(d_tag);
355 conv.xfrName(d_signer);
356 conv.xfrBlob(d_signature);
357 )
358
359 RRSIGRecordContent::RRSIGRecordContent() {}
360
361 boilerplate_conv(DNSKEY, 48,
362 conv.xfr16BitInt(d_flags);
363 conv.xfr8BitInt(d_protocol);
364 conv.xfr8BitInt(d_algorithm);
365 conv.xfrBlob(d_key);
366 )
367 DNSKEYRecordContent::DNSKEYRecordContent() {}
368
369 boilerplate_conv(CDNSKEY, 60,
370 conv.xfr16BitInt(d_flags);
371 conv.xfr8BitInt(d_protocol);
372 conv.xfr8BitInt(d_algorithm);
373 conv.xfrBlob(d_key);
374 )
375 CDNSKEYRecordContent::CDNSKEYRecordContent() {}
376
377 boilerplate_conv(RKEY, 57,
378 conv.xfr16BitInt(d_flags);
379 conv.xfr8BitInt(d_protocol);
380 conv.xfrBlob(d_key);
381 )
382 RKEYRecordContent::RKEYRecordContent() {}
383
384 /* EUI48 start */
385 void EUI48RecordContent::report(void)
386 {
387 regist(1, QType::EUI48, &make, &make, "EUI48");
388 }
389 DNSRecordContent* EUI48RecordContent::make(const DNSRecord &dr, PacketReader& pr)
390 {
391 if(dr.d_clen!=6)
392 throw MOADNSException("Wrong size for EUI48 record");
393
394 EUI48RecordContent* ret=new EUI48RecordContent();
395 pr.copyRecord((uint8_t*) &ret->d_eui48, 6);
396 return ret;
397 }
398 DNSRecordContent* EUI48RecordContent::make(const string& zone)
399 {
400 // try to parse
401 EUI48RecordContent *ret=new EUI48RecordContent();
402 // format is 6 hex bytes and dashes
403 if (sscanf(zone.c_str(), "%2hhx-%2hhx-%2hhx-%2hhx-%2hhx-%2hhx",
404 ret->d_eui48, ret->d_eui48+1, ret->d_eui48+2,
405 ret->d_eui48+3, ret->d_eui48+4, ret->d_eui48+5) != 6) {
406 throw MOADNSException("Asked to encode '"+zone+"' as an EUI48 address, but does not parse");
407 }
408 return ret;
409 }
410 void EUI48RecordContent::toPacket(DNSPacketWriter& pw)
411 {
412 string blob(d_eui48, d_eui48+6);
413 pw.xfrBlob(blob);
414 }
415 string EUI48RecordContent::getZoneRepresentation(bool noDot) const
416 {
417 char tmp[18];
418 snprintf(tmp,sizeof(tmp),"%02x-%02x-%02x-%02x-%02x-%02x",
419 d_eui48[0], d_eui48[1], d_eui48[2],
420 d_eui48[3], d_eui48[4], d_eui48[5]);
421 return tmp;
422 }
423
424 /* EUI48 end */
425
426 /* EUI64 start */
427
428 void EUI64RecordContent::report(void)
429 {
430 regist(1, QType::EUI64, &make, &make, "EUI64");
431 }
432 DNSRecordContent* EUI64RecordContent::make(const DNSRecord &dr, PacketReader& pr)
433 {
434 if(dr.d_clen!=8)
435 throw MOADNSException("Wrong size for EUI64 record");
436
437 EUI64RecordContent* ret=new EUI64RecordContent();
438 pr.copyRecord((uint8_t*) &ret->d_eui64, 8);
439 return ret;
440 }
441 DNSRecordContent* EUI64RecordContent::make(const string& zone)
442 {
443 // try to parse
444 EUI64RecordContent *ret=new EUI64RecordContent();
445 // format is 8 hex bytes and dashes
446 if (sscanf(zone.c_str(), "%2hhx-%2hhx-%2hhx-%2hhx-%2hhx-%2hhx-%2hhx-%2hhx",
447 ret->d_eui64, ret->d_eui64+1, ret->d_eui64+2,
448 ret->d_eui64+3, ret->d_eui64+4, ret->d_eui64+5,
449 ret->d_eui64+6, ret->d_eui64+7) != 8) {
450 throw MOADNSException("Asked to encode '"+zone+"' as an EUI64 address, but does not parse");
451 }
452 return ret;
453 }
454 void EUI64RecordContent::toPacket(DNSPacketWriter& pw)
455 {
456 string blob(d_eui64, d_eui64+8);
457 pw.xfrBlob(blob);
458 }
459 string EUI64RecordContent::getZoneRepresentation(bool noDot) const
460 {
461 char tmp[24];
462 snprintf(tmp,sizeof(tmp),"%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x",
463 d_eui64[0], d_eui64[1], d_eui64[2],
464 d_eui64[3], d_eui64[4], d_eui64[5],
465 d_eui64[6], d_eui64[7]);
466 return tmp;
467 }
468
469 /* EUI64 end */
470
471 boilerplate_conv(TKEY, QType::TKEY,
472 conv.xfrName(d_algo);
473 conv.xfr32BitInt(d_inception);
474 conv.xfr32BitInt(d_expiration);
475 conv.xfr16BitInt(d_mode);
476 conv.xfr16BitInt(d_error);
477 conv.xfr16BitInt(d_keysize);
478 if (d_keysize>0) conv.xfrBlobNoSpaces(d_key, d_keysize);
479 conv.xfr16BitInt(d_othersize);
480 if (d_othersize>0) conv.xfrBlobNoSpaces(d_other, d_othersize);
481 )
482 TKEYRecordContent::TKEYRecordContent() { d_othersize = 0; } // fix CID#1288932
483
484 boilerplate_conv(URI, QType::URI,
485 conv.xfr8BitInt(d_priority);
486 conv.xfr8BitInt(d_weight);
487 conv.xfrText(d_target, true, false);
488 )
489
490 boilerplate_conv(CAA, QType::CAA,
491 conv.xfr8BitInt(d_flags);
492 conv.xfrUnquotedText(d_tag, true);
493 conv.xfrText(d_value, true, false); /* no lenField */
494 )
495
496 static uint16_t makeTag(const std::string& data)
497 {
498 const unsigned char* key=(const unsigned char*)data.c_str();
499 unsigned int keysize=data.length();
500
501 unsigned long ac; /* assumed to be 32 bits or larger */
502 unsigned int i; /* loop index */
503
504 for ( ac = 0, i = 0; i < keysize; ++i )
505 ac += (i & 1) ? key[i] : key[i] << 8;
506 ac += (ac >> 16) & 0xFFFF;
507 return ac & 0xFFFF;
508 }
509
510 uint16_t DNSKEYRecordContent::getTag() const
511 {
512 DNSKEYRecordContent tmp(*this);
513 return makeTag(tmp.serialize(DNSName())); // this can't be const for some reason
514 }
515
516 uint16_t DNSKEYRecordContent::getTag()
517 {
518 return makeTag(this->serialize(DNSName()));
519 }
520
521
522 bool getEDNSOpts(const MOADNSParser& mdp, EDNSOpts* eo)
523 {
524 eo->d_Z=0;
525 if(mdp.d_header.arcount && !mdp.d_answers.empty()) {
526 for(const MOADNSParser::answers_t::value_type& val : mdp.d_answers) {
527 if(val.first.d_place == DNSResourceRecord::ADDITIONAL && val.first.d_type == QType::OPT) {
528 eo->d_packetsize=val.first.d_class;
529
530 EDNS0Record stuff;
531 uint32_t ttl=ntohl(val.first.d_ttl);
532 static_assert(sizeof(EDNS0Record) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t)");
533 memcpy(&stuff, &ttl, sizeof(stuff));
534
535 eo->d_extRCode=stuff.extRCode;
536 eo->d_version=stuff.version;
537 eo->d_Z = ntohs(stuff.Z);
538 OPTRecordContent* orc =
539 dynamic_cast<OPTRecordContent*>(val.first.d_content.get());
540 if(!orc)
541 return false;
542 orc->getData(eo->d_options);
543 return true;
544 }
545 }
546 }
547 return false;
548 }
549
550 DNSRecord makeOpt(int udpsize, int extRCode, int Z)
551 {
552 EDNS0Record stuff;
553 stuff.extRCode=0;
554 stuff.version=0;
555 stuff.Z=htons(Z);
556 DNSRecord dr;
557 static_assert(sizeof(EDNS0Record) == sizeof(dr.d_ttl), "sizeof(EDNS0Record) must match sizeof(DNSRecord.d_ttl)");
558 memcpy(&dr.d_ttl, &stuff, sizeof(stuff));
559 dr.d_ttl=ntohl(dr.d_ttl);
560 dr.d_name=DNSName(".");
561 dr.d_type = QType::OPT;
562 dr.d_class=udpsize;
563 dr.d_place=DNSResourceRecord::ADDITIONAL;
564 dr.d_content = std::make_shared<OPTRecordContent>();
565 // if we ever do options, I think we stuff them into OPTRecordContent::data
566 return dr;
567 }
568
569
570 void reportBasicTypes()
571 {
572 ARecordContent::report();
573 AAAARecordContent::report();
574 NSRecordContent::report();
575 CNAMERecordContent::report();
576 MXRecordContent::report();
577 SOARecordContent::report();
578 SRVRecordContent::report();
579 PTRRecordContent::report();
580 DNSRecordContent::regist(QClass::CHAOS, QType::TXT, &TXTRecordContent::make, &TXTRecordContent::make, "TXT");
581 TXTRecordContent::report();
582 DNSRecordContent::regist(QClass::IN, QType::ANY, 0, 0, "ANY");
583 DNSRecordContent::regist(QClass::IN, QType::AXFR, 0, 0, "AXFR");
584 DNSRecordContent::regist(QClass::IN, QType::IXFR, 0, 0, "IXFR");
585 }
586
587 void reportOtherTypes()
588 {
589 AFSDBRecordContent::report();
590 DNAMERecordContent::report();
591 ALIASRecordContent::report();
592 SPFRecordContent::report();
593 NAPTRRecordContent::report();
594 LOCRecordContent::report();
595 HINFORecordContent::report();
596 RPRecordContent::report();
597 KEYRecordContent::report();
598 DNSKEYRecordContent::report();
599 DHCIDRecordContent::report();
600 CDNSKEYRecordContent::report();
601 RKEYRecordContent::report();
602 RRSIGRecordContent::report();
603 DSRecordContent::report();
604 CDSRecordContent::report();
605 SSHFPRecordContent::report();
606 CERTRecordContent::report();
607 NSECRecordContent::report();
608 NSEC3RecordContent::report();
609 NSEC3PARAMRecordContent::report();
610 TLSARecordContent::report();
611 OPENPGPKEYRecordContent::report();
612 DLVRecordContent::report();
613 DNSRecordContent::regist(QClass::ANY, QType::TSIG, &TSIGRecordContent::make, &TSIGRecordContent::make, "TSIG");
614 DNSRecordContent::regist(QClass::ANY, QType::TKEY, &TKEYRecordContent::make, &TKEYRecordContent::make, "TKEY");
615 //TSIGRecordContent::report();
616 OPTRecordContent::report();
617 EUI48RecordContent::report();
618 EUI64RecordContent::report();
619 MINFORecordContent::report();
620 URIRecordContent::report();
621 CAARecordContent::report();
622 }
623
624 void reportAllTypes()
625 {
626 reportBasicTypes();
627 reportOtherTypes();
628 }
629
630 ComboAddress getAddr(const DNSRecord& dr, uint16_t defport)
631 {
632 if(auto addr=getRR<ARecordContent>(dr)) {
633 return addr->getCA(defport);
634 }
635 else
636 return getRR<AAAARecordContent>(dr)->getCA(defport);
637 }
638
639
640 #if 0
641 static struct Reporter
642 {
643 Reporter()
644 {
645 reportAllTypes();
646 }
647 } reporter __attribute__((init_priority(65535)));
648 #endif