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