57 if(scan_name.find(
'(') == std::string::npos && scan_name.find(
')') == std::string::npos) {
58 return {std::string(scan_name)};
61 std::string name(scan_name);
62 std::string substring;
63 std::vector<std::string> elems;
66 elems.push_back(name.substr(0, name.find(
'(')));
67 name = name.substr(name.find(
'('));
69 for(
auto i = name.begin(); i != name.end(); ++i) {
76 if(level == 1 && i == name.end() - 1) {
77 if(elems.size() == 1) {
78 elems.push_back(substring.substr(1));
80 elems.push_back(substring);
85 if(level == 0 || (level == 1 && i != name.end() - 1)) {
91 if(c ==
',' && level == 1) {
92 if(elems.size() == 1) {
93 elems.push_back(substring.substr(1));
95 elems.push_back(substring);
103 if(!substring.empty()) {
228 std::array<uint16_t, 8> pre{};
229 std::array<uint16_t, 8> post{};
230 size_t pre_count = 0;
231 size_t post_count = 0;
232 bool seen_double_colon =
false;
234 auto hex_value = [](
char c) -> std::optional<uint8_t> {
235 if(c >=
'0' && c <=
'9') {
237 }
else if(c >=
'a' && c <=
'f') {
238 return 10 + (c -
'a');
239 }
else if(c >=
'A' && c <=
'F') {
240 return 10 + (c -
'A');
247 bool expect_group =
true;
249 while(idx < str.size()) {
250 if(str[idx] ==
':') {
251 if(idx + 1 < str.size() && str[idx + 1] ==
':') {
252 if(seen_double_colon) {
255 seen_double_colon =
true;
257 expect_group = (idx < str.size());
271 size_t hex_chars = 0;
272 while(idx < str.size() && hex_chars < 4) {
273 const auto digit = hex_value(str[idx]);
274 if(digit.has_value() ==
false) {
277 group = (group << 4) | static_cast<uint32_t>(digit.value());
285 if(hex_chars == 4 && idx < str.size() && hex_value(str[idx]).has_value()) {
289 if(seen_double_colon) {
290 if(post_count >= 8) {
293 post[post_count++] =
static_cast<uint16_t
>(group);
298 pre[pre_count++] =
static_cast<uint16_t
>(group);
300 expect_group =
false;
308 const size_t total_groups = pre_count + post_count;
309 if(seen_double_colon) {
311 if(total_groups > 7) {
315 if(total_groups != 8) {
320 std::array<uint8_t, 16> out{};
321 for(
size_t i = 0; i != pre_count; ++i) {
325 const size_t gap = 8 - total_groups;
326 for(
size_t i = 0; i != post_count; ++i) {
327 const size_t target = pre_count + gap + i;
335 static const char* hex =
"0123456789abcdef";
340 for(
size_t i = 0; i != 16; i += 2) {
344 const uint16_t group =
make_uint16(a[i], a[i + 1]);
345 bool started =
false;
347 for(
int s = 12; s >= 0; s -= 4) {
348 const auto nibble = (group >> s) & 0xF;
349 if(nibble != 0 || started || s == 0) {
350 out.push_back(hex[nibble]);
390 if(host.empty() || issued.empty()) {
395 if(host.size() > 253) {
403 if(issued.size() > host.size() + 1) {
411 if(issued.find(
'\0') != std::string_view::npos) {
416 if(host.find(
'*') != std::string_view::npos) {
421 if(host.back() ==
'.') {
426 if(host.find(
"..") != std::string_view::npos) {
431 auto dns_char_eq = [](
char a,
char b) ->
bool {
435 const auto la =
static_cast<unsigned char>(a | 0x20);
436 const auto lb =
static_cast<unsigned char>(b | 0x20);
437 return la == lb && la >=
'a' && la <=
'z';
440 auto dns_char_eq_range = [&](std::string_view a, std::string_view b) ->
bool {
441 if(a.size() != b.size()) {
444 for(
size_t i = 0; i != a.size(); ++i) {
445 if(!dns_char_eq(a[i], b[i])) {
453 if(dns_char_eq_range(issued, host)) {
458 const size_t first_star = issued.find(
'*');
459 const bool has_wildcard = (first_star != std::string_view::npos);
462 if(has_wildcard && issued.find(
'*', first_star + 1) != std::string_view::npos) {
481 size_t dots_seen = 0;
484 for(
size_t i = 0; i != issued.size(); ++i) {
485 if(issued[i] ==
'.') {
489 if(issued[i] ==
'*') {
500 const size_t advance = (host.size() - issued.size() + 1);
502 if(host_idx + advance > host.size()) {
507 for(
size_t k = host_idx; k != host_idx + advance; ++k) {
515 if(!dns_char_eq(issued[i], host[host_idx])) {
532 if(name.size() > 255) {
540 if(name.starts_with(
".") || name.ends_with(
".")) {
549 constexpr uint8_t DNS_CHAR_MAPPING[128] = {
550 '\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
551 '\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
552 '\0',
'\0',
'\0',
'\0',
'*',
'\0',
'\0',
'-',
'.',
'\0',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
553 '9',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
554 'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'\0',
'\0',
'\0',
'\0',
555 '\0',
'\0',
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
556 'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'\0',
'\0',
'\0',
'\0',
'\0',
561 canon.reserve(name.size());
564 size_t current_label_length = 0;
566 for(
size_t i = 0; i != name.size(); ++i) {
567 const char c = name[i];
570 if(i > 0 && name[i - 1] ==
'.') {
571 throw Decoding_Error(
"DNS name contains sequential period chars");
574 if(current_label_length == 0) {
577 current_label_length = 0;
579 current_label_length++;
581 if(current_label_length > 63) {
582 throw Decoding_Error(
"DNS name label exceeds maximum length of 63 characters");
586 const uint8_t cu =
static_cast<uint8_t
>(c);
588 throw Decoding_Error(
"DNS name must not contain any extended ASCII code points");
590 const uint8_t mapped = DNS_CHAR_MAPPING[cu];
596 if(i == 0 || (i > 0 && name[i - 1] ==
'.')) {
598 }
else if(i == name.size() - 1 || (i < name.size() - 1 && name[i + 1] ==
'.')) {
602 canon.push_back(
static_cast<char>(mapped));
605 if(current_label_length == 0) {