Coverage Report

Created: 2024-04-25 06:25

/src/pdns/pdns/dnsdistdist/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
34
/* raw storage
35
   in DNS label format, with trailing 0. W/o trailing 0, we are 'empty'
36
   www.powerdns.com = 3www8powerdns3com0
37
*/
38
39
std::ostream & operator<<(std::ostream &os, const DNSName& d)
40
0
{
41
0
  return os <<d.toLogString();
42
0
}
43
44
void DNSName::throwSafeRangeError(const std::string& msg, const char* buf, size_t length)
45
0
{
46
0
  std::string dots;
47
0
  if (length > s_maxDNSNameLength) {
48
0
    length = s_maxDNSNameLength;
49
0
    dots = "...";
50
0
  }
51
0
  std::string label;
52
0
  DNSName::appendEscapedLabel(label, buf, length);
53
0
  throw std::range_error(msg + label + dots);
54
0
}
55
56
DNSName::DNSName(const std::string_view sw)
57
4
{
58
4
  const char* p = sw.data();
59
4
  size_t length = sw.length();
60
61
4
  if(length == 0 || (length == 1 && p[0]=='.')) {
62
2
    d_storage.assign(1, '\0');
63
2
  } else {
64
2
    if(!std::memchr(p, '\\', length)) {
65
2
      unsigned char lenpos=0;
66
2
      unsigned char labellen=0;
67
2
      const char* const pbegin=p, *pend=p+length;
68
69
2
      d_storage.reserve(length+1);
70
4
      for(auto iter = pbegin; iter != pend; ) {
71
2
        lenpos = d_storage.size();
72
2
        if(*iter=='.')
73
0
          throwSafeRangeError("Found . in wrong position in DNSName: ", p, length);
74
2
        d_storage.append(1, '\0');
75
2
        labellen=0;
76
2
        auto begiter=iter;
77
4
        for(; iter != pend && *iter!='.'; ++iter) {
78
2
          labellen++;
79
2
        }
80
2
        d_storage.append(begiter,iter);
81
2
        if(iter != pend)
82
0
          ++iter;
83
2
        if(labellen > 63)
84
0
          throwSafeRangeError("label too long to append: ", p, length);
85
86
2
        if(iter-pbegin > static_cast<ptrdiff_t>(s_maxDNSNameLength - 1)) // reserve two bytes, one for length and one for the root label
87
0
          throwSafeRangeError("name too long to append: ", p, length);
88
89
2
        d_storage[lenpos]=labellen;
90
2
      }
91
2
      d_storage.append(1, '\0');
92
2
    }
93
0
    else {
94
0
      d_storage=segmentDNSNameRaw(p, length);
95
0
      if(d_storage.size() > s_maxDNSNameLength) {
96
0
        throwSafeRangeError("name too long: ", p, length);
97
0
      }
98
0
    }
99
2
  }
100
4
}
101
102
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)
103
1.21k
{
104
1.21k
  if (offset >= len)
105
8
    throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
106
107
1.20k
  if(!uncompress) {
108
1.20k
    if(const void * fnd=memchr(pos+offset, 0, len-offset)) {
109
1.16k
      d_storage.reserve(2+(const char*)fnd-(pos+offset));
110
1.16k
    }
111
1.20k
  }
112
113
1.20k
  packetParser(pos, len, offset, uncompress, qtype, qclass, consumed, 0, minOffset);
114
1.20k
}
115
116
static void checkLabelLength(uint8_t length)
117
4.55k
{
118
4.55k
  if (length == 0) {
119
0
    throw std::range_error("no such thing as an empty label to append");
120
0
  }
121
4.55k
  if (length > 63) {
122
0
    throw std::range_error("label too long to append");
123
0
  }
124
4.55k
}
125
126
// this parses a DNS name until a compression pointer is found
127
size_t DNSName::parsePacketUncompressed(const pdns::views::UnsignedCharView& view, size_t pos, bool uncompress)
128
1.20k
{
129
1.20k
  const size_t initialPos = pos;
130
1.20k
  size_t totalLength = 0;
131
1.20k
  unsigned char labellen = 0;
132
133
5.70k
  do {
134
5.70k
    labellen = view.at(pos);
135
5.70k
    ++pos;
136
137
5.70k
    if (labellen == 0) {
138
1.06k
      --pos;
139
1.06k
      break;
140
1.06k
    }
141
142
4.64k
    if (labellen >= 0xc0) {
143
46
      if (!uncompress) {
144
46
        throw std::range_error("Found compressed label, instructed not to follow");
145
46
      }
146
0
      --pos;
147
0
      break;
148
46
    }
149
150
4.59k
    if ((labellen & 0xc0) != 0) {
151
42
      throw std::range_error("Found an invalid label length in qname (only one of the first two bits is set)");
152
42
    }
153
4.55k
    checkLabelLength(labellen);
154
    // reserve one byte for the label length
155
4.55k
    if (totalLength + labellen > s_maxDNSNameLength - 1) {
156
5
      throw std::range_error("name too long to append");
157
5
    }
158
4.54k
    if (pos + labellen >= view.size()) {
159
42
      throw std::range_error("Found an invalid label length in qname");
160
42
    }
161
4.50k
    pos += labellen;
162
4.50k
    totalLength += 1 + labellen;
163
4.50k
  }
164
4.50k
  while (pos < view.size());
165
166
1.06k
  if (totalLength != 0) {
167
290
    auto existingSize = d_storage.size();
168
290
    if (existingSize > 0) {
169
      // remove the last label count, we are about to override it */
170
0
      --existingSize;
171
0
    }
172
290
    d_storage.reserve(existingSize + totalLength + 1);
173
290
    d_storage.resize(existingSize + totalLength);
174
290
    memcpy(&d_storage.at(existingSize), &view.at(initialPos), totalLength);
175
290
    d_storage.append(1, static_cast<char>(0));
176
290
  }
177
1.06k
  return pos;
178
1.20k
}
179
180
// this should be the __only__ dns name parser in PowerDNS.
181
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)
182
1.20k
{
183
1.20k
  if (offset >= len) {
184
0
    throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
185
0
  }
186
187
1.20k
  if (offset < static_cast<size_t>(minOffset)) {
188
0
    throw std::range_error("Trying to read before the beginning of the buffer ("+std::to_string(offset)+ " < "+std::to_string(minOffset)+")");
189
0
  }
190
1.20k
  unsigned char labellen{0};
191
192
1.20k
  pdns::views::UnsignedCharView view(qpos, len);
193
1.20k
  auto pos = parsePacketUncompressed(view, offset, uncompress);
194
195
1.20k
  labellen = view.at(pos);
196
1.20k
  pos++;
197
1.20k
  if (labellen != 0 && pos < view.size()) {
198
0
    if (labellen < 0xc0) {
199
0
      abort();
200
0
    }
201
202
0
    if (!uncompress) {
203
0
      throw std::range_error("Found compressed label, instructed not to follow");
204
0
    }
205
206
0
    labellen &= (~0xc0);
207
0
    size_t newpos = (labellen << 8) + view.at(pos);
208
209
0
    if (newpos >= offset) {
210
0
      throw std::range_error("Found a forward reference during label decompression");
211
0
    }
212
213
0
    if (newpos < minOffset) {
214
0
      throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos)+ " < "+std::to_string(minOffset)+")");
215
0
    }
216
217
0
    if (++depth > 100) {
218
0
      throw std::range_error("Abort label decompression after 100 redirects");
219
0
    }
220
221
0
    packetParser(qpos, len, newpos, true, nullptr, nullptr, nullptr, depth, minOffset);
222
223
0
    pos++;
224
0
  }
225
226
1.20k
  if (d_storage.empty()) {
227
777
    d_storage.append(1, static_cast<char>(0)); // we just parsed the root
228
777
  }
229
230
1.20k
  if (consumed != nullptr) {
231
1.06k
    *consumed = pos - offset;
232
1.06k
  }
233
234
1.20k
  if (qtype != nullptr) {
235
1.06k
    if (pos + 2 > view.size()) {
236
65
      throw std::range_error("Trying to read qtype past the end of the buffer ("+std::to_string(pos + 2)+ " > "+std::to_string(len)+")");
237
65
    }
238
1.00k
    *qtype = view.at(pos)*256 + view.at(pos+1);
239
1.00k
  }
240
241
1.13k
  pos += 2;
242
1.13k
  if (qclass != nullptr) {
243
1.00k
    if (pos + 2 > view.size()) {
244
12
      throw std::range_error("Trying to read qclass past the end of the buffer ("+std::to_string(pos + 2)+ " > "+std::to_string(len)+")");
245
12
    }
246
990
    *qclass = view.at(pos)*256 + view.at(pos+1);
247
990
  }
248
1.13k
}
249
250
std::string DNSName::toString(const std::string& separator, const bool trailing) const
251
0
{
252
0
  std::string ret;
253
0
  toString(ret, separator, trailing);
254
0
  return ret;
255
0
}
256
257
void DNSName::toString(std::string& output, const std::string& separator, const bool trailing) const
258
0
{
259
0
  if (empty()) {
260
0
    throw std::out_of_range("Attempt to print an unset dnsname");
261
0
  }
262
263
0
  if (isRoot()) {
264
0
    output += (trailing ? separator : "");
265
0
    return;
266
0
  }
267
268
0
  if (output.capacity() < (output.size() + d_storage.size())) {
269
0
    output.reserve(output.size() + d_storage.size());
270
0
  }
271
272
0
  {
273
    // iterate over the raw labels
274
0
    const char* p = d_storage.c_str();
275
0
    const char* end = p + d_storage.size();
276
277
0
    while (p < end && *p) {
278
0
      appendEscapedLabel(output, p + 1, static_cast<size_t>(*p));
279
0
      output += separator;
280
0
      p += *p + 1;
281
0
    }
282
0
  }
283
284
0
  if (!trailing) {
285
0
    output.resize(output.size() - separator.size());
286
0
  }
287
0
}
288
289
std::string DNSName::toLogString() const
290
0
{
291
0
  if (empty()) {
292
0
    return "(empty)";
293
0
  }
294
295
0
  return toStringRootDot();
296
0
}
297
298
std::string DNSName::toDNSString() const
299
0
{
300
0
  if (empty()) {
301
0
    throw std::out_of_range("Attempt to DNSString an unset dnsname");
302
0
  }
303
304
0
  return std::string(d_storage.c_str(), d_storage.length());
305
0
}
306
307
std::string DNSName::toDNSStringLC() const
308
0
{
309
0
  auto result = toDNSString();
310
0
  toLowerInPlace(result); // label lengths are always < 'A'
311
0
  return result;
312
0
}
313
314
/**
315
 * Get the length of the DNSName on the wire
316
 *
317
 * @return the total wirelength of the DNSName
318
 */
319
0
size_t DNSName::wirelength() const {
320
0
  return d_storage.length();
321
0
}
322
323
// Are WE part of parent
324
bool DNSName::isPartOf(const DNSName& parent) const
325
0
{
326
0
  if(parent.empty() || empty()) {
327
0
    throw std::out_of_range("empty dnsnames aren't part of anything");
328
0
  }
329
330
0
  if(parent.d_storage.size() > d_storage.size()) {
331
0
    return false;
332
0
  }
333
334
  // this is slightly complicated since we can't start from the end, since we can't see where a label begins/ends then
335
0
  for(auto us=d_storage.cbegin(); us<d_storage.cend(); us+=*us+1) {
336
0
    auto distance = std::distance(us,d_storage.cend());
337
0
    if (distance < 0 || static_cast<size_t>(distance) < parent.d_storage.size()) {
338
0
      break;
339
0
    }
340
0
    if (static_cast<size_t>(distance) == parent.d_storage.size()) {
341
0
      auto p = parent.d_storage.cbegin();
342
0
      for(; us != d_storage.cend(); ++us, ++p) {
343
0
        if(dns_tolower(*p) != dns_tolower(*us))
344
0
          return false;
345
0
      }
346
0
      return true;
347
0
    }
348
0
    if (static_cast<uint8_t>(*us) > 63) {
349
0
      throw std::out_of_range("illegal label length in dnsname");
350
0
    }
351
0
  }
352
0
  return false;
353
0
}
354
355
DNSName DNSName::makeRelative(const DNSName& zone) const
356
0
{
357
0
  DNSName ret(*this);
358
0
  ret.makeUsRelative(zone);
359
0
  return ret;
360
0
}
361
362
void DNSName::makeUsRelative(const DNSName& zone)
363
0
{
364
0
  if (isPartOf(zone)) {
365
0
    d_storage.erase(d_storage.size()-zone.d_storage.size());
366
0
    d_storage.append(1, static_cast<char>(0)); // put back the trailing 0
367
0
  }
368
0
  else {
369
0
    clear();
370
0
  }
371
0
}
372
373
DNSName DNSName::getCommonLabels(const DNSName& other) const
374
0
{
375
0
  if (empty() || other.empty()) {
376
0
    return DNSName();
377
0
  }
378
379
0
  DNSName result(g_rootdnsname);
380
381
0
  const std::vector<std::string> ours = getRawLabels();
382
0
  const std::vector<std::string> others = other.getRawLabels();
383
384
0
  for (size_t pos = 0; ours.size() > pos && others.size() > pos; pos++) {
385
0
    const std::string& ourLabel = ours.at(ours.size() - pos - 1);
386
0
    const std::string& otherLabel = others.at(others.size() - pos - 1);
387
388
0
    if (!pdns_iequals(ourLabel, otherLabel)) {
389
0
      break;
390
0
    }
391
392
0
    result.prependRawLabel(ourLabel);
393
0
  }
394
395
0
  return result;
396
0
}
397
398
DNSName DNSName::labelReverse() const
399
0
{
400
0
  DNSName ret;
401
402
0
  if (isRoot()) {
403
0
    return *this; // we don't create the root automatically below
404
0
  }
405
406
0
  if (!empty()) {
407
0
    vector<string> l=getRawLabels();
408
0
    while(!l.empty()) {
409
0
      ret.appendRawLabel(l.back());
410
0
      l.pop_back();
411
0
    }
412
0
  }
413
0
  return ret;
414
0
}
415
416
void DNSName::appendRawLabel(const std::string& label)
417
0
{
418
0
  appendRawLabel(label.c_str(), label.length());
419
0
}
420
421
void DNSName::appendRawLabel(const char* start, unsigned int length)
422
0
{
423
0
  checkLabelLength(length);
424
425
  // reserve one byte for the label length
426
0
  if (d_storage.size() + length > s_maxDNSNameLength - 1) {
427
0
    throw std::range_error("name too long to append");
428
0
  }
429
430
0
  if (d_storage.empty()) {
431
0
    d_storage.reserve(1 + length + 1);
432
0
    d_storage.append(1, static_cast<char>(length));
433
0
  }
434
0
  else {
435
0
    d_storage.reserve(d_storage.size() + length + 1);
436
0
    *d_storage.rbegin() = static_cast<char>(length);
437
0
  }
438
0
  d_storage.append(start, length);
439
0
  d_storage.append(1, static_cast<char>(0));
440
0
}
441
442
void DNSName::prependRawLabel(const std::string& label)
443
0
{
444
0
  checkLabelLength(label.size());
445
446
  // reserve one byte for the label length
447
0
  if (d_storage.size() + label.size() > s_maxDNSNameLength - 1) {
448
0
    throw std::range_error("name too long to prepend");
449
0
  }
450
451
0
  if (d_storage.empty()) {
452
0
    d_storage.reserve(1 + label.size() + 1);
453
0
    d_storage.append(1, static_cast<char>(0));
454
0
  }
455
0
  else {
456
0
    d_storage.reserve(d_storage.size() + 1 + label.size());
457
0
  }
458
459
0
  string_t prep(1, static_cast<char>(label.size()));
460
0
  prep.append(label.c_str(), label.size());
461
0
  d_storage = prep+d_storage;
462
0
}
463
464
bool DNSName::slowCanonCompare(const DNSName& rhs) const
465
0
{
466
0
  auto ours=getRawLabels(), rhsLabels = rhs.getRawLabels();
467
0
  return std::lexicographical_compare(ours.rbegin(), ours.rend(), rhsLabels.rbegin(), rhsLabels.rend(), CIStringCompare());
468
0
}
469
470
vector<std::string> DNSName::getRawLabels() const
471
0
{
472
0
  vector<std::string> ret;
473
0
  ret.reserve(countLabels());
474
  // 3www4ds9a2nl0
475
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) {
476
0
    ret.push_back({(const char*)p+1, (size_t)*p}); // XXX FIXME
477
0
  }
478
0
  return ret;
479
0
}
480
481
std::string DNSName::getRawLabel(unsigned int pos) const
482
0
{
483
0
  unsigned int currentPos = 0;
484
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++) {
485
0
    if (currentPos == pos) {
486
0
      return std::string((const char*)p+1, (size_t)*p);
487
0
    }
488
0
  }
489
490
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");
491
0
}
492
493
DNSName DNSName::getLastLabel() const
494
0
{
495
0
  DNSName ret(*this);
496
0
  ret.trimToLabels(1);
497
0
  return ret;
498
0
}
499
500
bool DNSName::chopOff()
501
0
{
502
0
  if (d_storage.empty() || d_storage[0]==0) {
503
0
    return false;
504
0
  }
505
0
  d_storage.erase(0, (unsigned int)d_storage[0]+1);
506
0
  return true;
507
0
}
508
509
bool DNSName::isWildcard() const
510
0
{
511
0
  if (d_storage.size() < 2) {
512
0
    return false;
513
0
  }
514
0
  auto p = d_storage.begin();
515
0
  return (*p == 0x01 && *++p == '*');
516
0
}
517
518
/*
519
 * Returns true if the DNSName is a valid RFC 1123 hostname, this function uses
520
 * a regex on the string, so it is probably best not used when speed is essential.
521
 */
522
bool DNSName::isHostname() const
523
0
{
524
0
  static Regex hostNameRegex = Regex("^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\\.)+$");
525
0
  return hostNameRegex.match(this->toString());
526
0
}
527
528
unsigned int DNSName::countLabels() const
529
0
{
530
0
  unsigned int count=0;
531
0
  const unsigned char* p = reinterpret_cast<const unsigned char*>(d_storage.c_str());
532
0
  const unsigned char* end = reinterpret_cast<const unsigned char*>(p + d_storage.size());
533
534
0
  while (p < end && *p) {
535
0
    ++count;
536
0
    p += *p + 1;
537
0
  }
538
0
  return count;
539
0
}
540
541
void DNSName::trimToLabels(unsigned int to)
542
0
{
543
0
  while(countLabels() > to && chopOff()) {
544
0
    ;
545
0
  }
546
0
}
547
548
549
size_t hash_value(DNSName const& d)
550
0
{
551
0
  return d.hash();
552
0
}
553
554
void DNSName::appendEscapedLabel(std::string& appendTo, const char* orig, size_t len)
555
0
{
556
0
  size_t pos = 0;
557
558
0
  while (pos < len) {
559
0
    auto p = static_cast<uint8_t>(orig[pos]);
560
0
    if (p=='.') {
561
0
      appendTo+="\\.";
562
0
    }
563
0
    else if (p=='\\') {
564
0
      appendTo+="\\\\";
565
0
    }
566
0
    else if (p > 0x20 && p < 0x7f) {
567
0
      appendTo.append(1, static_cast<char>(p));
568
0
    }
569
0
    else {
570
0
      char buf[] = "000";
571
0
      auto got = snprintf(buf, sizeof(buf), "%03" PRIu8, p);
572
0
      if (got < 0 || static_cast<size_t>(got) >= sizeof(buf)) {
573
0
        throw std::runtime_error("Error, snprintf returned " + std::to_string(got) + " while escaping label " + std::string(orig, len));
574
0
      }
575
0
      appendTo.append(1, '\\');
576
0
      appendTo += buf;
577
0
    }
578
0
    ++pos;
579
0
  }
580
0
}
581
582
bool DNSName::has8bitBytes() const
583
0
{
584
0
  const auto& s = d_storage;
585
0
  string::size_type pos = 0;
586
0
  uint8_t length = s.at(pos);
587
0
  while (length > 0) {
588
0
    for (size_t idx = 0; idx < length; idx++) {
589
0
      ++pos;
590
0
      char c = s.at(pos);
591
0
      if (!((c >= 'a' && c <= 'z') ||
592
0
            (c >= 'A' && c <= 'Z') ||
593
0
            (c >= '0' && c <= '9') ||
594
0
            c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':')) {
595
0
        return true;
596
0
      }
597
0
    }
598
0
    ++pos;
599
0
    length = s.at(pos);
600
0
  }
601
602
0
  return false;
603
0
}
604
605
// clang-format off
606
const unsigned char dns_toupper_table[256] = {
607
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
608
  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
609
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
610
  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
611
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
612
  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
613
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
614
  0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
615
  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
616
  0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
617
  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
618
  0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
619
  0x60, 'A',  'B',  'C',  'D',  'E',  'F',  'G',
620
  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
621
  'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
622
  'X',  'Y',  'Z',  0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
623
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
624
  0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
625
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
626
  0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
627
  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
628
  0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
629
  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
630
  0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
631
  0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
632
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
633
  0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
634
  0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
635
  0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
636
  0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
637
  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
638
  0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
639
};
640
641
const unsigned char dns_tolower_table[256] = {
642
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
643
  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
644
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
645
  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
646
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
647
  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
648
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
649
  0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
650
  0x40, 'a',  'b',  'c',  'd',  'e',  'f',  'g',
651
  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
652
  'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
653
  'x',  'y',  'z',  0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
654
  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
655
  0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
656
  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
657
  0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
658
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
659
  0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
660
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
661
  0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
662
  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
663
  0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
664
  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
665
  0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
666
  0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
667
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
668
  0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
669
  0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
670
  0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
671
  0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
672
  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
673
  0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
674
};
675
676
DNSName::RawLabelsVisitor::RawLabelsVisitor(const DNSName::string_t& storage): d_storage(storage)
677
0
{
678
0
  size_t position = 0;
679
0
  while (position < storage.size()) {
680
0
    auto labelLength = static_cast<uint8_t>(storage.at(position));
681
0
    if (labelLength == 0) {
682
0
      break;
683
0
    }
684
0
    d_labelPositions.at(d_position) = position;
685
0
    d_position++;
686
0
    position += labelLength + 1;
687
0
  }
688
0
}
689
690
DNSName::RawLabelsVisitor DNSName::getRawLabelsVisitor() const
691
0
{
692
0
  return DNSName::RawLabelsVisitor(getStorage());
693
0
}
694
695
std::string_view DNSName::RawLabelsVisitor::front() const
696
0
{
697
0
  if (d_position == 0) {
698
0
    throw std::out_of_range("trying to access the front of an empty DNSName::RawLabelsVisitor");
699
0
  }
700
0
  uint8_t length = d_storage.at(0);
701
0
  if (length == 0) {
702
0
    return std::string_view();
703
0
  }
704
0
  return std::string_view(&d_storage.at(1), length);
705
0
}
706
707
std::string_view DNSName::RawLabelsVisitor::back() const
708
0
{
709
0
  if (d_position == 0) {
710
0
    throw std::out_of_range("trying to access the back of an empty DNSName::RawLabelsVisitor");
711
0
  }
712
0
  size_t offset = d_labelPositions.at(d_position-1);
713
0
  uint8_t length = d_storage.at(offset);
714
0
  if (length == 0) {
715
0
    return std::string_view();
716
0
  }
717
0
  return std::string_view(&d_storage.at(offset + 1), length);
718
0
}
719
720
bool DNSName::RawLabelsVisitor::pop_back()
721
0
{
722
0
  if (d_position > 0) {
723
0
    d_position--;
724
0
    return true;
725
0
  }
726
0
  return false;
727
0
}
728
729
bool DNSName::RawLabelsVisitor::empty() const
730
0
{
731
0
  return d_position == 0;
732
0
}