Coverage Report

Created: 2024-04-25 06:25

/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
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
251k
{
104
251k
  if (offset >= len)
105
2.58k
    throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
106
107
249k
  if(!uncompress) {
108
0
    if(const void * fnd=memchr(pos+offset, 0, len-offset)) {
109
0
      d_storage.reserve(2+(const char*)fnd-(pos+offset));
110
0
    }
111
0
  }
112
113
249k
  packetParser(pos, len, offset, uncompress, qtype, qclass, consumed, 0, minOffset);
114
249k
}
115
116
static void checkLabelLength(uint8_t length)
117
105k
{
118
105k
  if (length == 0) {
119
0
    throw std::range_error("no such thing as an empty label to append");
120
0
  }
121
105k
  if (length > 63) {
122
0
    throw std::range_error("label too long to append");
123
0
  }
124
105k
}
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
265k
{
129
265k
  const size_t initialPos = pos;
130
265k
  size_t totalLength = 0;
131
265k
  unsigned char labellen = 0;
132
133
370k
  do {
134
370k
    labellen = view.at(pos);
135
370k
    ++pos;
136
137
370k
    if (labellen == 0) {
138
247k
      --pos;
139
247k
      break;
140
247k
    }
141
142
122k
    if (labellen >= 0xc0) {
143
16.9k
      if (!uncompress) {
144
0
        throw std::range_error("Found compressed label, instructed not to follow");
145
0
      }
146
16.9k
      --pos;
147
16.9k
      break;
148
16.9k
    }
149
150
105k
    if ((labellen & 0xc0) != 0) {
151
265
      throw std::range_error("Found an invalid label length in qname (only one of the first two bits is set)");
152
265
    }
153
105k
    checkLabelLength(labellen);
154
    // reserve one byte for the label length
155
105k
    if (totalLength + labellen > s_maxDNSNameLength - 1) {
156
8
      throw std::range_error("name too long to append");
157
8
    }
158
105k
    if (pos + labellen >= view.size()) {
159
290
      throw std::range_error("Found an invalid label length in qname");
160
290
    }
161
105k
    pos += labellen;
162
105k
    totalLength += 1 + labellen;
163
105k
  }
164
265k
  while (pos < view.size());
165
166
264k
  if (totalLength != 0) {
167
56.7k
    auto existingSize = d_storage.size();
168
56.7k
    if (existingSize > 0) {
169
      // remove the last label count, we are about to override it */
170
3.69k
      --existingSize;
171
3.69k
    }
172
56.7k
    d_storage.reserve(existingSize + totalLength + 1);
173
56.7k
    d_storage.resize(existingSize + totalLength);
174
56.7k
    memcpy(&d_storage.at(existingSize), &view.at(initialPos), totalLength);
175
56.7k
    d_storage.append(1, static_cast<char>(0));
176
56.7k
  }
177
264k
  return pos;
178
265k
}
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
265k
{
183
265k
  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
265k
  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
265k
  unsigned char labellen{0};
191
192
265k
  pdns::views::UnsignedCharView view(qpos, len);
193
265k
  auto pos = parsePacketUncompressed(view, offset, uncompress);
194
195
265k
  labellen = view.at(pos);
196
265k
  pos++;
197
265k
  if (labellen != 0 && pos < view.size()) {
198
16.5k
    if (labellen < 0xc0) {
199
0
      abort();
200
0
    }
201
202
16.5k
    if (!uncompress) {
203
0
      throw std::range_error("Found compressed label, instructed not to follow");
204
0
    }
205
206
16.5k
    labellen &= (~0xc0);
207
16.5k
    size_t newpos = (labellen << 8) + view.at(pos);
208
209
16.5k
    if (newpos >= offset) {
210
288
      throw std::range_error("Found a forward reference during label decompression");
211
288
    }
212
213
16.2k
    if (newpos < minOffset) {
214
9
      throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos)+ " < "+std::to_string(minOffset)+")");
215
9
    }
216
217
16.2k
    if (++depth > 100) {
218
0
      throw std::range_error("Abort label decompression after 100 redirects");
219
0
    }
220
221
16.2k
    packetParser(qpos, len, newpos, true, nullptr, nullptr, nullptr, depth, minOffset);
222
223
16.2k
    pos++;
224
16.2k
  }
225
226
265k
  if (d_storage.empty()) {
227
195k
    d_storage.append(1, static_cast<char>(0)); // we just parsed the root
228
195k
  }
229
230
265k
  if (consumed != nullptr) {
231
248k
    *consumed = pos - offset;
232
248k
  }
233
234
265k
  if (qtype != nullptr) {
235
0
    if (pos + 2 > view.size()) {
236
0
      throw std::range_error("Trying to read qtype past the end of the buffer ("+std::to_string(pos + 2)+ " > "+std::to_string(len)+")");
237
0
    }
238
0
    *qtype = view.at(pos)*256 + view.at(pos+1);
239
0
  }
240
241
265k
  pos += 2;
242
265k
  if (qclass != nullptr) {
243
0
    if (pos + 2 > view.size()) {
244
0
      throw std::range_error("Trying to read qclass past the end of the buffer ("+std::to_string(pos + 2)+ " > "+std::to_string(len)+")");
245
0
    }
246
0
    *qclass = view.at(pos)*256 + view.at(pos+1);
247
0
  }
248
265k
}
249
250
std::string DNSName::toString(const std::string& separator, const bool trailing) const
251
293
{
252
293
  std::string ret;
253
293
  toString(ret, separator, trailing);
254
293
  return ret;
255
293
}
256
257
void DNSName::toString(std::string& output, const std::string& separator, const bool trailing) const
258
293
{
259
293
  if (empty()) {
260
141
    throw std::out_of_range("Attempt to print an unset dnsname");
261
141
  }
262
263
152
  if (isRoot()) {
264
3
    output += (trailing ? separator : "");
265
3
    return;
266
3
  }
267
268
149
  if (output.capacity() < (output.size() + d_storage.size())) {
269
43
    output.reserve(output.size() + d_storage.size());
270
43
  }
271
272
149
  {
273
    // iterate over the raw labels
274
149
    const char* p = d_storage.c_str();
275
149
    const char* end = p + d_storage.size();
276
277
801
    while (p < end && *p) {
278
652
      appendEscapedLabel(output, p + 1, static_cast<size_t>(*p));
279
652
      output += separator;
280
652
      p += *p + 1;
281
652
    }
282
149
  }
283
284
149
  if (!trailing) {
285
146
    output.resize(output.size() - separator.size());
286
146
  }
287
149
}
288
289
std::string DNSName::toLogString() const
290
192
{
291
192
  if (empty()) {
292
38
    return "(empty)";
293
38
  }
294
295
154
  return toStringRootDot();
296
192
}
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
652
{
556
652
  size_t pos = 0;
557
558
5.28k
  while (pos < len) {
559
4.63k
    auto p = static_cast<uint8_t>(orig[pos]);
560
4.63k
    if (p=='.') {
561
235
      appendTo+="\\.";
562
235
    }
563
4.39k
    else if (p=='\\') {
564
208
      appendTo+="\\\\";
565
208
    }
566
4.18k
    else if (p > 0x20 && p < 0x7f) {
567
1.11k
      appendTo.append(1, static_cast<char>(p));
568
1.11k
    }
569
3.07k
    else {
570
3.07k
      char buf[] = "000";
571
3.07k
      auto got = snprintf(buf, sizeof(buf), "%03" PRIu8, p);
572
3.07k
      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
3.07k
      appendTo.append(1, '\\');
576
3.07k
      appendTo += buf;
577
3.07k
    }
578
4.63k
    ++pos;
579
4.63k
  }
580
652
}
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
}