]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsname.cc
Meson: Separate test files from common files
[thirdparty/pdns.git] / pdns / dnsname.cc
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 {
41 return os <<d.toLogString();
42 }
43
44 void DNSName::throwSafeRangeError(const std::string& msg, const char* buf, size_t length)
45 {
46 std::string dots;
47 if (length > s_maxDNSNameLength) {
48 length = s_maxDNSNameLength;
49 dots = "...";
50 }
51 std::string label;
52 DNSName::appendEscapedLabel(label, buf, length);
53 throw std::range_error(msg + label + dots);
54 }
55
56 DNSName::DNSName(const std::string_view sw)
57 {
58 const char* p = sw.data();
59 size_t length = sw.length();
60
61 if(length == 0 || (length == 1 && p[0]=='.')) {
62 d_storage.assign(1, '\0');
63 } else {
64 if(!std::memchr(p, '\\', length)) {
65 unsigned char lenpos=0;
66 unsigned char labellen=0;
67 const char* const pbegin=p, *pend=p+length;
68
69 d_storage.reserve(length+1);
70 for(auto iter = pbegin; iter != pend; ) {
71 lenpos = d_storage.size();
72 if(*iter=='.')
73 throwSafeRangeError("Found . in wrong position in DNSName: ", p, length);
74 d_storage.append(1, '\0');
75 labellen=0;
76 auto begiter=iter;
77 for(; iter != pend && *iter!='.'; ++iter) {
78 labellen++;
79 }
80 d_storage.append(begiter,iter);
81 if(iter != pend)
82 ++iter;
83 if(labellen > 63)
84 throwSafeRangeError("label too long to append: ", p, length);
85
86 if(iter-pbegin > static_cast<ptrdiff_t>(s_maxDNSNameLength - 1)) // reserve two bytes, one for length and one for the root label
87 throwSafeRangeError("name too long to append: ", p, length);
88
89 d_storage[lenpos]=labellen;
90 }
91 d_storage.append(1, '\0');
92 }
93 else {
94 d_storage=segmentDNSNameRaw(p, length);
95 if(d_storage.size() > s_maxDNSNameLength) {
96 throwSafeRangeError("name too long: ", p, length);
97 }
98 }
99 }
100 }
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 {
104 if (offset >= len)
105 throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
106
107 if(!uncompress) {
108 if(const void * fnd=memchr(pos+offset, 0, len-offset)) {
109 d_storage.reserve(2+(const char*)fnd-(pos+offset));
110 }
111 }
112
113 packetParser(pos, len, offset, uncompress, qtype, qclass, consumed, 0, minOffset);
114 }
115
116 static void checkLabelLength(uint8_t length)
117 {
118 if (length == 0) {
119 throw std::range_error("no such thing as an empty label to append");
120 }
121 if (length > 63) {
122 throw std::range_error("label too long to append");
123 }
124 }
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 {
129 const size_t initialPos = pos;
130 size_t totalLength = 0;
131 unsigned char labellen = 0;
132
133 do {
134 labellen = view.at(pos);
135 ++pos;
136
137 if (labellen == 0) {
138 --pos;
139 break;
140 }
141
142 if (labellen >= 0xc0) {
143 if (!uncompress) {
144 throw std::range_error("Found compressed label, instructed not to follow");
145 }
146 --pos;
147 break;
148 }
149
150 if ((labellen & 0xc0) != 0) {
151 throw std::range_error("Found an invalid label length in qname (only one of the first two bits is set)");
152 }
153 checkLabelLength(labellen);
154 // reserve one byte for the label length
155 if (totalLength + labellen > s_maxDNSNameLength - 1) {
156 throw std::range_error("name too long to append");
157 }
158 if (pos + labellen >= view.size()) {
159 throw std::range_error("Found an invalid label length in qname");
160 }
161 pos += labellen;
162 totalLength += 1 + labellen;
163 }
164 while (pos < view.size());
165
166 if (totalLength != 0) {
167 auto existingSize = d_storage.size();
168 if (existingSize > 0) {
169 // remove the last label count, we are about to override it */
170 --existingSize;
171 }
172 d_storage.reserve(existingSize + totalLength + 1);
173 d_storage.resize(existingSize + totalLength);
174 memcpy(&d_storage.at(existingSize), &view.at(initialPos), totalLength);
175 d_storage.append(1, static_cast<char>(0));
176 }
177 return pos;
178 }
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 {
183 if (offset >= len) {
184 throw std::range_error("Trying to read past the end of the buffer ("+std::to_string(offset)+ " >= "+std::to_string(len)+")");
185 }
186
187 if (offset < static_cast<size_t>(minOffset)) {
188 throw std::range_error("Trying to read before the beginning of the buffer ("+std::to_string(offset)+ " < "+std::to_string(minOffset)+")");
189 }
190 unsigned char labellen{0};
191
192 pdns::views::UnsignedCharView view(qpos, len);
193 auto pos = parsePacketUncompressed(view, offset, uncompress);
194
195 labellen = view.at(pos);
196 pos++;
197 if (labellen != 0 && pos < view.size()) {
198 if (labellen < 0xc0) {
199 abort();
200 }
201
202 if (!uncompress) {
203 throw std::range_error("Found compressed label, instructed not to follow");
204 }
205
206 labellen &= (~0xc0);
207 size_t newpos = (labellen << 8) + view.at(pos);
208
209 if (newpos >= offset) {
210 throw std::range_error("Found a forward reference during label decompression");
211 }
212
213 if (newpos < minOffset) {
214 throw std::range_error("Invalid label position during decompression ("+std::to_string(newpos)+ " < "+std::to_string(minOffset)+")");
215 }
216
217 if (++depth > 100) {
218 throw std::range_error("Abort label decompression after 100 redirects");
219 }
220
221 packetParser(qpos, len, newpos, true, nullptr, nullptr, nullptr, depth, minOffset);
222
223 pos++;
224 }
225
226 if (d_storage.empty()) {
227 d_storage.append(1, static_cast<char>(0)); // we just parsed the root
228 }
229
230 if (consumed != nullptr) {
231 *consumed = pos - offset;
232 }
233
234 if (qtype != nullptr) {
235 if (pos + 2 > view.size()) {
236 throw std::range_error("Trying to read qtype past the end of the buffer ("+std::to_string(pos + 2)+ " > "+std::to_string(len)+")");
237 }
238 *qtype = view.at(pos)*256 + view.at(pos+1);
239 }
240
241 pos += 2;
242 if (qclass != nullptr) {
243 if (pos + 2 > view.size()) {
244 throw std::range_error("Trying to read qclass past the end of the buffer ("+std::to_string(pos + 2)+ " > "+std::to_string(len)+")");
245 }
246 *qclass = view.at(pos)*256 + view.at(pos+1);
247 }
248 }
249
250 std::string DNSName::toString(const std::string& separator, const bool trailing) const
251 {
252 std::string ret;
253 toString(ret, separator, trailing);
254 return ret;
255 }
256
257 void DNSName::toString(std::string& output, const std::string& separator, const bool trailing) const
258 {
259 if (empty()) {
260 throw std::out_of_range("Attempt to print an unset dnsname");
261 }
262
263 if (isRoot()) {
264 output += (trailing ? separator : "");
265 return;
266 }
267
268 if (output.capacity() < (output.size() + d_storage.size())) {
269 output.reserve(output.size() + d_storage.size());
270 }
271
272 {
273 // iterate over the raw labels
274 const char* p = d_storage.c_str();
275 const char* end = p + d_storage.size();
276
277 while (p < end && *p) {
278 appendEscapedLabel(output, p + 1, static_cast<size_t>(*p));
279 output += separator;
280 p += *p + 1;
281 }
282 }
283
284 if (!trailing) {
285 output.resize(output.size() - separator.size());
286 }
287 }
288
289 std::string DNSName::toLogString() const
290 {
291 if (empty()) {
292 return "(empty)";
293 }
294
295 return toStringRootDot();
296 }
297
298 std::string DNSName::toDNSString() const
299 {
300 if (empty()) {
301 throw std::out_of_range("Attempt to DNSString an unset dnsname");
302 }
303
304 return std::string(d_storage.c_str(), d_storage.length());
305 }
306
307 std::string DNSName::toDNSStringLC() const
308 {
309 auto result = toDNSString();
310 toLowerInPlace(result); // label lengths are always < 'A'
311 return result;
312 }
313
314 /**
315 * Get the length of the DNSName on the wire
316 *
317 * @return the total wirelength of the DNSName
318 */
319 size_t DNSName::wirelength() const {
320 return d_storage.length();
321 }
322
323 // Are WE part of parent
324 bool DNSName::isPartOf(const DNSName& parent) const
325 {
326 if(parent.empty() || empty()) {
327 throw std::out_of_range("empty dnsnames aren't part of anything");
328 }
329
330 if(parent.d_storage.size() > d_storage.size()) {
331 return false;
332 }
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 for(auto us=d_storage.cbegin(); us<d_storage.cend(); us+=*us+1) {
336 auto distance = std::distance(us,d_storage.cend());
337 if (distance < 0 || static_cast<size_t>(distance) < parent.d_storage.size()) {
338 break;
339 }
340 if (static_cast<size_t>(distance) == parent.d_storage.size()) {
341 auto p = parent.d_storage.cbegin();
342 for(; us != d_storage.cend(); ++us, ++p) {
343 if(dns_tolower(*p) != dns_tolower(*us))
344 return false;
345 }
346 return true;
347 }
348 if (static_cast<uint8_t>(*us) > 63) {
349 throw std::out_of_range("illegal label length in dnsname");
350 }
351 }
352 return false;
353 }
354
355 DNSName DNSName::makeRelative(const DNSName& zone) const
356 {
357 DNSName ret(*this);
358 ret.makeUsRelative(zone);
359 return ret;
360 }
361
362 void DNSName::makeUsRelative(const DNSName& zone)
363 {
364 if (isPartOf(zone)) {
365 d_storage.erase(d_storage.size()-zone.d_storage.size());
366 d_storage.append(1, static_cast<char>(0)); // put back the trailing 0
367 }
368 else {
369 clear();
370 }
371 }
372
373 DNSName DNSName::getCommonLabels(const DNSName& other) const
374 {
375 if (empty() || other.empty()) {
376 return DNSName();
377 }
378
379 DNSName result(g_rootdnsname);
380
381 const std::vector<std::string> ours = getRawLabels();
382 const std::vector<std::string> others = other.getRawLabels();
383
384 for (size_t pos = 0; ours.size() > pos && others.size() > pos; pos++) {
385 const std::string& ourLabel = ours.at(ours.size() - pos - 1);
386 const std::string& otherLabel = others.at(others.size() - pos - 1);
387
388 if (!pdns_iequals(ourLabel, otherLabel)) {
389 break;
390 }
391
392 result.prependRawLabel(ourLabel);
393 }
394
395 return result;
396 }
397
398 DNSName DNSName::labelReverse() const
399 {
400 DNSName ret;
401
402 if (isRoot()) {
403 return *this; // we don't create the root automatically below
404 }
405
406 if (!empty()) {
407 vector<string> l=getRawLabels();
408 while(!l.empty()) {
409 ret.appendRawLabel(l.back());
410 l.pop_back();
411 }
412 }
413 return ret;
414 }
415
416 void DNSName::appendRawLabel(const std::string& label)
417 {
418 appendRawLabel(label.c_str(), label.length());
419 }
420
421 void DNSName::appendRawLabel(const char* start, unsigned int length)
422 {
423 checkLabelLength(length);
424
425 // reserve one byte for the label length
426 if (d_storage.size() + length > s_maxDNSNameLength - 1) {
427 throw std::range_error("name too long to append");
428 }
429
430 if (d_storage.empty()) {
431 d_storage.reserve(1 + length + 1);
432 d_storage.append(1, static_cast<char>(length));
433 }
434 else {
435 d_storage.reserve(d_storage.size() + length + 1);
436 *d_storage.rbegin() = static_cast<char>(length);
437 }
438 d_storage.append(start, length);
439 d_storage.append(1, static_cast<char>(0));
440 }
441
442 void DNSName::prependRawLabel(const std::string& label)
443 {
444 checkLabelLength(label.size());
445
446 // reserve one byte for the label length
447 if (d_storage.size() + label.size() > s_maxDNSNameLength - 1) {
448 throw std::range_error("name too long to prepend");
449 }
450
451 if (d_storage.empty()) {
452 d_storage.reserve(1 + label.size() + 1);
453 d_storage.append(1, static_cast<char>(0));
454 }
455 else {
456 d_storage.reserve(d_storage.size() + 1 + label.size());
457 }
458
459 string_t prep(1, static_cast<char>(label.size()));
460 prep.append(label.c_str(), label.size());
461 d_storage = prep+d_storage;
462 }
463
464 bool DNSName::slowCanonCompare(const DNSName& rhs) const
465 {
466 auto ours=getRawLabels(), rhsLabels = rhs.getRawLabels();
467 return std::lexicographical_compare(ours.rbegin(), ours.rend(), rhsLabels.rbegin(), rhsLabels.rend(), CIStringCompare());
468 }
469
470 vector<std::string> DNSName::getRawLabels() const
471 {
472 vector<std::string> ret;
473 ret.reserve(countLabels());
474 // 3www4ds9a2nl0
475 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 ret.push_back({(const char*)p+1, (size_t)*p}); // XXX FIXME
477 }
478 return ret;
479 }
480
481 std::string DNSName::getRawLabel(unsigned int pos) const
482 {
483 unsigned int currentPos = 0;
484 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 if (currentPos == pos) {
486 return std::string((const char*)p+1, (size_t)*p);
487 }
488 }
489
490 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 }
492
493 DNSName DNSName::getLastLabel() const
494 {
495 DNSName ret(*this);
496 ret.trimToLabels(1);
497 return ret;
498 }
499
500 bool DNSName::chopOff()
501 {
502 if (d_storage.empty() || d_storage[0]==0) {
503 return false;
504 }
505 d_storage.erase(0, (unsigned int)d_storage[0]+1);
506 return true;
507 }
508
509 bool DNSName::isWildcard() const
510 {
511 if (d_storage.size() < 2) {
512 return false;
513 }
514 auto p = d_storage.begin();
515 return (*p == 0x01 && *++p == '*');
516 }
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 {
524 static Regex hostNameRegex = Regex("^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\\.)+$");
525 return hostNameRegex.match(this->toString());
526 }
527
528 unsigned int DNSName::countLabels() const
529 {
530 unsigned int count=0;
531 const unsigned char* p = reinterpret_cast<const unsigned char*>(d_storage.c_str());
532 const unsigned char* end = reinterpret_cast<const unsigned char*>(p + d_storage.size());
533
534 while (p < end && *p) {
535 ++count;
536 p += *p + 1;
537 }
538 return count;
539 }
540
541 void DNSName::trimToLabels(unsigned int to)
542 {
543 while(countLabels() > to && chopOff()) {
544 ;
545 }
546 }
547
548
549 size_t hash_value(DNSName const& d)
550 {
551 return d.hash();
552 }
553
554 void DNSName::appendEscapedLabel(std::string& appendTo, const char* orig, size_t len)
555 {
556 size_t pos = 0;
557
558 while (pos < len) {
559 auto p = static_cast<uint8_t>(orig[pos]);
560 if (p=='.') {
561 appendTo+="\\.";
562 }
563 else if (p=='\\') {
564 appendTo+="\\\\";
565 }
566 else if (p > 0x20 && p < 0x7f) {
567 appendTo.append(1, static_cast<char>(p));
568 }
569 else {
570 char buf[] = "000";
571 auto got = snprintf(buf, sizeof(buf), "%03" PRIu8, p);
572 if (got < 0 || static_cast<size_t>(got) >= sizeof(buf)) {
573 throw std::runtime_error("Error, snprintf returned " + std::to_string(got) + " while escaping label " + std::string(orig, len));
574 }
575 appendTo.append(1, '\\');
576 appendTo += buf;
577 }
578 ++pos;
579 }
580 }
581
582 bool DNSName::has8bitBytes() const
583 {
584 const auto& s = d_storage;
585 string::size_type pos = 0;
586 uint8_t length = s.at(pos);
587 while (length > 0) {
588 for (size_t idx = 0; idx < length; idx++) {
589 ++pos;
590 char c = s.at(pos);
591 if (!((c >= 'a' && c <= 'z') ||
592 (c >= 'A' && c <= 'Z') ||
593 (c >= '0' && c <= '9') ||
594 c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@' || c==' ' || c=='\\' || c==':')) {
595 return true;
596 }
597 }
598 ++pos;
599 length = s.at(pos);
600 }
601
602 return false;
603 }
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 {
678 size_t position = 0;
679 while (position < storage.size()) {
680 auto labelLength = static_cast<uint8_t>(storage.at(position));
681 if (labelLength == 0) {
682 break;
683 }
684 d_labelPositions.at(d_position) = position;
685 d_position++;
686 position += labelLength + 1;
687 }
688 }
689
690 DNSName::RawLabelsVisitor DNSName::getRawLabelsVisitor() const
691 {
692 return DNSName::RawLabelsVisitor(getStorage());
693 }
694
695 std::string_view DNSName::RawLabelsVisitor::front() const
696 {
697 if (d_position == 0) {
698 throw std::out_of_range("trying to access the front of an empty DNSName::RawLabelsVisitor");
699 }
700 uint8_t length = d_storage.at(0);
701 if (length == 0) {
702 return std::string_view();
703 }
704 return std::string_view(&d_storage.at(1), length);
705 }
706
707 std::string_view DNSName::RawLabelsVisitor::back() const
708 {
709 if (d_position == 0) {
710 throw std::out_of_range("trying to access the back of an empty DNSName::RawLabelsVisitor");
711 }
712 size_t offset = d_labelPositions.at(d_position-1);
713 uint8_t length = d_storage.at(offset);
714 if (length == 0) {
715 return std::string_view();
716 }
717 return std::string_view(&d_storage.at(offset + 1), length);
718 }
719
720 bool DNSName::RawLabelsVisitor::pop_back()
721 {
722 if (d_position > 0) {
723 d_position--;
724 return true;
725 }
726 return false;
727 }
728
729 bool DNSName::RawLabelsVisitor::empty() const
730 {
731 return d_position == 0;
732 }