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