Coverage Report

Created: 2023-09-25 06:58

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