Coverage Report

Created: 2023-03-26 07:18

/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
501
{
46
501
  std::string dots;
47
501
  if (length > s_maxDNSNameLength) {
48
352
    length = s_maxDNSNameLength;
49
352
    dots = "...";
50
352
  }
51
501
  std::string label;
52
501
  DNSName::appendEscapedLabel(label, buf, length);
53
501
  throw std::range_error(msg + label + dots);
54
501
}
55
56
DNSName::DNSName(const std::string_view sw)
57
202k
{
58
202k
  const char* p = sw.data();
59
202k
  size_t length = sw.length();
60
61
202k
  if(length == 0 || (length == 1 && p[0]=='.')) {
62
3.93k
    d_storage.assign(1, '\0');
63
198k
  } else {
64
198k
    if(!std::memchr(p, '\\', length)) {
65
152k
      unsigned char lenpos=0;
66
152k
      unsigned char labellen=0;
67
152k
      const char* const pbegin=p, *pend=p+length;
68
69
152k
      d_storage.reserve(length+1);
70
371k
      for(auto iter = pbegin; iter != pend; ) {
71
219k
        lenpos = d_storage.size();
72
219k
        if(*iter=='.')
73
63
          throwSafeRangeError("Found . in wrong position in DNSName: ", p, length);
74
219k
        d_storage.append(1, '\0');
75
219k
        labellen=0;
76
219k
        auto begiter=iter;
77
193M
        for(; iter != pend && *iter!='.'; ++iter) {
78
192M
          labellen++;
79
192M
        }
80
219k
        d_storage.append(begiter,iter);
81
219k
        if(iter != pend)
82
67.2k
          ++iter;
83
219k
        if(labellen > 63)
84
283
          throwSafeRangeError("label too long to append: ", p, length);
85
86
219k
        if(iter-pbegin > static_cast<ptrdiff_t>(s_maxDNSNameLength - 1)) // reserve two bytes, one for length and one for the root label
87
69
          throwSafeRangeError("name too long to append: ", p, length);
88
89
219k
        d_storage[lenpos]=labellen;
90
219k
      }
91
152k
      d_storage.append(1, '\0');
92
152k
    }
93
46.3k
    else {
94
46.3k
      d_storage=segmentDNSNameRaw(p, length);
95
46.3k
      if(d_storage.size() > s_maxDNSNameLength) {
96
79
        throwSafeRangeError("name too long: ", p, length);
97
79
      }
98
46.3k
    }
99
198k
  }
100
202k
}
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
0
{
105
0
  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
0
  if(!uncompress) {
109
0
    if(const void * fnd=memchr(pos+offset, 0, len-offset)) {
110
0
      d_storage.reserve(2+(const char*)fnd-(pos+offset));
111
0
    }
112
0
  }
113
114
0
  packetParser(pos, len, offset, uncompress, qtype, qclass, consumed, 0, minOffset);
115
0
}
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
0
{
120
0
  const unsigned char* pos=(const unsigned char*)qpos;
121
0
  unsigned char labellen;
122
0
  const unsigned char *opos = pos;
123
124
0
  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
0
  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
0
  const unsigned char* end = pos + len;
130
0
  pos += offset;
131
0
  while((labellen=*pos++) && pos < end) { // "scan and copy"
132
0
    if(labellen >= 0xc0) {
133
0
      if(!uncompress)
134
0
        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
0
    } else if(labellen & 0xc0) {
150
0
      throw std::range_error("Found an invalid label length in qname (only one of the first two bits is set)");
151
0
    }
152
0
    if (pos + labellen < end) {
153
0
      appendRawLabel((const char*)pos, labellen);
154
0
    }
155
0
    else
156
0
      throw std::range_error("Found an invalid label length in qname");
157
0
    pos+=labellen;
158
0
  }
159
0
  if(d_storage.empty())
160
0
    d_storage.append(1, (char)0); // we just parsed the root
161
0
  if(consumed)
162
0
    *consumed = pos - opos - offset;
163
0
  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
0
  pos+=2;
170
0
  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
0
}
177
178
std::string DNSName::toString(const std::string& separator, const bool trailing) const
179
25.7k
{
180
25.7k
  std::string ret;
181
25.7k
  toString(ret, separator, trailing);
182
25.7k
  return ret;
183
25.7k
}
184
185
void DNSName::toString(std::string& output, const std::string& separator, const bool trailing) const
186
25.7k
{
187
25.7k
  if (empty()) {
188
0
    throw std::out_of_range("Attempt to print an unset dnsname");
189
0
  }
190
191
25.7k
  if (isRoot()) {
192
663
    output += (trailing ? separator : "");
193
663
    return;
194
663
  }
195
196
25.0k
  if (output.capacity() < (output.size() + d_storage.size())) {
197
5.11k
    output.reserve(output.size() + d_storage.size());
198
5.11k
  }
199
200
25.0k
  {
201
    // iterate over the raw labels
202
25.0k
    const char* p = d_storage.c_str();
203
25.0k
    const char* end = p + d_storage.size();
204
205
59.7k
    while (p < end && *p) {
206
34.6k
      appendEscapedLabel(output, p + 1, static_cast<size_t>(*p));
207
34.6k
      output += separator;
208
34.6k
      p += *p + 1;
209
34.6k
    }
210
25.0k
  }
211
212
25.0k
  if (!trailing) {
213
24.7k
    output.resize(output.size() - separator.size());
214
24.7k
  }
215
25.0k
}
216
217
std::string DNSName::toLogString() const
218
210
{
219
210
  if (empty()) {
220
47
    return "(empty)";
221
47
  }
222
223
163
  return toStringRootDot();
224
210
}
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
0
size_t DNSName::wirelength() const {
247
0
  return d_storage.length();
248
0
}
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
  DNSName result;
300
301
0
  const std::vector<std::string> ours = getRawLabels();
302
0
  const std::vector<std::string> others = other.getRawLabels();
303
304
0
  for (size_t pos = 0; ours.size() > pos && others.size() > pos; pos++) {
305
0
    const std::string& ourLabel = ours.at(ours.size() - pos - 1);
306
0
    const std::string& otherLabel = others.at(others.size() - pos - 1);
307
308
0
    if (!pdns_iequals(ourLabel, otherLabel)) {
309
0
      break;
310
0
    }
311
312
0
    result.prependRawLabel(ourLabel);
313
0
  }
314
315
0
  return result;
316
0
}
317
318
DNSName DNSName::labelReverse() const
319
0
{
320
0
  DNSName ret;
321
322
0
  if(isRoot())
323
0
    return *this; // we don't create the root automatically below
324
325
0
  if (!empty()) {
326
0
    vector<string> l=getRawLabels();
327
0
    while(!l.empty()) {
328
0
      ret.appendRawLabel(l.back());
329
0
      l.pop_back();
330
0
    }
331
0
  }
332
0
  return ret;
333
0
}
334
335
void DNSName::appendRawLabel(const std::string& label)
336
0
{
337
0
  appendRawLabel(label.c_str(), label.length());
338
0
}
339
340
void DNSName::appendRawLabel(const char* start, unsigned int length)
341
0
{
342
0
  if(length==0)
343
0
    throw std::range_error("no such thing as an empty label to append");
344
0
  if(length > 63)
345
0
    throw std::range_error("label too long to append");
346
0
  if(d_storage.size() + length > s_maxDNSNameLength - 1) // reserve one byte for the label length
347
0
    throw std::range_error("name too long to append");
348
349
0
  if(d_storage.empty()) {
350
0
    d_storage.append(1, (char)length);
351
0
  }
352
0
  else {
353
0
    *d_storage.rbegin()=(char)length;
354
0
  }
355
0
  d_storage.append(start, length);
356
0
  d_storage.append(1, (char)0);
357
0
}
358
359
void DNSName::prependRawLabel(const std::string& label)
360
0
{
361
0
  if(label.empty())
362
0
    throw std::range_error("no such thing as an empty label to prepend");
363
0
  if(label.size() > 63)
364
0
    throw std::range_error("label too long to prepend");
365
0
  if(d_storage.size() + label.size() > s_maxDNSNameLength - 1) // reserve one byte for the label length
366
0
    throw std::range_error("name too long to prepend");
367
368
0
  if(d_storage.empty())
369
0
    d_storage.append(1, (char)0);
370
371
0
  string_t prep(1, (char)label.size());
372
0
  prep.append(label.c_str(), label.size());
373
0
  d_storage = prep+d_storage;
374
0
}
375
376
bool DNSName::slowCanonCompare(const DNSName& rhs) const 
377
0
{
378
0
  auto ours=getRawLabels(), rhsLabels = rhs.getRawLabels();
379
0
  return std::lexicographical_compare(ours.rbegin(), ours.rend(), rhsLabels.rbegin(), rhsLabels.rend(), CIStringCompare());
380
0
}
381
382
vector<std::string> DNSName::getRawLabels() const
383
0
{
384
0
  vector<std::string> ret;
385
0
  ret.reserve(countLabels());
386
  // 3www4ds9a2nl0
387
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) {
388
0
    ret.push_back({(const char*)p+1, (size_t)*p}); // XXX FIXME
389
0
  }
390
0
  return ret;
391
0
}
392
393
std::string DNSName::getRawLabel(unsigned int pos) const
394
0
{
395
0
  unsigned int currentPos = 0;
396
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++) {
397
0
    if (currentPos == pos) {
398
0
      return std::string((const char*)p+1, (size_t)*p);
399
0
    }
400
0
  }
401
402
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");
403
0
}
404
405
DNSName DNSName::getLastLabel() const
406
0
{
407
0
  DNSName ret(*this);
408
0
  ret.trimToLabels(1);
409
0
  return ret;
410
0
}
411
412
bool DNSName::chopOff()
413
0
{
414
0
  if(d_storage.empty() || d_storage[0]==0)
415
0
    return false;
416
0
  d_storage.erase(0, (unsigned int)d_storage[0]+1);
417
0
  return true;
418
0
}
419
420
bool DNSName::isWildcard() const
421
0
{
422
0
  if(d_storage.size() < 2)
423
0
    return false;
424
0
  auto p = d_storage.begin();
425
0
  return (*p == 0x01 && *++p == '*');
426
0
}
427
428
/*
429
 * Returns true if the DNSName is a valid RFC 1123 hostname, this function uses
430
 * a regex on the string, so it is probably best not used when speed is essential.
431
 */
432
bool DNSName::isHostname() const
433
0
{
434
0
  static Regex hostNameRegex = Regex("^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\\.)+$");
435
0
  return hostNameRegex.match(this->toString());
436
0
}
437
438
unsigned int DNSName::countLabels() const
439
0
{
440
0
  unsigned int count=0;
441
0
  const unsigned char* p = reinterpret_cast<const unsigned char*>(d_storage.c_str());
442
0
  const unsigned char* end = reinterpret_cast<const unsigned char*>(p + d_storage.size());
443
444
0
  while (p < end && *p) {
445
0
    ++count;
446
0
    p += *p + 1;
447
0
  }
448
0
  return count;
449
0
}
450
451
void DNSName::trimToLabels(unsigned int to)
452
0
{
453
0
  while(countLabels() > to && chopOff())
454
0
    ;
455
0
}
456
457
458
size_t hash_value(DNSName const& d)
459
0
{
460
0
  return d.hash();
461
0
}
462
463
void DNSName::appendEscapedLabel(std::string& appendTo, const char* orig, size_t len)
464
35.1k
{
465
35.1k
  size_t pos = 0;
466
467
577k
  while (pos < len) {
468
542k
    auto p = static_cast<uint8_t>(orig[pos]);
469
542k
    if(p=='.')
470
711
      appendTo+="\\.";
471
542k
    else if(p=='\\')
472
411
      appendTo+="\\\\";
473
541k
    else if(p > 0x20 && p < 0x7f)
474
463k
      appendTo.append(1, (char)p);
475
78.4k
    else {
476
78.4k
      char buf[] = "000";
477
78.4k
      auto got = snprintf(buf, sizeof(buf), "%03" PRIu8, p);
478
78.4k
      if (got < 0 || static_cast<size_t>(got) >= sizeof(buf)) {
479
0
        throw std::runtime_error("Error, snprintf returned " + std::to_string(got) + " while escaping label " + std::string(orig, len));
480
0
      }
481
78.4k
      appendTo.append(1, '\\');
482
78.4k
      appendTo += buf;
483
78.4k
    }
484
542k
    ++pos;
485
542k
  }
486
35.1k
}
487
488
bool DNSName::has8bitBytes() const
489
0
{
490
0
  const auto& s = d_storage;
491
0
  string::size_type pos = 0;
492
0
  uint8_t length = s.at(pos);
493
0
  while (length > 0) {
494
0
    for (size_t idx = 0; idx < length; idx++) {
495
0
      ++pos;
496
0
      char c = s.at(pos);
497
0
      if(!((c >= 'a' && c <= 'z') ||
498
0
           (c >= 'A' && c <= 'Z') ||
499
0
           (c >= '0' && c <= '9') ||
500
0
           c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':'))
501
0
        return true;
502
0
    }
503
0
    ++pos;
504
0
    length = s.at(pos);
505
0
  }
506
507
0
  return false;
508
0
}
509
510
// clang-format off
511
const unsigned char dns_toupper_table[256] = {
512
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
513
  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
514
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
515
  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
516
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
517
  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
518
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
519
  0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
520
  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
521
  0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
522
  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
523
  0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
524
  0x60, 'A',  'B',  'C',  'D',  'E',  'F',  'G',
525
  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
526
  'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
527
  'X',  'Y',  'Z',  0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
528
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
529
  0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
530
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
531
  0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
532
  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
533
  0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
534
  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
535
  0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
536
  0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
537
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
538
  0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
539
  0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
540
  0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
541
  0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
542
  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
543
  0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
544
};
545
546
const unsigned char dns_tolower_table[256] = {
547
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
548
  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
549
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
550
  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
551
  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
552
  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
553
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
554
  0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
555
  0x40, 'a',  'b',  'c',  'd',  'e',  'f',  'g',
556
  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
557
  'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
558
  'x',  'y',  'z',  0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
559
  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
560
  0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
561
  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
562
  0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
563
  0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
564
  0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
565
  0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
566
  0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
567
  0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
568
  0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
569
  0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
570
  0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
571
  0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
572
  0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
573
  0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
574
  0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
575
  0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
576
  0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
577
  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
578
  0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
579
};
580
581
DNSName::RawLabelsVisitor::RawLabelsVisitor(const DNSName::string_t& storage): d_storage(storage)
582
0
{
583
0
  size_t position = 0;
584
0
  while (position < storage.size()) {
585
0
    auto labelLength = static_cast<uint8_t>(storage.at(position));
586
0
    if (labelLength == 0) {
587
0
      break;
588
0
    }
589
0
    d_labelPositions.at(d_position) = position;
590
0
    d_position++;
591
0
    position += labelLength + 1;
592
0
  }
593
0
}
594
595
DNSName::RawLabelsVisitor DNSName::getRawLabelsVisitor() const
596
0
{
597
0
  return DNSName::RawLabelsVisitor(getStorage());
598
0
}
599
600
std::string_view DNSName::RawLabelsVisitor::front() const
601
0
{
602
0
  if (d_position == 0) {
603
0
    throw std::out_of_range("trying to access the front of an empty DNSName::RawLabelsVisitor");
604
0
  }
605
0
  uint8_t length = d_storage.at(0);
606
0
  if (length == 0) {
607
0
    return std::string_view();
608
0
  }
609
0
  return std::string_view(&d_storage.at(1), length);
610
0
}
611
612
std::string_view DNSName::RawLabelsVisitor::back() const
613
0
{
614
0
  if (d_position == 0) {
615
0
    throw std::out_of_range("trying to access the back of an empty DNSName::RawLabelsVisitor");
616
0
  }
617
0
  size_t offset = d_labelPositions.at(d_position-1);
618
0
  uint8_t length = d_storage.at(offset);
619
0
  if (length == 0) {
620
0
    return std::string_view();
621
0
  }
622
0
  return std::string_view(&d_storage.at(offset + 1), length);
623
0
}
624
625
bool DNSName::RawLabelsVisitor::pop_back()
626
0
{
627
0
  if (d_position > 0) {
628
0
    d_position--;
629
0
    return true;
630
0
  }
631
0
  return false;
632
0
}
633
634
bool DNSName::RawLabelsVisitor::empty() const
635
0
{
636
0
  return d_position == 0;
637
0
}