• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 15920880335

26 Jun 2025 03:30PM UTC coverage: 61.923% (-3.7%) from 65.652%
15920880335

push

github

web-flow
Merge pull request #15669 from miodvallat/serial_keyer

Increase zone serial number after zone key operations

38311 of 91850 branches covered (41.71%)

Branch coverage included in aggregate %.

27 of 29 new or added lines in 1 file covered. (93.1%)

6308 existing lines in 78 files now uncovered.

120482 of 164587 relevant lines covered (73.2%)

5965233.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.42
/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

26
#include <boost/format.hpp>
27

28
#include "utility.hh"
29
#include "dnsrecords.hh"
30
#include "iputils.hh"
31

32
void DNSResourceRecord::setContent(const string &cont) {
175✔
33
  content = cont;
175✔
34
  switch(qtype.getCode()) {
175✔
35
    case QType::SRV:
1✔
36
    case QType::MX:
1!
37
      if (content.size() >= 2 && *(content.rbegin()+1) == ' ')
1!
38
        return;
×
39
      [[fallthrough]];
1✔
40
#if !defined(RECURSOR)
41
    case QType::ALIAS:
1!
42
#endif
1✔
43
    case QType::CNAME:
25✔
44
    case QType::DNAME:
25!
45
    case QType::NS:
27✔
46
    case QType::PTR:
27!
47
      if (content.size() >= 2 && *(content.rbegin()) == '.')
27!
48
        boost::erase_tail(content, 1);
27✔
49
  }
175✔
50
}
175✔
51

52
string DNSResourceRecord::getZoneRepresentation(bool noDot) const {
58,784✔
53
  ostringstream ret;
58,784✔
54
  vector<string> parts;
58,784✔
55
  string last;
58,784✔
56

57
  switch(qtype.getCode()) {
58,784✔
58
    case QType::SRV:
36✔
59
    case QType::MX:
264✔
60
      stringtok(parts, content);
264✔
61
      if (parts.empty())
264!
62
        return "";
×
63
      last = *parts.rbegin();
264✔
64
      ret << content;
264✔
65
      if (last == ".")
264!
66
        break;
×
67
      if (*(last.rbegin()) != '.' && !noDot)
264!
68
        ret << ".";
264✔
69
      break;
264✔
70
#if !defined(RECURSOR)
71
    case QType::ALIAS:
58,784!
72
#endif
73
    case QType::CNAME:
48✔
74
    case QType::DNAME:
48!
75
    case QType::NS:
132✔
76
    case QType::PTR:
144✔
77
      ret<<content;
144✔
78
      if (*(content.rbegin()) != '.' && !noDot)
144!
79
        ret<<".";
144✔
80
      break;
144✔
81
    default:
58,376✔
82
      ret<<content;
58,376✔
83
    break;
58,376✔
84
  }
58,784✔
85
  return ret.str();
58,784✔
86
}
58,784✔
87

88
bool DNSResourceRecord::operator==(const DNSResourceRecord& rhs)
89
{
840✔
90
  string lcontent=toLower(content);
840✔
91
  string rcontent=toLower(rhs.content);
840✔
92

93
  return
840✔
94
    std::tie(qname, qtype, lcontent, ttl) ==
840✔
95
    std::tie(rhs.qname, rhs.qtype, rcontent, rhs.ttl);
840✔
96
}
840✔
97

98
boilerplate_conv(A, conv.xfrIP(d_ip));
99

100
ARecordContent::ARecordContent(uint32_t ip)
101
{
426,936✔
102
  d_ip = ip;
426,936✔
103
}
426,936✔
104

105
ARecordContent::ARecordContent(const ComboAddress& ca)
106
{
9,433✔
107
  d_ip = ca.sin4.sin_addr.s_addr;
9,433✔
108
}
9,433✔
109

110
AAAARecordContent::AAAARecordContent(const ComboAddress& ca)
111
{
7,927✔
112
  d_ip6.assign((const char*)ca.sin6.sin6_addr.s6_addr, 16);
7,927✔
113
}
7,927✔
114

115

116

117
ComboAddress ARecordContent::getCA(int port) const
118
{
24,308✔
119
  ComboAddress ret;
24,308✔
120
  ret.sin4.sin_family=AF_INET;
24,308✔
121
  ret.sin4.sin_port=htons(port);
24,308✔
122
  memcpy(&ret.sin4.sin_addr.s_addr, &d_ip, sizeof(ret.sin4.sin_addr.s_addr));
24,308✔
123
  return ret;
24,308✔
124
}
24,308✔
125

126
ComboAddress AAAARecordContent::getCA(int port) const
127
{
595,432✔
128
  ComboAddress ret;
595,432✔
129
  ret.reset();
595,432✔
130

131
  ret.sin4.sin_family=AF_INET6;
595,432✔
132
  ret.sin6.sin6_port = htons(port);
595,432✔
133
  memcpy(&ret.sin6.sin6_addr.s6_addr, d_ip6.c_str(), sizeof(ret.sin6.sin6_addr.s6_addr));
595,432✔
134
  return ret;
595,432✔
135
}
595,432✔
136

137

138
void ARecordContent::doRecordCheck(const DNSRecord& dr)
139
{
906,629✔
140
  if(dr.d_clen!=4)
906,629!
141
    throw MOADNSException("Wrong size for A record ("+std::to_string(dr.d_clen)+")");
×
142
}
906,629✔
143

144
boilerplate_conv(AAAA, conv.xfrIP6(d_ip6); );
145

146
boilerplate_conv(NS, conv.xfrName(d_content, true));
147
boilerplate_conv(PTR, conv.xfrName(d_content, true));
148
boilerplate_conv(CNAME, conv.xfrName(d_content, true));
149
#if !defined(RECURSOR)
150
boilerplate_conv(ALIAS, conv.xfrName(d_content, false));
151
#endif
152
boilerplate_conv(DNAME, conv.xfrName(d_content));
153
boilerplate_conv(MB, conv.xfrName(d_madname, true));
154
boilerplate_conv(MG, conv.xfrName(d_mgmname, true));
155
boilerplate_conv(MR, conv.xfrName(d_alias, true));
156
boilerplate_conv(MINFO, conv.xfrName(d_rmailbx, true); conv.xfrName(d_emailbx, true));
157
boilerplate_conv(TXT, conv.xfrText(d_text, true));
158
#ifdef HAVE_LUA_RECORDS
159
boilerplate_conv(LUA, conv.xfrType(d_type); conv.xfrText(d_code, true));
160
#endif
161
boilerplate_conv(ENT, );
162
boilerplate_conv(SPF, conv.xfrText(d_text, true));
163
boilerplate_conv(HINFO, conv.xfrText(d_cpu);   conv.xfrText(d_host));
164

165
boilerplate_conv(RP,
166
                 conv.xfrName(d_mbox);
167
                 conv.xfrName(d_info)
168
                 );
169

170

171
boilerplate_conv(OPT,
172
                   conv.xfrBlob(d_data)
173
                 );
174

175
#ifdef HAVE_LUA_RECORDS
176

177
bool g_luaRecordInsertWhitespace;
178

179
string LUARecordContent::getCode() const
180
{
181
  // in d_code, series of "part1" "part2"
182
  vector<string> parts;
183
  stringtok(parts, d_code, "\"");
184
  string ret;
185
  if (g_luaRecordInsertWhitespace) { // default before 5.0
×
186
    for(const auto& part : parts) {
×
187
      ret += part;
188
      ret.append(1, ' ');
189
    }
190
  }
191
  else { // default since 5.0
192
    for(const auto& part : parts) {
×
193
      if (part != " ") {
×
194
        ret += part;
195
      }
196
    }
197
  }
198
  return ret;
199
}
200
#endif
201

202
void OPTRecordContent::getData(vector<pair<uint16_t, string> >& options) const
203
{
131,857✔
204
  string::size_type pos=0;
131,857✔
205
  uint16_t code, len;
131,857✔
206
  while(d_data.size() >= 4 + pos) {
132,575✔
207
    code = 256 * (unsigned char)d_data.at(pos) + (unsigned char)d_data.at(pos+1);
718✔
208
    len = 256 * (unsigned char)d_data.at(pos+2) + (unsigned char)d_data.at(pos+3);
718✔
209
    pos+=4;
718✔
210

211
    if(pos + len > d_data.size())
718!
212
      break;
×
213

214
    string field(d_data.c_str() + pos, len);
718✔
215
    pos+=len;
718✔
216
    options.emplace_back(code, std::move(field));
718✔
217
  }
718✔
218
}
131,857✔
219

220
boilerplate_conv(TSIG,
221
                 conv.xfrName(d_algoName);
222
                 conv.xfr48BitInt(d_time);
223
                 conv.xfr16BitInt(d_fudge);
224
                 uint16_t size=d_mac.size();
225
                 conv.xfr16BitInt(size);
226
                 if (size>0) conv.xfrBlobNoSpaces(d_mac, size);
227
                 conv.xfr16BitInt(d_origID);
228
                 conv.xfr16BitInt(d_eRcode);
229
                 size=d_otherData.size();
230
                 conv.xfr16BitInt(size);
231
                 if (size>0) conv.xfrBlobNoSpaces(d_otherData, size);
232
                 );
233

234
MXRecordContent::MXRecordContent(uint16_t preference, DNSName  mxname):  d_preference(preference), d_mxname(std::move(mxname))
235
{
×
236
}
×
237

238
boilerplate_conv(MX,
239
                 conv.xfr16BitInt(d_preference);
240
                 conv.xfrName(d_mxname, true);
241
                 )
242

243
boilerplate_conv(KX,
244
                 conv.xfr16BitInt(d_preference);
245
                 conv.xfrName(d_exchanger, false);
246
                 )
247

248
boilerplate_conv(IPSECKEY,
249
   conv.xfr8BitInt(d_preference);
250
   conv.xfr8BitInt(d_gatewaytype);
251
   conv.xfr8BitInt(d_algorithm);
252

253
   // now we need to determine values
254
   switch(d_gatewaytype) {
255
   case 0: // NO KEY
256
     break;
257
   case 1: // IPv4 GW
258
     conv.xfrIP(d_ip4);
259
     break;
260
   case 2: // IPv6 GW
261
     conv.xfrIP6(d_ip6);
262
     break;
263
   case 3: // DNS label
264
     conv.xfrName(d_gateway, false);
265
     break;
266
   default:
267
     throw MOADNSException("Parsing record content: invalid gateway type");
268
   };
269

270
   switch(d_algorithm) {
271
   case 0:
272
     break;
273
   case 1:
274
   case 2:
275
     conv.xfrBlob(d_publickey);
276
     break;
277
   default:
278
     throw MOADNSException("Parsing record content: invalid algorithm type");
279
   }
280
)
281

282
boilerplate_conv(DHCID,
283
                 conv.xfrBlob(d_content);
284
                 )
285

286

287
boilerplate_conv(AFSDB,
288
                 conv.xfr16BitInt(d_subtype);
289
                 conv.xfrName(d_hostname);
290
                 )
291

292

293
boilerplate_conv(NAPTR,
294
                 conv.xfr16BitInt(d_order);    conv.xfr16BitInt(d_preference);
295
                 conv.xfrText(d_flags);        conv.xfrText(d_services);         conv.xfrText(d_regexp);
296
                 conv.xfrName(d_replacement);
297
                 )
298

299

300
SRVRecordContent::SRVRecordContent(uint16_t preference, uint16_t weight, uint16_t port, DNSName  target)
301
: d_weight(weight), d_port(port), d_target(std::move(target)), d_preference(preference)
302
{}
×
303

304
boilerplate_conv(SRV,
305
                 conv.xfr16BitInt(d_preference);   conv.xfr16BitInt(d_weight);   conv.xfr16BitInt(d_port);
306
                 conv.xfrName(d_target);
307
                 )
308

309
SOARecordContent::SOARecordContent(DNSName  mname, DNSName  rname, const struct soatimes& st)
310
: d_mname(std::move(mname)), d_rname(std::move(rname)), d_st(st)
311
{
72,464✔
312
}
72,464✔
313

314
boilerplate_conv(SOA,
315
                 conv.xfrName(d_mname, true);
316
                 conv.xfrName(d_rname, true);
317
                 conv.xfr32BitInt(d_st.serial);
318
                 conv.xfr32BitInt(d_st.refresh);
319
                 conv.xfr32BitInt(d_st.retry);
320
                 conv.xfr32BitInt(d_st.expire);
321
                 conv.xfr32BitInt(d_st.minimum);
322
                 );
323
#undef KEY
324
boilerplate_conv(KEY,
325
                 conv.xfr16BitInt(d_flags);
326
                 conv.xfr8BitInt(d_protocol);
327
                 conv.xfr8BitInt(d_algorithm);
328
                 conv.xfrBlob(d_certificate);
329
                 );
330

331
boilerplate_conv(ZONEMD,
332
                 conv.xfr32BitInt(d_serial);
333
                 conv.xfr8BitInt(d_scheme);
334
                 conv.xfr8BitInt(d_hashalgo);
335
                 conv.xfrHexBlob(d_digest, true); // keep reading across spaces
336
                 );
337

338
boilerplate_conv(CERT,
339
                 conv.xfr16BitInt(d_type);
340
                 if (d_type == 0) throw MOADNSException("CERT type 0 is reserved");
341

342
                 conv.xfr16BitInt(d_tag);
343
                 conv.xfr8BitInt(d_algorithm);
344
                 conv.xfrBlob(d_certificate);
345
                 )
346

347
boilerplate_conv(TLSA,
348
                 conv.xfr8BitInt(d_certusage);
349
                 conv.xfr8BitInt(d_selector);
350
                 conv.xfr8BitInt(d_matchtype);
351
                 conv.xfrHexBlob(d_cert, true);
352
                 )
353

354
boilerplate_conv(OPENPGPKEY,
355
                 conv.xfrBlob(d_keyring);
356
                 )
357

358
boilerplate_conv(SVCB,
359
                 conv.xfr16BitInt(d_priority);
360
                 conv.xfrName(d_target, false);
361
                 if (d_priority != 0) {
362
                   conv.xfrSvcParamKeyVals(d_params);
363
                 }
364
                 )
365

366
boilerplate_conv(HTTPS,
367
                 conv.xfr16BitInt(d_priority);
368
                 conv.xfrName(d_target, false);
369
                 if (d_priority != 0) {
370
                   conv.xfrSvcParamKeyVals(d_params);
371
                 }
372
                 )
373

374
boilerplate_conv(SMIMEA,
375
                 conv.xfr8BitInt(d_certusage);
376
                 conv.xfr8BitInt(d_selector);
377
                 conv.xfr8BitInt(d_matchtype);
378
                 conv.xfrHexBlob(d_cert, true);
379
                 )
380

381
DSRecordContent::DSRecordContent() = default;
4,188✔
382
boilerplate_conv(DS,
383
                 conv.xfr16BitInt(d_tag);
384
                 conv.xfr8BitInt(d_algorithm);
385
                 conv.xfr8BitInt(d_digesttype);
386
                 conv.xfrHexBlob(d_digest, true); // keep reading across spaces
387
                 )
388

389
CDSRecordContent::CDSRecordContent() = default;
×
390
boilerplate_conv(CDS,
391
                 conv.xfr16BitInt(d_tag);
392
                 conv.xfr8BitInt(d_algorithm);
393
                 conv.xfr8BitInt(d_digesttype);
394
                 conv.xfrHexBlob(d_digest, true); // keep reading across spaces
395
                 )
396

397
DLVRecordContent::DLVRecordContent() = default;
×
398
boilerplate_conv(DLV,
399
                 conv.xfr16BitInt(d_tag);
400
                 conv.xfr8BitInt(d_algorithm);
401
                 conv.xfr8BitInt(d_digesttype);
402
                 conv.xfrHexBlob(d_digest, true); // keep reading across spaces
403
                 )
404

405

406
boilerplate_conv(SSHFP,
407
                 conv.xfr8BitInt(d_algorithm);
408
                 conv.xfr8BitInt(d_fptype);
409
                 conv.xfrHexBlob(d_fingerprint, true);
410
                 )
411

412
boilerplate_conv(RRSIG,
413
                 conv.xfrType(d_type);
414
                   conv.xfr8BitInt(d_algorithm);
415
                   conv.xfr8BitInt(d_labels);
416
                   conv.xfr32BitInt(d_originalttl);
417
                   conv.xfrTime(d_sigexpire);
418
                   conv.xfrTime(d_siginception);
419
                 conv.xfr16BitInt(d_tag);
420
                 conv.xfrName(d_signer);
421
                 conv.xfrBlob(d_signature);
422
                 )
423

424
RRSIGRecordContent::RRSIGRecordContent() = default;
859,435✔
425

426
boilerplate_conv(DNSKEY,
427
                 conv.xfr16BitInt(d_flags);
428
                 conv.xfr8BitInt(d_protocol);
429
                 conv.xfr8BitInt(d_algorithm);
430
                 conv.xfrBlob(d_key);
431
                 )
432
DNSKEYRecordContent::DNSKEYRecordContent() = default;
22,985✔
433

434
boilerplate_conv(CDNSKEY,
435
                 conv.xfr16BitInt(d_flags);
436
                 conv.xfr8BitInt(d_protocol);
437
                 conv.xfr8BitInt(d_algorithm);
438
                 conv.xfrBlob(d_key);
439
                 )
440
CDNSKEYRecordContent::CDNSKEYRecordContent() = default;
×
441

442
boilerplate_conv(RKEY,
443
                 conv.xfr16BitInt(d_flags);
444
                 conv.xfr8BitInt(d_protocol);
445
                 conv.xfr8BitInt(d_algorithm);
446
                 conv.xfrBlob(d_key);
447
                 )
448
RKEYRecordContent::RKEYRecordContent() = default;
×
449

450
boilerplate_conv(NID,
451
                 conv.xfr16BitInt(d_preference);
452
                 conv.xfrNodeOrLocatorID(d_node_id);)
453

454
boilerplate_conv(L32,
455
                 conv.xfr16BitInt(d_preference);
456
                 conv.xfrIP(d_locator);)
457

458
boilerplate_conv(L64,
459
                 conv.xfr16BitInt(d_preference);
460
                 conv.xfrNodeOrLocatorID(d_locator);)
461

462
boilerplate_conv(LP,
463
                 conv.xfr16BitInt(d_preference);
464
                 conv.xfrName(d_fqdn, false);)
465

466
/* EUI48 start */
467
void EUI48RecordContent::report(const ReportIsOnlyCallableByReportAllTypes& /* unused */)
468
{
33,455✔
469
  regist(1, QType::EUI48, &make, &make, "EUI48");
33,455✔
470
}
33,455✔
471
std::shared_ptr<DNSRecordContent> EUI48RecordContent::make(const DNSRecord &dr, PacketReader& pr)
472
{
212✔
473
    if(dr.d_clen!=6)
212!
474
      throw MOADNSException("Wrong size for EUI48 record");
×
475

476
    auto ret=std::make_shared<EUI48RecordContent>();
212✔
477
    pr.copyRecord((uint8_t*) &ret->d_eui48, 6);
212✔
478
    return ret;
212✔
479
}
212✔
480
std::shared_ptr<DNSRecordContent> EUI48RecordContent::make(const string& zone)
481
{
127✔
482
    // try to parse
483
    auto ret=std::make_shared<EUI48RecordContent>();
127✔
484
    // format is 6 hex bytes and dashes
485
    if (sscanf(zone.c_str(), "%2hhx-%2hhx-%2hhx-%2hhx-%2hhx-%2hhx",
127✔
486
           ret->d_eui48, ret->d_eui48+1, ret->d_eui48+2,
127✔
487
           ret->d_eui48+3, ret->d_eui48+4, ret->d_eui48+5) != 6) {
127✔
488
       throw MOADNSException("Asked to encode '"+zone+"' as an EUI48 address, but does not parse");
3✔
489
    }
3✔
490
    return ret;
124✔
491
}
127✔
492
void EUI48RecordContent::toPacket(DNSPacketWriter& pw) const
493
{
908✔
494
    string blob(d_eui48, d_eui48+6);
908✔
495
    pw.xfrBlob(blob);
908✔
496
}
908✔
497

498
string EUI48RecordContent::getZoneRepresentation(bool /* noDot */) const
499
{
203✔
500
    char tmp[18];
203✔
501
    snprintf(tmp,sizeof(tmp),"%02x-%02x-%02x-%02x-%02x-%02x",
203✔
502
           d_eui48[0], d_eui48[1], d_eui48[2],
203✔
503
           d_eui48[3], d_eui48[4], d_eui48[5]);
203✔
504
    return tmp;
203✔
505
}
203✔
506

507
/* EUI48 end */
508

509
/* EUI64 start */
510

511
void EUI64RecordContent::report(const ReportIsOnlyCallableByReportAllTypes& /* unused */)
512
{
33,455✔
513
  regist(1, QType::EUI64, &make, &make, "EUI64");
33,455✔
514
}
33,455✔
515
std::shared_ptr<DNSRecordContent> EUI64RecordContent::make(const DNSRecord &dr, PacketReader& pr)
516
{
212✔
517
    if(dr.d_clen!=8)
212!
518
      throw MOADNSException("Wrong size for EUI64 record");
×
519

520
    auto ret=std::make_shared<EUI64RecordContent>();
212✔
521
    pr.copyRecord((uint8_t*) &ret->d_eui64, 8);
212✔
522
    return ret;
212✔
523
}
212✔
524
std::shared_ptr<DNSRecordContent> EUI64RecordContent::make(const string& zone)
525
{
127✔
526
    // try to parse
527
    auto ret=std::make_shared<EUI64RecordContent>();
127✔
528
    // format is 8 hex bytes and dashes
529
    if (sscanf(zone.c_str(), "%2hhx-%2hhx-%2hhx-%2hhx-%2hhx-%2hhx-%2hhx-%2hhx",
127✔
530
           ret->d_eui64, ret->d_eui64+1, ret->d_eui64+2,
127✔
531
           ret->d_eui64+3, ret->d_eui64+4, ret->d_eui64+5,
127✔
532
           ret->d_eui64+6, ret->d_eui64+7) != 8) {
127✔
533
       throw MOADNSException("Asked to encode '"+zone+"' as an EUI64 address, but does not parse");
3✔
534
    }
3✔
535
    return ret;
124✔
536
}
127✔
537
void EUI64RecordContent::toPacket(DNSPacketWriter& pw) const
538
{
908✔
539
    string blob(d_eui64, d_eui64+8);
908✔
540
    pw.xfrBlob(blob);
908✔
541
}
908✔
542

543
string EUI64RecordContent::getZoneRepresentation(bool /* noDot */) const
544
{
203✔
545
    char tmp[24];
203✔
546
    snprintf(tmp,sizeof(tmp),"%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x",
203✔
547
           d_eui64[0], d_eui64[1], d_eui64[2],
203✔
548
           d_eui64[3], d_eui64[4], d_eui64[5],
203✔
549
           d_eui64[6], d_eui64[7]);
203✔
550
    return tmp;
203✔
551
}
203✔
552

553
/* EUI64 end */
554

555
/* APL start */
556
/* https://tools.ietf.org/html/rfc3123 */
557
void APLRecordContent::report(const ReportIsOnlyCallableByReportAllTypes& /* unused */)
558
{
33,455✔
559
  regist(1, QType::APL, &make, &make, "APL");
33,455✔
560
}
33,455✔
561

562
// Parse incoming packets (e.g. nsupdate)
563
std::shared_ptr<DNSRecordContent> APLRecordContent::make(const DNSRecord &dr, PacketReader& pr) {
66✔
564
  uint8_t temp;
66✔
565
  APLRDataElement ard;
66✔
566
  size_t processed = 0;
66✔
567

568
  auto ret=std::make_shared<APLRecordContent>();
66✔
569

570
  while (processed<dr.d_clen) {
135✔
571
    pr.xfr16BitInt(ard.d_family);
69✔
572
    pr.xfr8BitInt(ard.d_prefix);
69✔
573
    pr.xfr8BitInt(temp);
69✔
574
    ard.d_n = (temp & 128) >> 7;
69✔
575
    ard.d_afdlength = temp & 127;
69✔
576

577
    if (ard.d_family == APL_FAMILY_IPV4) {
69✔
578
      if (ard.d_afdlength > 4) {
30!
579
        throw MOADNSException("Invalid IP length for IPv4 APL");
×
580
      }
×
581
      memset(ard.d_ip.d_ip4, 0, sizeof(ard.d_ip.d_ip4));
30✔
582
      for (u_int i=0; i < ard.d_afdlength; i++)
111✔
583
        pr.xfr8BitInt(ard.d_ip.d_ip4[i]);
81✔
584
    } else if (ard.d_family == APL_FAMILY_IPV6) {
39!
585
      if (ard.d_afdlength > 16) {
39!
586
        throw MOADNSException("Invalid IP length for IPv6 APL");
×
587
      }
×
588
      memset(ard.d_ip.d_ip6, 0, sizeof(ard.d_ip.d_ip6));
39✔
589
      for (u_int i=0; i < ard.d_afdlength; i++)
327✔
590
        pr.xfr8BitInt(ard.d_ip.d_ip6[i]);
288✔
591
    } else
39✔
592
    throw MOADNSException("Unknown family for APL record");
×
593

594
    processed += 4 + ard.d_afdlength;
69✔
595

596
    ret->aplrdata.push_back(ard);
69✔
597
  }
69✔
598

599
  return ret;
66✔
600
}
66✔
601

602
// Parse a single APL <apitem>
603
APLRDataElement APLRecordContent::parseAPLElement(const string& element) {
69✔
604
  string record;
69✔
605
  Netmask nm;
69✔
606
  unsigned int bytes;
69✔
607
  bool done_trimming;
69✔
608
  APLRDataElement ard;
69✔
609

610
  // Parse the optional leading ! (negate)
611
  if (element.at(0) == '!') {
69✔
612
    ard.d_n = true;
6✔
613
    record = element.substr(1, element.length()-1);
6✔
614
  } else {
63✔
615
    ard.d_n = false;
63✔
616
    record = element;
63✔
617
  }
63✔
618

619
  if (record.find('/') == string::npos) { // Required by RFC section 5
69!
620
    throw MOADNSException("Asked to decode '"+element+"' as an APL record, but missing subnet mask");
×
621
  }
×
622

623

624
  if (record.find("1:", 0) == 0) { // IPv4
69✔
625
    uint32_t v4ip;
30✔
626

627
    ard.d_family = APL_FAMILY_IPV4;
30✔
628

629
    // Ensure that a mask is provided
630

631
    // Read IPv4 string into a Netmask object
632
    nm = Netmask(record.substr(2, record.length() - 2));
30✔
633
    ard.d_prefix = nm.getBits();
30✔
634

635
    if (nm.getNetwork().isIPv4() == 0)
30!
636
      throw MOADNSException("Asked to decode '"+element+"' as an APL v4 record");
×
637

638
    // Section 4.1 of RFC 3123 (don't send trailing "0" bytes)
639
    // Copy data; using array of bytes since we might end up truncating them in the packet
640
    v4ip = ntohl(nm.getNetwork().sin4.sin_addr.s_addr);
30✔
641
    memset(ard.d_ip.d_ip4, 0, sizeof(ard.d_ip.d_ip4));
30✔
642
    bytes  = 4; // Start by assuming we'll send 4 bytes
30✔
643
    done_trimming = false;
30✔
644
    for (int i=0; i<4; i++) {
150✔
645
      ard.d_ip.d_ip4[3-i] = (v4ip & 255);
120✔
646
      // Remove trailing "0" bytes from packet and update length
647
      if ((v4ip & 255) == 0 and !done_trimming) {
120!
648
        bytes--;
39✔
649
      } else {
81✔
650
        done_trimming = true;
81✔
651
      }
81✔
652
      v4ip = v4ip >> 8;
120✔
653
    }
120✔
654
    ard.d_afdlength = bytes;
30✔
655

656
  } else if (record.find("2:", 0) == 0) { // IPv6
39!
657
    ard.d_family = APL_FAMILY_IPV6;
39✔
658

659
    // Parse IPv6 string into a Netmask object
660
    nm = Netmask(record.substr(2, record.length() - 2));
39✔
661
    ard.d_prefix = nm.getBits();
39✔
662

663
    if (nm.getNetwork().isIPv6() == 0)
39!
664
      throw MOADNSException("Asked to decode '"+element+"' as an APL v6 record");
×
665

666
    // Section 4.2 of RFC 3123 (don't send trailing "0" bytes)
667
    // Remove trailing "0" bytes from packet and reduce length
668
    memset(ard.d_ip.d_ip6, 0, sizeof(ard.d_ip.d_ip6));
39✔
669
    bytes = 16; // Start by assuming we'll send 16 bytes
39✔
670
    done_trimming = false;
39✔
671
    for (int i=0; i<16; i++) {
663✔
672
      ard.d_ip.d_ip6[15-i] = nm.getNetwork().sin6.sin6_addr.s6_addr[15-i];
624✔
673
      if (nm.getNetwork().sin6.sin6_addr.s6_addr[15-i] == 0 and !done_trimming) {
624✔
674
        // trailing 0 byte, update length
675
        bytes--;
336✔
676
      } else {
336✔
677
        done_trimming = true;
288✔
678
      }
288✔
679
    }
624✔
680
    ard.d_afdlength = bytes;
39✔
681

682
  } else {
39✔
683
      throw MOADNSException("Asked to encode '"+element+"' as an IPv6 APL record but got unknown Address Family");
×
684
  }
×
685
  return ard;
69✔
686

687
}
69✔
688

689
// Parse backend record (0, 1 or more <apitem>)
690
std::shared_ptr<DNSRecordContent> APLRecordContent::make(const string& zone) {
66✔
691
  APLRDataElement ard;
66✔
692
  vector<string> elements;
66✔
693

694
  auto ret=std::make_shared<APLRecordContent>();
66✔
695

696
  boost::split(elements, zone, boost::is_any_of(" "));
66✔
697
  for (auto & element : elements) {
72✔
698
    if (!element.empty()) {
72✔
699
      ard = ret->parseAPLElement(element);
69✔
700
      ret->aplrdata.push_back(ard);
69✔
701
    }
69✔
702
  }
72✔
703
  return ret;
66✔
704
}
66✔
705

706

707
// DNSRecord to Packet conversion
708
void APLRecordContent::toPacket(DNSPacketWriter& pw) const {
66✔
709
  for (auto & ard : aplrdata) {
69✔
710
    pw.xfr16BitInt(ard.d_family);
69✔
711
    pw.xfr8BitInt(ard.d_prefix);
69✔
712
    pw.xfr8BitInt((ard.d_n << 7) + ard.d_afdlength);
69✔
713
    if (ard.d_family == APL_FAMILY_IPV4) {
69✔
714
      for (int i=0; i<ard.d_afdlength; i++) {
111✔
715
        pw.xfr8BitInt(ard.d_ip.d_ip4[i]);
81✔
716
      }
81✔
717
    } else if (ard.d_family == APL_FAMILY_IPV6) {
39!
718
      for (int i=0; i<ard.d_afdlength; i++) {
327✔
719
        pw.xfr8BitInt(ard.d_ip.d_ip6[i]);
288✔
720
      }
288✔
721
    }
39✔
722
  }
69✔
723
}
66✔
724

725
// Decode record into string
726
string APLRecordContent::getZoneRepresentation(bool /* noDot */) const {
132✔
727
  string s_n, s_family, output;
132✔
728
  ComboAddress ca;
132✔
729
  Netmask nm;
132✔
730

731
  output = "";
132✔
732

733
  for (std::vector<APLRDataElement>::const_iterator ard = aplrdata.begin() ; ard != aplrdata.end(); ++ard) {
270✔
734

735
    // Negation flag
736
    if (ard->d_n) {
138✔
737
      s_n = "!";
12✔
738
    } else {
126✔
739
      s_n = "";
126✔
740
    }
126✔
741

742
    if (ard->d_family == APL_FAMILY_IPV4) { // IPv4
138✔
743
      s_family = std::to_string(APL_FAMILY_IPV4);
60✔
744
      ca = ComboAddress();
60✔
745
      memcpy(&ca.sin4.sin_addr.s_addr, ard->d_ip.d_ip4, sizeof(ca.sin4.sin_addr.s_addr));
60✔
746
    } else if (ard->d_family == APL_FAMILY_IPV6) { // IPv6
78!
747
      s_family = std::to_string(APL_FAMILY_IPV6);
78✔
748
      ca = ComboAddress();
78✔
749
      ca.sin4.sin_family = AF_INET6;
78✔
750
      memset(&ca.sin6.sin6_addr.s6_addr, 0, sizeof(ca.sin6.sin6_addr.s6_addr));
78✔
751
      memcpy(&ca.sin6.sin6_addr.s6_addr, ard->d_ip.d_ip6, ard->d_afdlength);
78✔
752
    } else {
78✔
753
      throw MOADNSException("Asked to decode APL record but got unknown Address Family");
×
754
    }
×
755

756
    nm = Netmask(ca, ard->d_prefix);
138✔
757

758
    output += s_n + s_family + ":" + nm.toString();
138✔
759
    if (std::next(ard) != aplrdata.end())
138✔
760
      output += " ";
12✔
761
  }
138✔
762
  return output;
132✔
763
}
132✔
764

765
/* APL end */
766

767
/* SVCB start */
768
bool SVCBBaseRecordContent::autoHint(const SvcParam::SvcParamKey &key) const {
276✔
769
  auto p = getParamIt(key);
276✔
770
  if (p == d_params.end()) {
276!
771
    return false;
276✔
772
  }
276✔
773
  return p->getAutoHint();
×
774
}
276✔
775

776
void SVCBBaseRecordContent::setHints(const SvcParam::SvcParamKey &key, const std::vector<ComboAddress> &addresses) {
×
777
  auto p = getParamIt(key);
×
778
  if (p == d_params.end()) {
×
779
    return;
×
780
  }
×
781

782
  std::vector<ComboAddress> h;
×
783
  h.reserve(h.size() + addresses.size());
×
784
  h.insert(h.end(), addresses.begin(), addresses.end());
×
785

786
  try {
×
787
    auto newParam = SvcParam(key, std::move(h));
×
788
    d_params.erase(p);
×
789
    d_params.insert(std::move(newParam));
×
790
  } catch (...) {
×
791
    // XXX maybe we should SERVFAIL instead?
792
    return;
×
793
  }
×
794
}
×
795

796
void SVCBBaseRecordContent::removeParam(const SvcParam::SvcParamKey &key) {
×
797
  auto p = getParamIt(key);
×
798
  if (p == d_params.end()) {
×
799
    return;
×
800
  }
×
801
  d_params.erase(p);
×
802
}
×
803

804
bool SVCBBaseRecordContent::hasParams() const {
×
805
  return !d_params.empty();
×
806
}
×
807

808
bool SVCBBaseRecordContent::hasParam(const SvcParam::SvcParamKey &key) const {
4,200✔
809
  return getParamIt(key) != d_params.end();
4,200✔
810
}
4,200✔
811

812
SvcParam SVCBBaseRecordContent::getParam(const SvcParam::SvcParamKey &key) const {
×
813
  auto p = getParamIt(key);
×
814
  if (p == d_params.end()) {
×
815
    throw std::out_of_range("No param with key " + SvcParam::keyToString(key));
×
816
  }
×
817
  return *p;
×
818
}
×
819

820
set<SvcParam>::const_iterator SVCBBaseRecordContent::getParamIt(const SvcParam::SvcParamKey &key) const {
4,476✔
821
  return std::find(d_params.begin(), d_params.end(), key);
4,476✔
822
}
4,476✔
823

824
std::shared_ptr<SVCBBaseRecordContent> SVCBRecordContent::clone() const
825
{
138✔
826
  return std::shared_ptr<SVCBBaseRecordContent>(std::make_shared<SVCBRecordContent>(*this));
138✔
827
}
138✔
828

829
std::shared_ptr<SVCBBaseRecordContent> HTTPSRecordContent::clone() const
830
{
×
831
  return std::shared_ptr<SVCBBaseRecordContent>(std::make_shared<HTTPSRecordContent>(*this));
×
832
}
×
833

834
/* SVCB end */
835

836
boilerplate_conv(TKEY,
837
                 conv.xfrName(d_algo);
838
                 conv.xfr32BitInt(d_inception);
839
                 conv.xfr32BitInt(d_expiration);
840
                 conv.xfr16BitInt(d_mode);
841
                 conv.xfr16BitInt(d_error);
842
                 conv.xfr16BitInt(d_keysize);
843
                 if (d_keysize>0) conv.xfrBlobNoSpaces(d_key, d_keysize);
844
                 conv.xfr16BitInt(d_othersize);
845
                 if (d_othersize>0) conv.xfrBlobNoSpaces(d_other, d_othersize);
846
                 )
847
TKEYRecordContent::TKEYRecordContent() { d_othersize = 0; } // fix CID#1288932
148✔
848

849
boilerplate_conv(URI,
850
                 conv.xfr16BitInt(d_priority);
851
                 conv.xfr16BitInt(d_weight);
852
                 conv.xfrText(d_target, true, false);
853
                 )
854

855
boilerplate_conv(CAA,
856
                 conv.xfr8BitInt(d_flags);
857
                 conv.xfrUnquotedText(d_tag, true);
858
                 conv.xfrText(d_value, true, false); /* no lenField */
859
                )
860

861
static uint16_t makeTag(const std::string& data)
862
{
855,592✔
863
  const unsigned char* key=(const unsigned char*)data.c_str();
855,592✔
864
  unsigned int keysize=data.length();
855,592✔
865

866
  unsigned long ac;     /* assumed to be 32 bits or larger */
855,592✔
867
  unsigned int i;                /* loop index */
855,592✔
868

869
  for ( ac = 0, i = 0; i < keysize; ++i )
59,550,228✔
870
    ac += (i & 1) ? key[i] : key[i] << 8;
58,694,636✔
871
  ac += (ac >> 16) & 0xFFFF;
855,592✔
872
  return ac & 0xFFFF;
855,592✔
873
}
855,592✔
874

875
uint16_t DNSKEYRecordContent::getTag() const
876
{
856,137✔
877
  return makeTag(this->serialize(DNSName()));
856,137✔
878
}
856,137✔
879

880

881
/*
882
 * Fills `eo` by parsing the EDNS(0) OPT RR (RFC 6891)
883
 */
884
bool getEDNSOpts(const MOADNSParser& mdp, EDNSOpts* eo)
885
{
1,352,181✔
886
  eo->d_extFlags=0;
1,352,181✔
887
  if(mdp.d_header.arcount && !mdp.d_answers.empty()) {
1,352,181✔
888
    for(const MOADNSParser::answers_t::value_type& val :  mdp.d_answers) {
214,136✔
889
      if(val.d_place == DNSResourceRecord::ADDITIONAL && val.d_type == QType::OPT) {
214,136✔
890
        eo->d_packetsize=val.d_class;
131,857✔
891

892
        EDNS0Record stuff;
131,857✔
893
        uint32_t ttl=ntohl(val.d_ttl);
131,857✔
894
        static_assert(sizeof(EDNS0Record) == sizeof(uint32_t), "sizeof(EDNS0Record) must match sizeof(uint32_t)");
131,857✔
895
        memcpy(&stuff, &ttl, sizeof(stuff));
131,857✔
896

897
        eo->d_extRCode=stuff.extRCode;
131,857✔
898
        eo->d_version=stuff.version;
131,857✔
899
        eo->d_extFlags = ntohs(stuff.extFlags);
131,857✔
900
        auto orc = getRR<OPTRecordContent>(val);
131,857✔
901
        if(orc == nullptr)
131,857!
902
          return false;
×
903
        orc->getData(eo->d_options);
131,857✔
904
        return true;
131,857✔
905
      }
131,857✔
906
    }
214,136✔
907
  }
133,449✔
908
  return false;
1,220,324✔
909
}
1,352,181✔
910

911
static void reportBasicTypes(const ReportIsOnlyCallableByReportAllTypes& guard)
912
{
33,455✔
913
  ARecordContent::report(guard);
33,455✔
914
  AAAARecordContent::report(guard);
33,455✔
915
  NSRecordContent::report(guard);
33,455✔
916
  CNAMERecordContent::report(guard);
33,455✔
917
  MXRecordContent::report(guard);
33,455✔
918
  SOARecordContent::report(guard);
33,455✔
919
  SRVRecordContent::report(guard);
33,455✔
920
  PTRRecordContent::report(guard);
33,455✔
921
  DNSRecordContent::regist(QClass::CHAOS, QType::TXT, &TXTRecordContent::make, &TXTRecordContent::make, "TXT");
33,455✔
922
  TXTRecordContent::report(guard);
33,455✔
923
#ifdef HAVE_LUA_RECORDS
33,436✔
924
  LUARecordContent::report(guard);
33,436✔
925
#endif
33,436✔
926
  DNSRecordContent::regist(QClass::IN, QType::ANY, nullptr, nullptr, "ANY");
33,455✔
927
  DNSRecordContent::regist(QClass::IN, QType::AXFR, nullptr, nullptr, "AXFR");
33,455✔
928
  DNSRecordContent::regist(QClass::IN, QType::IXFR, nullptr, nullptr, "IXFR");
33,455✔
929
}
33,455✔
930

931
static void reportOtherTypes(const ReportIsOnlyCallableByReportAllTypes& guard)
932
{
33,455✔
933
   MBRecordContent::report(guard);
33,455✔
934
   MGRecordContent::report(guard);
33,455✔
935
   MRRecordContent::report(guard);
33,455✔
936
   AFSDBRecordContent::report(guard);
33,455✔
937
   DNAMERecordContent::report(guard);
33,455✔
938
#if !defined(RECURSOR)
33,436✔
939
   ALIASRecordContent::report(guard);
33,436✔
940
#endif
33,436✔
941
   SPFRecordContent::report(guard);
33,455✔
942
   NAPTRRecordContent::report(guard);
33,455✔
943
   KXRecordContent::report(guard);
33,455✔
944
   LOCRecordContent::report(guard);
33,455✔
945
   ENTRecordContent::report(guard);
33,455✔
946
   HINFORecordContent::report(guard);
33,455✔
947
   RPRecordContent::report(guard);
33,455✔
948
   KEYRecordContent::report(guard);
33,455✔
949
   DNSKEYRecordContent::report(guard);
33,455✔
950
   DHCIDRecordContent::report(guard);
33,455✔
951
   CDNSKEYRecordContent::report(guard);
33,455✔
952
   RKEYRecordContent::report(guard);
33,455✔
953
   RRSIGRecordContent::report(guard);
33,455✔
954
   DSRecordContent::report(guard);
33,455✔
955
   CDSRecordContent::report(guard);
33,455✔
956
   SSHFPRecordContent::report(guard);
33,455✔
957
   CERTRecordContent::report(guard);
33,455✔
958
   NSECRecordContent::report(guard);
33,455✔
959
   NSEC3RecordContent::report(guard);
33,455✔
960
   NSEC3PARAMRecordContent::report(guard);
33,455✔
961
   TLSARecordContent::report(guard);
33,455✔
962
   SMIMEARecordContent::report(guard);
33,455✔
963
   OPENPGPKEYRecordContent::report(guard);
33,455✔
964
   SVCBRecordContent::report(guard);
33,455✔
965
   HTTPSRecordContent::report(guard);
33,455✔
966
   DLVRecordContent::report(guard);
33,455✔
967
   DNSRecordContent::regist(QClass::ANY, QType::TSIG, &TSIGRecordContent::make, &TSIGRecordContent::make, "TSIG");
33,455✔
968
   DNSRecordContent::regist(QClass::ANY, QType::TKEY, &TKEYRecordContent::make, &TKEYRecordContent::make, "TKEY");
33,455✔
969
   //TSIGRecordContent::report(guard);
970
   OPTRecordContent::report(guard);
33,455✔
971
   EUI48RecordContent::report(guard);
33,455✔
972
   EUI64RecordContent::report(guard);
33,455✔
973
   MINFORecordContent::report(guard);
33,455✔
974
   URIRecordContent::report(guard);
33,455✔
975
   CAARecordContent::report(guard);
33,455✔
976
   APLRecordContent::report(guard);
33,455✔
977
   IPSECKEYRecordContent::report(guard);
33,455✔
978
   CSYNCRecordContent::report(guard);
33,455✔
979
   NIDRecordContent::report(guard);
33,455✔
980
   L32RecordContent::report(guard);
33,455✔
981
   L64RecordContent::report(guard);
33,455✔
982
   LPRecordContent::report(guard);
33,455✔
983
   ZONEMDRecordContent::report(guard);
33,455✔
984
}
33,455✔
985

986
struct ReportIsOnlyCallableByReportAllTypes
987
{
988
};
989

990
void reportAllTypes()
991
{
33,455✔
992
  ReportIsOnlyCallableByReportAllTypes guard;
33,455✔
993
  reportBasicTypes(guard);
33,455✔
994
  reportOtherTypes(guard);
33,455✔
995
  DNSRecordContent::lock();
33,455✔
996
}
33,455✔
997

998
ComboAddress getAddr(const DNSRecord& dr, uint16_t defport)
UNCOV
999
{
×
UNCOV
1000
  if (auto a = getRR<ARecordContent>(dr)) {
×
UNCOV
1001
    return a->getCA(defport);
×
UNCOV
1002
  }
×
1003
  else if (auto aaaa = getRR<AAAARecordContent>(dr)) {
×
1004
    return aaaa->getCA(defport);
×
1005
  }
×
1006
  throw std::invalid_argument("not an A or AAAA record");
×
UNCOV
1007
}
×
1008

1009
/**
1010
 * Check if the DNSNames that should be hostnames, are hostnames
1011
 */
1012
void checkHostnameCorrectness(const DNSResourceRecord& rr)
1013
{
1,660✔
1014
  if (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV) {
1,660!
1015
    DNSName toCheck;
1,025✔
1016
    if (rr.qtype.getCode() == QType::SRV) {
1,025!
1017
      vector<string> parts;
×
1018
      stringtok(parts, rr.getZoneRepresentation());
×
1019
      if (parts.size() == 4) toCheck = DNSName(parts[3]);
×
1020
    } else if (rr.qtype.getCode() == QType::MX) {
1,025✔
1021
      vector<string> parts;
24✔
1022
      stringtok(parts, rr.getZoneRepresentation());
24✔
1023
      if (parts.size() == 2) toCheck = DNSName(parts[1]);
24!
1024
    } else {
1,001✔
1025
      toCheck = DNSName(rr.content);
1,001✔
1026
    }
1,001✔
1027

1028
    if (toCheck.empty()) {
1,025!
1029
      throw std::runtime_error("unable to extract hostname from content");
×
1030
    }
×
1031
    else if ((rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV) && toCheck == g_rootdnsname) {
1,025!
1032
      // allow null MX/SRV
1033
    } else if(!toCheck.isHostname()) {
1,025✔
1034
      throw std::runtime_error(boost::str(boost::format("non-hostname content %s") % toCheck.toString()));
8✔
1035
    }
8✔
1036
  }
1,025✔
1037
}
1,660✔
1038

1039
#if 0
1040
static struct Reporter
1041
{
1042
  Reporter()
1043
  {
1044
    reportAllTypes();
1045
  }
1046
} reporter __attribute__((init_priority(65535)));
1047
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc