Coverage Report

Created: 2025-06-13 06:28

/src/pdns/pdns/dnsname.cc
Line
Count
Source (jump to first uncovered line)
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
#include "dnsname.hh"
23
#include <boost/format.hpp>
24
#include <string>
25
#include <cinttypes>
26
27
#include "dnswriter.hh"
28
#include "misc.hh"
29
30
#include <boost/functional/hash.hpp>
31
32
const DNSName g_rootdnsname("."), g_wildcarddnsname("*");
33
const ZoneName g_rootzonename(".");
34
35
/* raw storage
36
   in DNS label format, with trailing 0. W/o trailing 0, we are 'empty'
37
   www.powerdns.com = 3www8powerdns3com0
38
*/
39
40
std::ostream & operator<<(std::ostream &os, const DNSName& d)
41
0
{
42
0
  return os <<d.toLogString();
43
0
}
44
45
void DNSName::throwSafeRangeError(const std::string& msg, const char* buf, size_t length)
46
707
{
47
707
  std::string dots;
48
707
  if (length > s_maxDNSNameLength) {
49
496
    length = s_maxDNSNameLength;
50
496
    dots = "...";
51
496
  }
52
707
  std::string label;
53
707
  DNSName::appendEscapedLabel(label, buf, length);
54
707
  throw std::range_error(msg + label + dots);
55
707
}
56
57
DNSName::DNSName(const std::string_view sw)
58
260k
{
59
260k
  const char* p = sw.data();
60
260k
  size_t length = sw.length();
61
62
260k
  if(length == 0 || (length == 1 && p[0]=='.')) {
63
9.51k
    d_storage.assign(1, '\0');
64
250k
  } else {
65
250k
    if(!std::memchr(p, '\\', length)) {
66
219k
      unsigned char lenpos=0;
67
219k
      unsigned char labellen=0;
68
219k
      const char* const pbegin=p, *pend=p+length;
69
70
219k
      d_storage.reserve(length+1);
71
501k
      for(auto iter = pbegin; iter != pend; ) {
72
281k
        lenpos = d_storage.size();
73
281k
        if(*iter=='.')
74
89
          throwSafeRangeError("Found . in wrong position in DNSName: ", p, length);
75
281k
        d_storage.append(1, '\0');
76
281k
        labellen=0;
77
281k
        auto begiter=iter;
78
305M
        for(; iter != pend && *iter!='.'; ++iter) {
79
304M
          labellen++;
80
304M
        }
81
281k
        d_storage.append(begiter,iter);
82
281k
        if(iter != pend)
83
63.5k
          ++iter;
84
281k
        if(labellen > 63)
85
445
          throwSafeRangeError("label too long to append: ", p, length);
86
87
281k
        if(iter-pbegin > static_cast<ptrdiff_t>(s_maxDNSNameLength - 1)) // reserve two bytes, one for length and one for the root label
88
89
          throwSafeRangeError("name too long to append: ", p, length);
89
90
281k
        d_storage[lenpos]=labellen;
91
281k
      }
92
219k
      d_storage.append(1, '\0');
93
219k
    }
94
31.3k
    else {
95
31.3k
      d_storage=segmentDNSNameRaw(p, length);
96
31.3k
      if(d_storage.size() > s_maxDNSNameLength) {
97
78
        throwSafeRangeError("name too long: ", p, length);
98
78
      }
99
31.3k
    }
100
250k
  }
101
260k
}
102
103
DNSName::DNSName(const char* pos, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, uint16_t minOffset)
104
239k
{
105
239k
  if (offset >= len)
106
2.65k
    throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
107
108
236k
  if(!uncompress) {
109
3.11k
    if(const void * fnd=memchr(pos+offset, 0, len-offset)) {
110
2.99k
      d_storage.reserve(2+(const char*)fnd-(pos+offset));
111
2.99k
    }
112
3.11k
  }
113
114
236k
  packetParser(pos, len, offset, uncompress, qtype, qclass, consumed, 0, minOffset);
115
236k
}
116
117
static void checkLabelLength(uint8_t length)
118
104k
{
119
104k
  if (length == 0) {
120
0
    throw std::range_error("no such thing as an empty label to append");
121
0
  }
122
104k
  if (length > 63) {
123
0
    throw std::range_error("label too long to append");
124
0
  }
125
104k
}
126
127
// this parses a DNS name until a compression pointer is found
128
size_t DNSName::parsePacketUncompressed(const pdns::views::UnsignedCharView& view, size_t pos, bool uncompress)
129
253k
{
130
253k
  const size_t initialPos = pos;
131
253k
  size_t totalLength = 0;
132
253k
  unsigned char labellen = 0;
133
134
357k
  do {
135
357k
    labellen = view.at(pos);
136
357k
    ++pos;
137
138
357k
    if (labellen == 0) {
139
234k
      --pos;
140
234k
      break;
141
234k
    }
142
143
123k
    if (labellen >= 0xc0) {
144
18.1k
      if (!uncompress) {
145
181
        throw std::range_error("Found compressed label, instructed not to follow");
146
181
      }
147
17.9k
      --pos;
148
17.9k
      break;
149
18.1k
    }
150
151
104k
    if ((labellen & 0xc0) != 0) {
152
558
      throw std::range_error("Found an invalid label length in qname (only one of the first two bits is set)");
153
558
    }
154
104k
    checkLabelLength(labellen);
155
    // reserve one byte for the label length
156
104k
    if (totalLength + labellen > s_maxDNSNameLength - 1) {
157
21
      throw std::range_error("name too long to append");
158
21
    }
159
104k
    if (pos + labellen >= view.size()) {
160
459
      throw std::range_error("Found an invalid label length in qname");
161
459
    }
162
103k
    pos += labellen;
163
103k
    totalLength += 1 + labellen;
164
103k
  }
165
253k
  while (pos < view.size());
166
167
252k
  if (totalLength != 0) {
168
53.4k
    auto existingSize = d_storage.size();
169
53.4k
    if (existingSize > 0) {
170
      // remove the last label count, we are about to override it */
171
4.01k
      --existingSize;
172
4.01k
    }
173
53.4k
    d_storage.reserve(existingSize + totalLength + 1);
174
53.4k
    d_storage.resize(existingSize + totalLength);
175
53.4k
    memcpy(&d_storage.at(existingSize), &view.at(initialPos), totalLength);
176
53.4k
    d_storage.append(1, static_cast<char>(0));
177
53.4k
  }
178
252k
  return pos;
179
253k
}
180
181
// this should be the __only__ dns name parser in PowerDNS.
182
void DNSName::packetParser(const char* qpos, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset)
183
253k
{
184
253k
  if (offset >= len) {
185
0
    throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
186
0
  }
187
188
253k
  if (offset < static_cast<size_t>(minOffset)) {
189
0
    throw std::range_error("Trying to read before the beginning of the buffer ("+std::to_string(offset)+ " < "+std::to_string(minOffset)+")");
190
0
  }
191
253k
  unsigned char labellen{0};
192
193
253k
  pdns::views::UnsignedCharView view(qpos, len);
194
253k
  auto pos = parsePacketUncompressed(view, offset, uncompress);
195
196
253k
  labellen = view.at(pos);
197
253k
  pos++;
198
253k
  if (labellen != 0 && pos < view.size()) {
199
17.6k
    if (labellen < 0xc0) {
200
0
      abort();
201
0
    }
202
203
17.6k
    if (!uncompress) {
204
0
      throw std::range_error("Found compressed label, instructed not to follow");
205
0
    }
206
207
17.6k
    labellen &= (~0xc0);
208
17.6k
    size_t newpos = (labellen << 8) + view.at(pos);
209
210
17.6k
    if (newpos >= offset) {
211
310
      throw std::range_error("Found a forward reference during label decompression");
212
310
    }
213
214
17.2k
    if (newpos < minOffset) {
215
16
      throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos)+ " < "+std::to_string(minOffset)+")");
216
16
    }
217
218
17.2k
    if (++depth > 100) {
219
0
      throw std::range_error("Abort label decompression after 100 redirects");
220
0
    }
221
222
17.2k
    packetParser(qpos, len, newpos, true, nullptr, nullptr, nullptr, depth, minOffset);
223
224
17.2k
    pos++;
225
17.2k
  }
226
227
253k
  if (d_storage.empty()) {
228
185k
    d_storage.append(1, static_cast<char>(0)); // we just parsed the root
229
185k
  }
230
231
253k
  if (consumed != nullptr) {
232
233k
    *consumed = pos - offset;
233
233k
  }
234
235
253k
  if (qtype != nullptr) {
236
1.05k
    if (pos + 2 > view.size()) {
237
67
      throw std::range_error("Trying to read qtype past the end of the buffer ("+std::to_string(pos + 2)+ " > "+std::to_string(len)+")");
238
67
    }
239
987
    *qtype = view.at(pos)*256 + view.at(pos+1);
240
987
  }
241
242
253k
  pos += 2;
243
253k
  if (qclass != nullptr) {
244
987
    if (pos + 2 > view.size()) {
245
14
      throw std::range_error("Trying to read qclass past the end of the buffer ("+std::to_string(pos + 2)+ " > "+std::to_string(len)+")");
246
14
    }
247
973
    *qclass = view.at(pos)*256 + view.at(pos+1);
248
973
  }
249
253k
}
250
251
std::string DNSName::toString(const std::string& separator, const bool trailing) const
252
33.6k
{
253
33.6k
  std::string ret;
254
33.6k
  toString(ret, separator, trailing);
255
33.6k
  return ret;
256
33.6k
}
257
258
void DNSName::toString(std::string& output, const std::string& separator, const bool trailing) const
259
33.6k
{
260
33.6k
  if (empty()) {
261
148
    throw std::out_of_range("Attempt to print an unset DNSName");
262
148
  }
263
264
33.4k
  if (isRoot()) {
265
621
    output += (trailing ? separator : "");
266
621
    return;
267
621
  }
268
269
32.8k
  if (output.capacity() < (output.size() + d_storage.size())) {
270
3.51k
    output.reserve(output.size() + d_storage.size());
271
3.51k
  }
272
273
32.8k
  {
274
    // iterate over the raw labels
275
32.8k
    const char* p = d_storage.c_str();
276
32.8k
    const char* end = p + d_storage.size();
277
278
67.2k
    while (p < end && *p) {
279
34.4k
      appendEscapedLabel(output, p + 1, static_cast<size_t>(*p));
280
34.4k
      output += separator;
281
34.4k
      p += *p + 1;
282
34.4k
    }
283
32.8k
  }
284
285
32.8k
  if (!trailing) {
286
32.4k
    output.resize(output.size() - separator.size());
287
32.4k
  }
288
32.8k
}
289
290
std::string DNSName::toLogString() const
291
479
{
292
479
  if (empty()) {
293
88
    return "(empty)";
294
88
  }
295
296
391
  return toStringRootDot();
297
479
}
298
299
std::string DNSName::toDNSString() const
300
0
{
301
0
  if (empty()) {
302
0
    throw std::out_of_range("Attempt to DNSString an unset DNSName");
303
0
  }
304
305
0
  return std::string(d_storage.c_str(), d_storage.length());
306
0
}
307
308
std::string DNSName::toDNSStringLC() const
309
0
{
310
0
  auto result = toDNSString();
311
0
  toLowerInPlace(result); // label lengths are always < 'A'
312
0
  return result;
313
0
}
314
315
/**
316
 * Get the length of the DNSName on the wire
317
 *
318
 * @return the total wirelength of the DNSName
319
 */
320
1.42k
size_t DNSName::wirelength() const {
321
1.42k
  return d_storage.length();
322
1.42k
}
323
324
// Are WE part of parent
325
bool DNSName::isPartOf(const DNSName& parent) const
326
0
{
327
0
  if(parent.empty() || empty()) {
328
0
    throw std::out_of_range("empty DNSNames aren't part of anything");
329
0
  }
330
331
0
  if(parent.d_storage.size() > d_storage.size()) {
332
0
    return false;
333
0
  }
334
335
  // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then
336
0
  for(auto us=d_storage.cbegin(); us<d_storage.cend(); us+=*us+1) {
337
0
    auto distance = std::distance(us,d_storage.cend());
338
0
    if (distance < 0 || static_cast<size_t>(distance) < parent.d_storage.size()) {
339
0
      break;
340
0
    }
341
0
    if (static_cast<size_t>(distance) == parent.d_storage.size()) {
342
0
      auto p = parent.d_storage.cbegin();
343
0
      for(; us != d_storage.cend(); ++us, ++p) {
344
0
        if(dns_tolower(*p) != dns_tolower(*us))
345
0
          return false;
346
0
      }
347
0
      return true;
348
0
    }
349
0
    if (static_cast<uint8_t>(*us) > 63) {
350
0
      throw std::out_of_range("illegal label length in DNSName");
351
0
    }
352
0
  }
353
0
  return false;
354
0
}
355
356
DNSName DNSName::makeRelative(const DNSName& zone) const
357
0
{
358
0
  DNSName ret(*this);
359
0
  ret.makeUsRelative(zone);
360
0
  return ret;
361
0
}
362
363
void DNSName::makeUsRelative(const DNSName& zone)
364
0
{
365
0
  if (isPartOf(zone)) {
366
0
    d_storage.erase(d_storage.size()-zone.d_storage.size());
367
0
    d_storage.append(1, static_cast<char>(0)); // put back the trailing 0
368
0
  }
369
0
  else {
370
0
    clear();
371
0
  }
372
0
}
373
374
DNSName DNSName::getCommonLabels(const DNSName& other) const
375
0
{
376
0
  if (empty() || other.empty()) {
377
0
    return DNSName();
378
0
  }
379
380
0
  DNSName result(g_rootdnsname);
381
382
0
  const std::vector<std::string> ours = getRawLabels();
383
0
  const std::vector<std::string> others = other.getRawLabels();
384
385
0
  for (size_t pos = 0; ours.size() > pos && others.size() > pos; pos++) {
386
0
    const std::string& ourLabel = ours.at(ours.size() - pos - 1);
387
0
    const std::string& otherLabel = others.at(others.size() - pos - 1);
388
389
0
    if (!pdns_iequals(ourLabel, otherLabel)) {
390
0
      break;
391
0
    }
392
393
0
    result.prependRawLabel(ourLabel);
394
0
  }
395
396
0
  return result;
397
0
}
398
399
DNSName DNSName::labelReverse() const
400
0
{
401
0
  DNSName ret;
402
403
0
  if (isRoot()) {
404
0
    return *this; // we don't create the root automatically below
405
0
  }
406
407
0
  if (!empty()) {
408
0
    vector<string> l=getRawLabels();
409
0
    while(!l.empty()) {
410
0
      ret.appendRawLabel(l.back());
411
0
      l.pop_back();
412
0
    }
413
0
  }
414
0
  return ret;
415
0
}
416
417
void DNSName::appendRawLabel(const std::string& label)
418
0
{
419
0
  appendRawLabel(label.c_str(), label.length());
420
0
}
421
422
void DNSName::appendRawLabel(const char* start, unsigned int length)
423
0
{
424
0
  checkLabelLength(length);
425
426
  // reserve one byte for the label length
427
0
  if (d_storage.size() + length > s_maxDNSNameLength - 1) {
428
0
    throw std::range_error("name too long to append");
429
0
  }
430
431
0
  if (d_storage.empty()) {
432
0
    d_storage.reserve(1 + length + 1);
433
0
    d_storage.append(1, static_cast<char>(length));
434
0
  }
435
0
  else {
436
0
    d_storage.reserve(d_storage.size() + length + 1);
437
0
    *d_storage.rbegin() = static_cast<char>(length);
438
0
  }
439
0
  d_storage.append(start, length);
440
0
  d_storage.append(1, static_cast<char>(0));
441
0
}
442
443
void DNSName::prependRawLabel(const std::string& label)
444
0
{
445
0
  checkLabelLength(label.size());
446
447
  // reserve one byte for the label length
448
0
  if (d_storage.size() + label.size() > s_maxDNSNameLength - 1) {
449
0
    throw std::range_error("name too long to prepend");
450
0
  }
451
452
0
  if (d_storage.empty()) {
453
0
    d_storage.reserve(1 + label.size() + 1);
454
0
    d_storage.append(1, static_cast<char>(0));
455
0
  }
456
0
  else {
457
0
    d_storage.reserve(d_storage.size() + 1 + label.size());
458
0
  }
459
460
0
  string_t prep(1, static_cast<char>(label.size()));
461
0
  prep.append(label.c_str(), label.size());
462
0
  d_storage = prep+d_storage;
463
0
}
464
465
bool DNSName::slowCanonCompare(const DNSName& rhs) const
466
0
{
467
0
  auto ours=getRawLabels(), rhsLabels = rhs.getRawLabels();
468
0
  return std::lexicographical_compare(ours.rbegin(), ours.rend(), rhsLabels.rbegin(), rhsLabels.rend(), CIStringCompare());
469
0
}
470
471
vector<std::string> DNSName::getRawLabels() const
472
0
{
473
0
  vector<std::string> ret;
474
0
  ret.reserve(countLabels());
475
  // 3www4ds9a2nl0
476
0
  for(const unsigned char* p = (const unsigned char*) d_storage.c_str(); p < ((const unsigned char*) d_storage.c_str()) + d_storage.size() && *p; p+=*p+1) {
477
0
    ret.push_back({(const char*)p+1, (size_t)*p}); // XXX FIXME
478
0
  }
479
0
  return ret;
480
0
}
481
482
std::string DNSName::getRawLabel(unsigned int pos) const
483
0
{
484
0
  unsigned int currentPos = 0;
485
0
  for(const unsigned char* p = (const unsigned char*) d_storage.c_str(); p < ((const unsigned char*) d_storage.c_str()) + d_storage.size() && *p; p+=*p+1, currentPos++) {
486
0
    if (currentPos == pos) {
487
0
      return std::string((const char*)p+1, (size_t)*p);
488
0
    }
489
0
  }
490
491
0
  throw std::out_of_range("trying to get label at position "+std::to_string(pos)+" of a DNSName that only has "+std::to_string(currentPos)+" labels");
492
0
}
493
494
DNSName DNSName::getLastLabel() const
495
0
{
496
0
  DNSName ret(*this);
497
0
  ret.trimToLabels(1);
498
0
  return ret;
499
0
}
500
501
bool DNSName::chopOff()
502
0
{
503
0
  if (d_storage.empty() || d_storage[0]==0) {
504
0
    return false;
505
0
  }
506
0
  d_storage.erase(0, (unsigned int)d_storage[0]+1);
507
0
  return true;
508
0
}
509
510
bool DNSName::isWildcard() const
511
0
{
512
0
  if (d_storage.size() < 2) {
513
0
    return false;
514
0
  }
515
0
  auto p = d_storage.begin();
516
0
  return (*p == 0x01 && *++p == '*');
517
0
}
518
519
/*
520
 * Returns true if the DNSName is a valid RFC 1123 hostname, this function uses
521
 * a regex on the string, so it is probably best not used when speed is essential.
522
 */
523
bool DNSName::isHostname() const
524
0
{
525
0
  static Regex hostNameRegex = Regex("^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\\.)+$");
526
0
  return hostNameRegex.match(this->toString());
527
0
}
528
529
unsigned int DNSName::countLabels() const
530
0
{
531
0
  unsigned int count=0;
532
0
  const unsigned char* p = reinterpret_cast<const unsigned char*>(d_storage.c_str());
533
0
  const unsigned char* end = reinterpret_cast<const unsigned char*>(p + d_storage.size());
534
535
0
  while (p < end && *p) {
536
0
    ++count;
537
0
    p += *p + 1;
538
0
  }
539
0
  return count;
540
0
}
541
542
void DNSName::trimToLabels(unsigned int to)
543
0
{
544
0
  while(countLabels() > to && chopOff()) {
545
0
    ;
546
0
  }
547
0
}
548
549
550
size_t hash_value(DNSName const& d)
551
0
{
552
0
  return d.hash();
553
0
}
554
555
void DNSName::appendEscapedLabel(std::string& appendTo, const char* orig, size_t len)
556
35.1k
{
557
35.1k
  size_t pos = 0;
558
559
393k
  while (pos < len) {
560
358k
    auto p = static_cast<uint8_t>(orig[pos]);
561
358k
    if (p=='.') {
562
714
      appendTo+="\\.";
563
714
    }
564
358k
    else if (p=='\\') {
565
739
      appendTo+="\\\\";
566
739
    }
567
357k
    else if (p > 0x20 && p < 0x7f) {
568
297k
      appendTo.append(1, static_cast<char>(p));
569
297k
    }
570
59.6k
    else {
571
59.6k
      char buf[] = "000";
572
59.6k
      auto got = snprintf(buf, sizeof(buf), "%03" PRIu8, p);
573
59.6k
      if (got < 0 || static_cast<size_t>(got) >= sizeof(buf)) {
574
0
        throw std::runtime_error("Error, snprintf returned " + std::to_string(got) + " while escaping label " + std::string(orig, len));
575
0
      }
576
59.6k
      appendTo.append(1, '\\');
577
59.6k
      appendTo += buf;
578
59.6k
    }
579
358k
    ++pos;
580
358k
  }
581
35.1k
}
582
583
bool DNSName::has8bitBytes() const
584
0
{
585
0
  const auto& s = d_storage;
586
0
  string::size_type pos = 0;
587
0
  uint8_t length = s.at(pos);
588
0
  while (length > 0) {
589
0
    for (size_t idx = 0; idx < length; idx++) {
590
0
      ++pos;
591
0
      char c = s.at(pos);
592
0
      if (!((c >= 'a' && c <= 'z') ||
593
0
            (c >= 'A' && c <= 'Z') ||
594
0
            (c >= '0' && c <= '9') ||
595
0
            c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':')) {
596
0
        return true;
597
0
      }
598
0
    }
599
0
    ++pos;
600
0
    length = s.at(pos);
601
0
  }
602
603
0
  return false;
604
0
}
605
606
// clang-format off
607
const unsigned char dns_toupper_table[256] = {
608
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
609
  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
610
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
611
  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
612
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
613
  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
614
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
615
  0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
616
  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
617
  0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
618
  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
619
  0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
620
  0x60, 'A',  'B',  'C',  'D',  'E',  'F',  'G',
621
  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
622
  'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
623
  'X',  'Y',  'Z',  0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
624
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
625
  0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
626
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
627
  0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
628
  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
629
  0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
630
  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
631
  0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
632
  0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
633
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
634
  0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
635
  0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
636
  0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
637
  0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
638
  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
639
  0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
640
};
641
642
const unsigned char dns_tolower_table[256] = {
643
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
644
  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
645
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
646
  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
647
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
648
  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
649
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
650
  0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
651
  0x40, 'a',  'b',  'c',  'd',  'e',  'f',  'g',
652
  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
653
  'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
654
  'x',  'y',  'z',  0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
655
  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
656
  0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
657
  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
658
  0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
659
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
660
  0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
661
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
662
  0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
663
  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
664
  0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
665
  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
666
  0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
667
  0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
668
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
669
  0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
670
  0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
671
  0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
672
  0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
673
  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
674
  0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
675
};
676
677
0
DNSName::RawLabelsVisitor::RawLabelsVisitor(const DNSName::string_t& storage): d_storage(storage)
678
0
{
679
0
  size_t position = 0;
680
0
  while (position < storage.size()) {
681
0
    auto labelLength = static_cast<uint8_t>(storage.at(position));
682
0
    if (labelLength == 0) {
683
0
      break;
684
0
    }
685
0
    d_labelPositions.at(d_position) = position;
686
0
    d_position++;
687
0
    position += labelLength + 1;
688
0
  }
689
0
}
690
691
DNSName::RawLabelsVisitor DNSName::getRawLabelsVisitor() const
692
0
{
693
0
  return DNSName::RawLabelsVisitor(getStorage());
694
0
}
695
696
std::string_view DNSName::RawLabelsVisitor::front() const
697
0
{
698
0
  if (d_position == 0) {
699
0
    throw std::out_of_range("trying to access the front of an empty DNSName::RawLabelsVisitor");
700
0
  }
701
0
  uint8_t length = d_storage.at(0);
702
0
  if (length == 0) {
703
0
    return std::string_view();
704
0
  }
705
0
  return std::string_view(&d_storage.at(1), length);
706
0
}
707
708
std::string_view DNSName::RawLabelsVisitor::back() const
709
0
{
710
0
  if (d_position == 0) {
711
0
    throw std::out_of_range("trying to access the back of an empty DNSName::RawLabelsVisitor");
712
0
  }
713
0
  size_t offset = d_labelPositions.at(d_position-1);
714
0
  uint8_t length = d_storage.at(offset);
715
0
  if (length == 0) {
716
0
    return std::string_view();
717
0
  }
718
0
  return std::string_view(&d_storage.at(offset + 1), length);
719
0
}
720
721
bool DNSName::RawLabelsVisitor::pop_back()
722
0
{
723
0
  if (d_position > 0) {
724
0
    d_position--;
725
0
    return true;
726
0
  }
727
0
  return false;
728
0
}
729
730
bool DNSName::RawLabelsVisitor::empty() const
731
0
{
732
0
  return d_position == 0;
733
0
}
734
735
#if defined(PDNS_AUTH) // [
736
std::ostream & operator<<(std::ostream &ostr, const ZoneName& zone)
737
0
{
738
0
  return ostr << zone.toLogString();
739
0
}
740
741
size_t hash_value(ZoneName const& zone)
742
0
{
743
0
  return zone.hash();
744
0
}
745
746
// Sugar while ZoneName::operator DNSName are made explicit. These can't be
747
// made inline in class DNSName due to chicken-and-egg declaration order
748
// between DNSName and ZoneName.
749
bool DNSName::isPartOf(const ZoneName& rhs) const
750
0
{
751
0
  return isPartOf(rhs.operator const DNSName&());
752
0
}
753
DNSName DNSName::makeRelative(const ZoneName& zone) const
754
0
{
755
0
  return makeRelative(zone.operator const DNSName&());
756
0
}
757
void DNSName::makeUsRelative(const ZoneName& zone)
758
0
{
759
0
  makeUsRelative(zone.operator const DNSName&());
760
0
}
761
762
std::string_view::size_type ZoneName::findVariantSeparator(std::string_view name)
763
49.2k
{
764
49.2k
  std::string_view::size_type pos{0};
765
766
  // Try to be as fast as possible in the non-variant case and exit
767
  // quickly if we don't find two dots in a row.
768
50.4k
  while ((pos = name.find('.', pos)) != std::string_view::npos) {
769
3.18k
    ++pos;
770
3.18k
    if (pos >= name.length()) { // trailing single dot
771
642
      return std::string_view::npos;
772
642
    }
773
2.54k
    if (name.at(pos) == '.') {
774
      // We have found two dots in a row, but the first dot might have been
775
      // escaped. So we now need to count how many \ characters we can find a
776
      // row before it; if their number is odd, the first dot is escaped and
777
      // we need to keep searching.
778
1.75k
      size_t slashes{0};
779
2.28k
      while (pos >= 2 + slashes && name.at(pos - 2 - slashes) == '\\') {
780
529
        ++slashes;
781
529
      }
782
1.75k
      if ((slashes % 2) == 0) {
783
1.37k
  break;
784
1.37k
      }
785
1.75k
    }
786
2.54k
  }
787
48.6k
  return pos;
788
49.2k
}
789
790
ZoneName::ZoneName(std::string_view name)
791
49.2k
{
792
49.2k
  if (auto sep = findVariantSeparator(name); sep != std::string_view::npos) {
793
1.37k
    setVariant(name.substr(sep + 1)); // ignore leading dot in variant name
794
1.37k
    name = name.substr(0, sep); // keep trailing dot in zone name
795
1.37k
  }
796
49.2k
  d_name = DNSName(name);
797
49.2k
}
798
799
ZoneName::ZoneName(std::string_view name, std::string_view::size_type sep)
800
0
{
801
0
  if (sep != std::string_view::npos) {
802
0
    setVariant(name.substr(sep + 1)); // ignore leading dot in variant name
803
0
    name = name.substr(0, sep); // keep trailing dot in zone name
804
0
  }
805
0
  d_name = DNSName(name);
806
0
}
807
808
void ZoneName::setVariant(std::string_view variant)
809
1.37k
{
810
1.37k
  if (variant.find_first_not_of("abcdefghijklmnopqrstuvwxyz0123456789_-") != std::string_view::npos) {
811
114
    throw std::out_of_range("invalid character in variant name '" + std::string{variant} + "'");
812
114
  }
813
1.25k
  d_variant = variant;
814
1.25k
}
815
816
std::string ZoneName::toLogString() const
817
0
{
818
0
  std::string ret = d_name.toLogString();
819
0
  if (!d_variant.empty()) {
820
    // Because toLogString() above uses toStringRootDot(), we do not want to
821
    // output one too many dots if this is a root-with-variant.
822
0
    ret.push_back('.');
823
0
    if (!d_name.isRoot()) {
824
0
      ret.push_back('.');
825
0
    }
826
0
    ret += d_variant;
827
0
  }
828
0
  return ret;
829
0
}
830
831
std::string ZoneName::toStringFull(const std::string& separator, const bool trailing) const
832
0
{
833
0
  std::string ret = d_name.toString(separator, trailing);
834
0
  if (!d_variant.empty()) {
835
0
    if (!trailing) {
836
0
      ret.push_back('.');
837
0
    }
838
    // toString of root emits "" if no trailing, "." if trailing
839
0
    if (d_name.isRoot()) {
840
0
      ret.push_back('.');
841
0
    }
842
0
    ret += d_variant;
843
0
  }
844
0
  return ret;
845
0
}
846
847
size_t ZoneName::hash(size_t init) const
848
0
{
849
0
  if (!d_variant.empty()) {
850
0
    init = burtleCI(reinterpret_cast<const unsigned char *>(d_variant.data()), d_variant.length(), init); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): can't static_cast because of sign difference
851
0
  }
852
853
0
  return d_name.hash(init);
854
0
}
855
856
bool ZoneName::operator<(const ZoneName& rhs)  const
857
0
{
858
  // Order by DNSName first, by variant second.
859
  // Unfortunately we can't use std::lexicographical_compare_three_way() yet
860
  // as this would require C++20.
861
0
  const auto *iter1 = d_name.getStorage().cbegin();
862
0
  const auto *last1 = d_name.getStorage().cend();
863
0
  const auto *iter2 = rhs.d_name.getStorage().cbegin();
864
0
  const auto *last2 = rhs.d_name.getStorage().cend();
865
0
  while (iter1 != last1 && iter2 != last2) {
866
0
    auto char1 = dns_tolower(*iter1);
867
0
    auto char2 = dns_tolower(*iter2);
868
0
    if (char1 < char2) {
869
0
      return true;
870
0
    }
871
0
    if (char1 > char2) {
872
0
      return false;
873
0
    }
874
0
    ++iter1; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
875
0
    ++iter2; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
876
0
  }
877
0
  if (iter1 == last1) {
878
0
    if (iter2 != last2) {
879
0
      return true; // our DNSName is shorter (subset) than the other
880
0
    }
881
0
  }
882
0
  else {
883
0
    return false; // our DNSName is longer (superset) than the other
884
0
  }
885
  // At this point, both DNSName compare equal, we have to compare
886
  // variants (which are case-sensitive).
887
0
  return d_variant < rhs.d_variant;
888
0
}
889
890
bool ZoneName::canonCompare(const ZoneName& rhs) const
891
0
{
892
  // Similarly to operator< above, this compares DNSName first, variant
893
  // second. Unfortunately because DNSName::canonCompare() is complicated,
894
  // it can't pragmatically be duplicated here, hence the two calls.
895
  // TODO: change DNSName::canonCompare() to return a three-state value
896
  // (lt, eq, ge) in order to be able to call it only once.
897
0
  if (!d_name.canonCompare(rhs.d_name)) {
898
0
    return false;
899
0
  }
900
0
  if (!rhs.d_name.canonCompare(d_name)) {
901
0
    return true;
902
0
  }
903
  // Both DNSName compare equal.
904
0
  return d_variant < rhs.d_variant;
905
0
}
906
#endif // ]