]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/dns-domain.c
tree-wide: drop string.h when string-util.h or friends are included
[thirdparty/systemd.git] / src / shared / dns-domain.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #if HAVE_LIBIDN2
4 # include <idn2.h>
5 #elif HAVE_LIBIDN
6 # include <idna.h>
7 # include <stringprep.h>
8 #endif
9
10 #include <endian.h>
11 #include <netinet/in.h>
12 #include <stdio.h>
13 #include <sys/socket.h>
14
15 #include "alloc-util.h"
16 #include "dns-domain.h"
17 #include "hashmap.h"
18 #include "hexdecoct.h"
19 #include "hostname-util.h"
20 #include "in-addr-util.h"
21 #include "macro.h"
22 #include "parse-util.h"
23 #include "string-util.h"
24 #include "strv.h"
25 #include "utf8.h"
26
27 int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
28 const char *n;
29 char *d, last_char = 0;
30 int r = 0;
31
32 assert(name);
33 assert(*name);
34
35 n = *name;
36 d = dest;
37
38 for (;;) {
39 if (IN_SET(*n, 0, '.')) {
40 if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
41 /* Trailing dash */
42 return -EINVAL;
43
44 if (*n == '.')
45 n++;
46 break;
47 }
48
49 if (r >= DNS_LABEL_MAX)
50 return -EINVAL;
51
52 if (sz <= 0)
53 return -ENOBUFS;
54
55 if (*n == '\\') {
56 /* Escaped character */
57 if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
58 return -EINVAL;
59
60 n++;
61
62 if (*n == 0)
63 /* Ending NUL */
64 return -EINVAL;
65
66 else if (IN_SET(*n, '\\', '.')) {
67 /* Escaped backslash or dot */
68
69 if (FLAGS_SET(flags, DNS_LABEL_LDH))
70 return -EINVAL;
71
72 last_char = *n;
73 if (d)
74 *(d++) = *n;
75 sz--;
76 r++;
77 n++;
78
79 } else if (n[0] >= '0' && n[0] <= '9') {
80 unsigned k;
81
82 /* Escaped literal ASCII character */
83
84 if (!(n[1] >= '0' && n[1] <= '9') ||
85 !(n[2] >= '0' && n[2] <= '9'))
86 return -EINVAL;
87
88 k = ((unsigned) (n[0] - '0') * 100) +
89 ((unsigned) (n[1] - '0') * 10) +
90 ((unsigned) (n[2] - '0'));
91
92 /* Don't allow anything that doesn't
93 * fit in 8bit. Note that we do allow
94 * control characters, as some servers
95 * (e.g. cloudflare) are happy to
96 * generate labels with them
97 * inside. */
98 if (k > 255)
99 return -EINVAL;
100
101 if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
102 !valid_ldh_char((char) k))
103 return -EINVAL;
104
105 last_char = (char) k;
106 if (d)
107 *(d++) = (char) k;
108 sz--;
109 r++;
110
111 n += 3;
112 } else
113 return -EINVAL;
114
115 } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
116
117 /* Normal character */
118
119 if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
120 if (!valid_ldh_char(*n))
121 return -EINVAL;
122 if (r == 0 && *n == '-')
123 /* Leading dash */
124 return -EINVAL;
125 }
126
127 last_char = *n;
128 if (d)
129 *(d++) = *n;
130 sz--;
131 r++;
132 n++;
133 } else
134 return -EINVAL;
135 }
136
137 /* Empty label that is not at the end? */
138 if (r == 0 && *n)
139 return -EINVAL;
140
141 /* More than one trailing dot? */
142 if (*n == '.')
143 return -EINVAL;
144
145 if (sz >= 1 && d)
146 *d = 0;
147
148 *name = n;
149 return r;
150 }
151
152 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
153 * the previous label (always skipping one dot) or to NULL if there are no more
154 * labels. */
155 int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
156 const char *terminal;
157 int r;
158
159 assert(name);
160 assert(label_terminal);
161 assert(dest);
162
163 /* no more labels */
164 if (!*label_terminal) {
165 if (sz >= 1)
166 *dest = 0;
167
168 return 0;
169 }
170
171 terminal = *label_terminal;
172 assert(IN_SET(*terminal, 0, '.'));
173
174 /* Skip current terminal character (and accept domain names ending it ".") */
175 if (*terminal == 0)
176 terminal--;
177 if (terminal >= name && *terminal == '.')
178 terminal--;
179
180 /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
181 for (;;) {
182 if (terminal < name) {
183 /* Reached the first label, so indicate that there are no more */
184 terminal = NULL;
185 break;
186 }
187
188 /* Find the start of the last label */
189 if (*terminal == '.') {
190 const char *y;
191 unsigned slashes = 0;
192
193 for (y = terminal - 1; y >= name && *y == '\\'; y--)
194 slashes++;
195
196 if (slashes % 2 == 0) {
197 /* The '.' was not escaped */
198 name = terminal + 1;
199 break;
200 } else {
201 terminal = y;
202 continue;
203 }
204 }
205
206 terminal--;
207 }
208
209 r = dns_label_unescape(&name, dest, sz, 0);
210 if (r < 0)
211 return r;
212
213 *label_terminal = terminal;
214
215 return r;
216 }
217
218 int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
219 char *q;
220
221 /* DNS labels must be between 1 and 63 characters long. A
222 * zero-length label does not exist. See RFC 2182, Section
223 * 11. */
224
225 if (l <= 0 || l > DNS_LABEL_MAX)
226 return -EINVAL;
227 if (sz < 1)
228 return -ENOBUFS;
229
230 assert(p);
231 assert(dest);
232
233 q = dest;
234 while (l > 0) {
235
236 if (IN_SET(*p, '.', '\\')) {
237
238 /* Dot or backslash */
239
240 if (sz < 3)
241 return -ENOBUFS;
242
243 *(q++) = '\\';
244 *(q++) = *p;
245
246 sz -= 2;
247
248 } else if (IN_SET(*p, '_', '-') ||
249 (*p >= '0' && *p <= '9') ||
250 (*p >= 'a' && *p <= 'z') ||
251 (*p >= 'A' && *p <= 'Z')) {
252
253 /* Proper character */
254
255 if (sz < 2)
256 return -ENOBUFS;
257
258 *(q++) = *p;
259 sz -= 1;
260
261 } else {
262
263 /* Everything else */
264
265 if (sz < 5)
266 return -ENOBUFS;
267
268 *(q++) = '\\';
269 *(q++) = '0' + (char) ((uint8_t) *p / 100);
270 *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
271 *(q++) = '0' + (char) ((uint8_t) *p % 10);
272
273 sz -= 4;
274 }
275
276 p++;
277 l--;
278 }
279
280 *q = 0;
281 return (int) (q - dest);
282 }
283
284 int dns_label_escape_new(const char *p, size_t l, char **ret) {
285 _cleanup_free_ char *s = NULL;
286 int r;
287
288 assert(p);
289 assert(ret);
290
291 if (l <= 0 || l > DNS_LABEL_MAX)
292 return -EINVAL;
293
294 s = new(char, DNS_LABEL_ESCAPED_MAX);
295 if (!s)
296 return -ENOMEM;
297
298 r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
299 if (r < 0)
300 return r;
301
302 *ret = TAKE_PTR(s);
303
304 return r;
305 }
306
307 #if HAVE_LIBIDN
308 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
309 _cleanup_free_ uint32_t *input = NULL;
310 size_t input_size, l;
311 const char *p;
312 bool contains_8bit = false;
313 char buffer[DNS_LABEL_MAX+1];
314
315 assert(encoded);
316 assert(decoded);
317
318 /* Converts an U-label into an A-label */
319
320 if (encoded_size <= 0)
321 return -EINVAL;
322
323 for (p = encoded; p < encoded + encoded_size; p++)
324 if ((uint8_t) *p > 127)
325 contains_8bit = true;
326
327 if (!contains_8bit) {
328 if (encoded_size > DNS_LABEL_MAX)
329 return -EINVAL;
330
331 return 0;
332 }
333
334 input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
335 if (!input)
336 return -ENOMEM;
337
338 if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0)
339 return -EINVAL;
340
341 l = strlen(buffer);
342
343 /* Verify that the result is not longer than one DNS label. */
344 if (l <= 0 || l > DNS_LABEL_MAX)
345 return -EINVAL;
346 if (l > decoded_max)
347 return -ENOBUFS;
348
349 memcpy(decoded, buffer, l);
350
351 /* If there's room, append a trailing NUL byte, but only then */
352 if (decoded_max > l)
353 decoded[l] = 0;
354
355 return (int) l;
356 }
357
358 int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
359 size_t input_size, output_size;
360 _cleanup_free_ uint32_t *input = NULL;
361 _cleanup_free_ char *result = NULL;
362 uint32_t *output = NULL;
363 size_t w;
364
365 /* To be invoked after unescaping. Converts an A-label into an U-label. */
366
367 assert(encoded);
368 assert(decoded);
369
370 if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX)
371 return -EINVAL;
372
373 if (!memory_startswith(encoded, encoded_size, IDNA_ACE_PREFIX))
374 return 0;
375
376 input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
377 if (!input)
378 return -ENOMEM;
379
380 output_size = input_size;
381 output = newa(uint32_t, output_size);
382
383 idna_to_unicode_44i(input, input_size, output, &output_size, 0);
384
385 result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
386 if (!result)
387 return -ENOMEM;
388 if (w <= 0)
389 return -EINVAL;
390 if (w > decoded_max)
391 return -ENOBUFS;
392
393 memcpy(decoded, result, w);
394
395 /* Append trailing NUL byte if there's space, but only then. */
396 if (decoded_max > w)
397 decoded[w] = 0;
398
399 return w;
400 }
401 #endif
402
403 int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
404 _cleanup_free_ char *ret = NULL;
405 size_t n = 0, allocated = 0;
406 const char *p;
407 bool first = true;
408 int r;
409
410 if (a)
411 p = a;
412 else if (b)
413 p = TAKE_PTR(b);
414 else
415 goto finish;
416
417 for (;;) {
418 char label[DNS_LABEL_MAX];
419
420 r = dns_label_unescape(&p, label, sizeof label, flags);
421 if (r < 0)
422 return r;
423 if (r == 0) {
424 if (*p != 0)
425 return -EINVAL;
426
427 if (b) {
428 /* Now continue with the second string, if there is one */
429 p = TAKE_PTR(b);
430 continue;
431 }
432
433 break;
434 }
435
436 if (_ret) {
437 if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
438 return -ENOMEM;
439
440 r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
441 if (r < 0)
442 return r;
443
444 if (!first)
445 ret[n] = '.';
446 } else {
447 char escaped[DNS_LABEL_ESCAPED_MAX];
448
449 r = dns_label_escape(label, r, escaped, sizeof(escaped));
450 if (r < 0)
451 return r;
452 }
453
454 if (!first)
455 n++;
456 else
457 first = false;
458
459 n += r;
460 }
461
462 finish:
463 if (n > DNS_HOSTNAME_MAX)
464 return -EINVAL;
465
466 if (_ret) {
467 if (n == 0) {
468 /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
469 if (!GREEDY_REALLOC(ret, allocated, 2))
470 return -ENOMEM;
471
472 ret[n++] = '.';
473 } else {
474 if (!GREEDY_REALLOC(ret, allocated, n + 1))
475 return -ENOMEM;
476 }
477
478 ret[n] = 0;
479 *_ret = TAKE_PTR(ret);
480 }
481
482 return 0;
483 }
484
485 void dns_name_hash_func(const char *p, struct siphash *state) {
486 int r;
487
488 assert(p);
489
490 for (;;) {
491 char label[DNS_LABEL_MAX+1];
492
493 r = dns_label_unescape(&p, label, sizeof label, 0);
494 if (r < 0)
495 break;
496 if (r == 0)
497 break;
498
499 ascii_strlower_n(label, r);
500 siphash24_compress(label, r, state);
501 siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */
502 }
503
504 /* enforce that all names are terminated by the empty label */
505 string_hash_func("", state);
506 }
507
508 int dns_name_compare_func(const char *a, const char *b) {
509 const char *x, *y;
510 int r, q;
511
512 assert(a);
513 assert(b);
514
515 x = a + strlen(a);
516 y = b + strlen(b);
517
518 for (;;) {
519 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
520
521 if (x == NULL && y == NULL)
522 return 0;
523
524 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
525 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
526 if (r < 0 || q < 0)
527 return CMP(r, q);
528
529 r = ascii_strcasecmp_nn(la, r, lb, q);
530 if (r != 0)
531 return r;
532 }
533 }
534
535 DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
536
537 int dns_name_equal(const char *x, const char *y) {
538 int r, q;
539
540 assert(x);
541 assert(y);
542
543 for (;;) {
544 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
545
546 r = dns_label_unescape(&x, la, sizeof la, 0);
547 if (r < 0)
548 return r;
549
550 q = dns_label_unescape(&y, lb, sizeof lb, 0);
551 if (q < 0)
552 return q;
553
554 if (r != q)
555 return false;
556 if (r == 0)
557 return true;
558
559 if (ascii_strcasecmp_n(la, lb, r) != 0)
560 return false;
561 }
562 }
563
564 int dns_name_endswith(const char *name, const char *suffix) {
565 const char *n, *s, *saved_n = NULL;
566 int r, q;
567
568 assert(name);
569 assert(suffix);
570
571 n = name;
572 s = suffix;
573
574 for (;;) {
575 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
576
577 r = dns_label_unescape(&n, ln, sizeof ln, 0);
578 if (r < 0)
579 return r;
580
581 if (!saved_n)
582 saved_n = n;
583
584 q = dns_label_unescape(&s, ls, sizeof ls, 0);
585 if (q < 0)
586 return q;
587
588 if (r == 0 && q == 0)
589 return true;
590 if (r == 0 && saved_n == n)
591 return false;
592
593 if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
594
595 /* Not the same, let's jump back, and try with the next label again */
596 s = suffix;
597 n = TAKE_PTR(saved_n);
598 }
599 }
600 }
601
602 int dns_name_startswith(const char *name, const char *prefix) {
603 const char *n, *p;
604 int r, q;
605
606 assert(name);
607 assert(prefix);
608
609 n = name;
610 p = prefix;
611
612 for (;;) {
613 char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
614
615 r = dns_label_unescape(&p, lp, sizeof lp, 0);
616 if (r < 0)
617 return r;
618 if (r == 0)
619 return true;
620
621 q = dns_label_unescape(&n, ln, sizeof ln, 0);
622 if (q < 0)
623 return q;
624
625 if (r != q)
626 return false;
627 if (ascii_strcasecmp_n(ln, lp, r) != 0)
628 return false;
629 }
630 }
631
632 int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
633 const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
634 int r, q;
635
636 assert(name);
637 assert(old_suffix);
638 assert(new_suffix);
639 assert(ret);
640
641 n = name;
642 s = old_suffix;
643
644 for (;;) {
645 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
646
647 if (!saved_before)
648 saved_before = n;
649
650 r = dns_label_unescape(&n, ln, sizeof ln, 0);
651 if (r < 0)
652 return r;
653
654 if (!saved_after)
655 saved_after = n;
656
657 q = dns_label_unescape(&s, ls, sizeof ls, 0);
658 if (q < 0)
659 return q;
660
661 if (r == 0 && q == 0)
662 break;
663 if (r == 0 && saved_after == n) {
664 *ret = NULL; /* doesn't match */
665 return 0;
666 }
667
668 if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
669
670 /* Not the same, let's jump back, and try with the next label again */
671 s = old_suffix;
672 n = TAKE_PTR(saved_after);
673 saved_before = NULL;
674 }
675 }
676
677 /* Found it! Now generate the new name */
678 prefix = strndupa(name, saved_before - name);
679
680 r = dns_name_concat(prefix, new_suffix, 0, ret);
681 if (r < 0)
682 return r;
683
684 return 1;
685 }
686
687 int dns_name_between(const char *a, const char *b, const char *c) {
688 /* Determine if b is strictly greater than a and strictly smaller than c.
689 We consider the order of names to be circular, so that if a is
690 strictly greater than c, we consider b to be between them if it is
691 either greater than a or smaller than c. This is how the canonical
692 DNS name order used in NSEC records work. */
693
694 if (dns_name_compare_func(a, c) < 0)
695 /*
696 a and c are properly ordered:
697 a<---b--->c
698 */
699 return dns_name_compare_func(a, b) < 0 &&
700 dns_name_compare_func(b, c) < 0;
701 else
702 /*
703 a and c are equal or 'reversed':
704 <--b--c a----->
705 or:
706 <-----c a--b-->
707 */
708 return dns_name_compare_func(b, c) < 0 ||
709 dns_name_compare_func(a, b) < 0;
710 }
711
712 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
713 const uint8_t *p;
714 int r;
715
716 assert(a);
717 assert(ret);
718
719 p = (const uint8_t*) a;
720
721 if (family == AF_INET)
722 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
723 else if (family == AF_INET6)
724 r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa",
725 hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
726 hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
727 hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
728 hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
729 hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
730 hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
731 hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
732 hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
733 else
734 return -EAFNOSUPPORT;
735 if (r < 0)
736 return -ENOMEM;
737
738 return 0;
739 }
740
741 int dns_name_address(const char *p, int *family, union in_addr_union *address) {
742 int r;
743
744 assert(p);
745 assert(family);
746 assert(address);
747
748 r = dns_name_endswith(p, "in-addr.arpa");
749 if (r < 0)
750 return r;
751 if (r > 0) {
752 uint8_t a[4];
753 unsigned i;
754
755 for (i = 0; i < ELEMENTSOF(a); i++) {
756 char label[DNS_LABEL_MAX+1];
757
758 r = dns_label_unescape(&p, label, sizeof label, 0);
759 if (r < 0)
760 return r;
761 if (r == 0)
762 return -EINVAL;
763 if (r > 3)
764 return -EINVAL;
765
766 r = safe_atou8(label, &a[i]);
767 if (r < 0)
768 return r;
769 }
770
771 r = dns_name_equal(p, "in-addr.arpa");
772 if (r <= 0)
773 return r;
774
775 *family = AF_INET;
776 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
777 ((uint32_t) a[2] << 16) |
778 ((uint32_t) a[1] << 8) |
779 (uint32_t) a[0]);
780
781 return 1;
782 }
783
784 r = dns_name_endswith(p, "ip6.arpa");
785 if (r < 0)
786 return r;
787 if (r > 0) {
788 struct in6_addr a;
789 unsigned i;
790
791 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
792 char label[DNS_LABEL_MAX+1];
793 int x, y;
794
795 r = dns_label_unescape(&p, label, sizeof label, 0);
796 if (r <= 0)
797 return r;
798 if (r != 1)
799 return -EINVAL;
800 x = unhexchar(label[0]);
801 if (x < 0)
802 return -EINVAL;
803
804 r = dns_label_unescape(&p, label, sizeof label, 0);
805 if (r <= 0)
806 return r;
807 if (r != 1)
808 return -EINVAL;
809 y = unhexchar(label[0]);
810 if (y < 0)
811 return -EINVAL;
812
813 a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
814 }
815
816 r = dns_name_equal(p, "ip6.arpa");
817 if (r <= 0)
818 return r;
819
820 *family = AF_INET6;
821 address->in6 = a;
822 return 1;
823 }
824
825 return 0;
826 }
827
828 bool dns_name_is_root(const char *name) {
829
830 assert(name);
831
832 /* There are exactly two ways to encode the root domain name:
833 * as empty string, or with a single dot. */
834
835 return STR_IN_SET(name, "", ".");
836 }
837
838 bool dns_name_is_single_label(const char *name) {
839 int r;
840
841 assert(name);
842
843 r = dns_name_parent(&name);
844 if (r <= 0)
845 return false;
846
847 return dns_name_is_root(name);
848 }
849
850 /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
851 int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
852 uint8_t *label_length, *out;
853 int r;
854
855 assert(domain);
856 assert(buffer);
857
858 out = buffer;
859
860 do {
861 /* Reserve a byte for label length */
862 if (len <= 0)
863 return -ENOBUFS;
864 len--;
865 label_length = out;
866 out++;
867
868 /* Convert and copy a single label. Note that
869 * dns_label_unescape() returns 0 when it hits the end
870 * of the domain name, which we rely on here to encode
871 * the trailing NUL byte. */
872 r = dns_label_unescape(&domain, (char *) out, len, 0);
873 if (r < 0)
874 return r;
875
876 /* Optionally, output the name in DNSSEC canonical
877 * format, as described in RFC 4034, section 6.2. Or
878 * in other words: in lower-case. */
879 if (canonical)
880 ascii_strlower_n((char*) out, (size_t) r);
881
882 /* Fill label length, move forward */
883 *label_length = r;
884 out += r;
885 len -= r;
886
887 } while (r != 0);
888
889 /* Verify the maximum size of the encoded name. The trailing
890 * dot + NUL byte account are included this time, hence
891 * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
892 * time. */
893 if (out - buffer > DNS_HOSTNAME_MAX + 2)
894 return -EINVAL;
895
896 return out - buffer;
897 }
898
899 static bool srv_type_label_is_valid(const char *label, size_t n) {
900 size_t k;
901
902 assert(label);
903
904 if (n < 2) /* Label needs to be at least 2 chars long */
905 return false;
906
907 if (label[0] != '_') /* First label char needs to be underscore */
908 return false;
909
910 /* Second char must be a letter */
911 if (!(label[1] >= 'A' && label[1] <= 'Z') &&
912 !(label[1] >= 'a' && label[1] <= 'z'))
913 return false;
914
915 /* Third and further chars must be alphanumeric or a hyphen */
916 for (k = 2; k < n; k++) {
917 if (!(label[k] >= 'A' && label[k] <= 'Z') &&
918 !(label[k] >= 'a' && label[k] <= 'z') &&
919 !(label[k] >= '0' && label[k] <= '9') &&
920 label[k] != '-')
921 return false;
922 }
923
924 return true;
925 }
926
927 bool dns_srv_type_is_valid(const char *name) {
928 unsigned c = 0;
929 int r;
930
931 if (!name)
932 return false;
933
934 for (;;) {
935 char label[DNS_LABEL_MAX];
936
937 /* This more or less implements RFC 6335, Section 5.1 */
938
939 r = dns_label_unescape(&name, label, sizeof label, 0);
940 if (r < 0)
941 return false;
942 if (r == 0)
943 break;
944
945 if (c >= 2)
946 return false;
947
948 if (!srv_type_label_is_valid(label, r))
949 return false;
950
951 c++;
952 }
953
954 return c == 2; /* exactly two labels */
955 }
956
957 bool dnssd_srv_type_is_valid(const char *name) {
958 return dns_srv_type_is_valid(name) &&
959 ((dns_name_endswith(name, "_tcp") > 0) ||
960 (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */
961 }
962
963 bool dns_service_name_is_valid(const char *name) {
964 size_t l;
965
966 /* This more or less implements RFC 6763, Section 4.1.1 */
967
968 if (!name)
969 return false;
970
971 if (!utf8_is_valid(name))
972 return false;
973
974 if (string_has_cc(name, NULL))
975 return false;
976
977 l = strlen(name);
978 if (l <= 0)
979 return false;
980 if (l > 63)
981 return false;
982
983 return true;
984 }
985
986 int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
987 char escaped[DNS_LABEL_ESCAPED_MAX];
988 _cleanup_free_ char *n = NULL;
989 int r;
990
991 assert(type);
992 assert(domain);
993 assert(ret);
994
995 if (!dns_srv_type_is_valid(type))
996 return -EINVAL;
997
998 if (!name)
999 return dns_name_concat(type, domain, 0, ret);
1000
1001 if (!dns_service_name_is_valid(name))
1002 return -EINVAL;
1003
1004 r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
1005 if (r < 0)
1006 return r;
1007
1008 r = dns_name_concat(type, domain, 0, &n);
1009 if (r < 0)
1010 return r;
1011
1012 return dns_name_concat(escaped, n, 0, ret);
1013 }
1014
1015 static bool dns_service_name_label_is_valid(const char *label, size_t n) {
1016 char *s;
1017
1018 assert(label);
1019
1020 if (memchr(label, 0, n))
1021 return false;
1022
1023 s = strndupa(label, n);
1024 return dns_service_name_is_valid(s);
1025 }
1026
1027 int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
1028 _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
1029 const char *p = joined, *q = NULL, *d = NULL;
1030 char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
1031 int an, bn, cn, r;
1032 unsigned x = 0;
1033
1034 assert(joined);
1035
1036 /* Get first label from the full name */
1037 an = dns_label_unescape(&p, a, sizeof(a), 0);
1038 if (an < 0)
1039 return an;
1040
1041 if (an > 0) {
1042 x++;
1043
1044 /* If there was a first label, try to get the second one */
1045 bn = dns_label_unescape(&p, b, sizeof(b), 0);
1046 if (bn < 0)
1047 return bn;
1048
1049 if (bn > 0) {
1050 x++;
1051
1052 /* If there was a second label, try to get the third one */
1053 q = p;
1054 cn = dns_label_unescape(&p, c, sizeof(c), 0);
1055 if (cn < 0)
1056 return cn;
1057
1058 if (cn > 0)
1059 x++;
1060 } else
1061 cn = 0;
1062 } else
1063 an = 0;
1064
1065 if (x >= 2 && srv_type_label_is_valid(b, bn)) {
1066
1067 if (x >= 3 && srv_type_label_is_valid(c, cn)) {
1068
1069 if (dns_service_name_label_is_valid(a, an)) {
1070 /* OK, got <name> . <type> . <type2> . <domain> */
1071
1072 name = strndup(a, an);
1073 if (!name)
1074 return -ENOMEM;
1075
1076 type = strjoin(b, ".", c);
1077 if (!type)
1078 return -ENOMEM;
1079
1080 d = p;
1081 goto finish;
1082 }
1083
1084 } else if (srv_type_label_is_valid(a, an)) {
1085
1086 /* OK, got <type> . <type2> . <domain> */
1087
1088 name = NULL;
1089
1090 type = strjoin(a, ".", b);
1091 if (!type)
1092 return -ENOMEM;
1093
1094 d = q;
1095 goto finish;
1096 }
1097 }
1098
1099 name = NULL;
1100 type = NULL;
1101 d = joined;
1102
1103 finish:
1104 r = dns_name_normalize(d, 0, &domain);
1105 if (r < 0)
1106 return r;
1107
1108 if (_domain)
1109 *_domain = TAKE_PTR(domain);
1110
1111 if (_type)
1112 *_type = TAKE_PTR(type);
1113
1114 if (_name)
1115 *_name = TAKE_PTR(name);
1116
1117 return 0;
1118 }
1119
1120 static int dns_name_build_suffix_table(const char *name, const char *table[]) {
1121 const char *p;
1122 unsigned n = 0;
1123 int r;
1124
1125 assert(name);
1126 assert(table);
1127
1128 p = name;
1129 for (;;) {
1130 if (n > DNS_N_LABELS_MAX)
1131 return -EINVAL;
1132
1133 table[n] = p;
1134 r = dns_name_parent(&p);
1135 if (r < 0)
1136 return r;
1137 if (r == 0)
1138 break;
1139
1140 n++;
1141 }
1142
1143 return (int) n;
1144 }
1145
1146 int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
1147 const char* labels[DNS_N_LABELS_MAX+1];
1148 int n;
1149
1150 assert(name);
1151 assert(ret);
1152
1153 n = dns_name_build_suffix_table(name, labels);
1154 if (n < 0)
1155 return n;
1156
1157 if ((unsigned) n < n_labels)
1158 return -EINVAL;
1159
1160 *ret = labels[n - n_labels];
1161 return (int) (n - n_labels);
1162 }
1163
1164 int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
1165 int r;
1166
1167 assert(a);
1168 assert(ret);
1169
1170 for (; n_labels > 0; n_labels--) {
1171 r = dns_name_parent(&a);
1172 if (r < 0)
1173 return r;
1174 if (r == 0) {
1175 *ret = "";
1176 return 0;
1177 }
1178 }
1179
1180 *ret = a;
1181 return 1;
1182 }
1183
1184 int dns_name_count_labels(const char *name) {
1185 unsigned n = 0;
1186 const char *p;
1187 int r;
1188
1189 assert(name);
1190
1191 p = name;
1192 for (;;) {
1193 r = dns_name_parent(&p);
1194 if (r < 0)
1195 return r;
1196 if (r == 0)
1197 break;
1198
1199 if (n >= DNS_N_LABELS_MAX)
1200 return -EINVAL;
1201
1202 n++;
1203 }
1204
1205 return (int) n;
1206 }
1207
1208 int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
1209 int r;
1210
1211 assert(a);
1212 assert(b);
1213
1214 r = dns_name_skip(a, n_labels, &a);
1215 if (r <= 0)
1216 return r;
1217
1218 return dns_name_equal(a, b);
1219 }
1220
1221 int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
1222 const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1];
1223 int n = 0, m = 0, k = 0, r, q;
1224
1225 assert(a);
1226 assert(b);
1227 assert(ret);
1228
1229 /* Determines the common suffix of domain names a and b */
1230
1231 n = dns_name_build_suffix_table(a, a_labels);
1232 if (n < 0)
1233 return n;
1234
1235 m = dns_name_build_suffix_table(b, b_labels);
1236 if (m < 0)
1237 return m;
1238
1239 for (;;) {
1240 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
1241 const char *x, *y;
1242
1243 if (k >= n || k >= m) {
1244 *ret = a_labels[n - k];
1245 return 0;
1246 }
1247
1248 x = a_labels[n - 1 - k];
1249 r = dns_label_unescape(&x, la, sizeof la, 0);
1250 if (r < 0)
1251 return r;
1252
1253 y = b_labels[m - 1 - k];
1254 q = dns_label_unescape(&y, lb, sizeof lb, 0);
1255 if (q < 0)
1256 return q;
1257
1258 if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) {
1259 *ret = a_labels[n - k];
1260 return 0;
1261 }
1262
1263 k++;
1264 }
1265 }
1266
1267 int dns_name_apply_idna(const char *name, char **ret) {
1268 /* Return negative on error, 0 if not implemented, positive on success. */
1269
1270 #if HAVE_LIBIDN2
1271 int r;
1272 _cleanup_free_ char *t = NULL;
1273
1274 assert(name);
1275 assert(ret);
1276
1277 r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
1278 IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
1279 log_debug("idn2_lookup_u8: %s → %s", name, t);
1280 if (r == IDN2_OK) {
1281 if (!startswith(name, "xn--")) {
1282 _cleanup_free_ char *s = NULL;
1283
1284 r = idn2_to_unicode_8z8z(t, &s, 0);
1285 if (r != IDN2_OK) {
1286 log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s",
1287 t, r, idn2_strerror(r));
1288 return 0;
1289 }
1290
1291 if (!streq_ptr(name, s)) {
1292 log_debug("idn2 roundtrip failed: \"%s\" \"%s\" \"%s\", ignoring.",
1293 name, t, s);
1294 return 0;
1295 }
1296 }
1297
1298 *ret = TAKE_PTR(t);
1299
1300 return 1; /* *ret has been written */
1301 }
1302
1303 log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
1304 if (r == IDN2_2HYPHEN)
1305 /* The name has two hyphens — forbidden by IDNA2008 in some cases */
1306 return 0;
1307 if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
1308 return -ENOSPC;
1309 return -EINVAL;
1310 #elif HAVE_LIBIDN
1311 _cleanup_free_ char *buf = NULL;
1312 size_t n = 0, allocated = 0;
1313 bool first = true;
1314 int r, q;
1315
1316 assert(name);
1317 assert(ret);
1318
1319 for (;;) {
1320 char label[DNS_LABEL_MAX];
1321
1322 r = dns_label_unescape(&name, label, sizeof label, 0);
1323 if (r < 0)
1324 return r;
1325 if (r == 0)
1326 break;
1327
1328 q = dns_label_apply_idna(label, r, label, sizeof label);
1329 if (q < 0)
1330 return q;
1331 if (q > 0)
1332 r = q;
1333
1334 if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
1335 return -ENOMEM;
1336
1337 r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
1338 if (r < 0)
1339 return r;
1340
1341 if (first)
1342 first = false;
1343 else
1344 buf[n++] = '.';
1345
1346 n += r;
1347 }
1348
1349 if (n > DNS_HOSTNAME_MAX)
1350 return -EINVAL;
1351
1352 if (!GREEDY_REALLOC(buf, allocated, n + 1))
1353 return -ENOMEM;
1354
1355 buf[n] = 0;
1356 *ret = TAKE_PTR(buf);
1357
1358 return 1;
1359 #else
1360 return 0;
1361 #endif
1362 }
1363
1364 int dns_name_is_valid_or_address(const char *name) {
1365 /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */
1366
1367 if (isempty(name))
1368 return 0;
1369
1370 if (in_addr_from_string_auto(name, NULL, NULL) >= 0)
1371 return 1;
1372
1373 return dns_name_is_valid(name);
1374 }