]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/dns-domain.c
tree-wide: add whitespace between type and variable name
[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 char *p, struct siphash *state) {
464 int r;
465
466 assert(p);
467
468 for (;;) {
469 char label[DNS_LABEL_MAX+1];
470
471 r = dns_label_unescape(&p, label, sizeof(label));
472 if (r < 0)
473 break;
474 if (r == 0)
475 break;
476
477 ascii_strlower_n(label, r);
478 siphash24_compress(label, r, state);
479 siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */
480 }
481
482 /* enforce that all names are terminated by the empty label */
483 string_hash_func("", state);
484 }
485
486 int dns_name_compare_func(const char *a, const char *b) {
487 const char *x, *y;
488 int r, q;
489
490 assert(a);
491 assert(b);
492
493 x = a + strlen(a);
494 y = b + strlen(b);
495
496 for (;;) {
497 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
498
499 if (x == NULL && y == NULL)
500 return 0;
501
502 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
503 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
504 if (r < 0 || q < 0)
505 return CMP(r, q);
506
507 r = ascii_strcasecmp_nn(la, r, lb, q);
508 if (r != 0)
509 return r;
510 }
511 }
512
513 DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
514
515 int dns_name_equal(const char *x, const char *y) {
516 int r, q;
517
518 assert(x);
519 assert(y);
520
521 for (;;) {
522 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
523
524 r = dns_label_unescape(&x, la, sizeof(la));
525 if (r < 0)
526 return r;
527
528 q = dns_label_unescape(&y, lb, sizeof(lb));
529 if (q < 0)
530 return q;
531
532 if (r != q)
533 return false;
534 if (r == 0)
535 return true;
536
537 if (ascii_strcasecmp_n(la, lb, r) != 0)
538 return false;
539 }
540 }
541
542 int dns_name_endswith(const char *name, const char *suffix) {
543 const char *n, *s, *saved_n = NULL;
544 int r, q;
545
546 assert(name);
547 assert(suffix);
548
549 n = name;
550 s = suffix;
551
552 for (;;) {
553 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
554
555 r = dns_label_unescape(&n, ln, sizeof(ln));
556 if (r < 0)
557 return r;
558
559 if (!saved_n)
560 saved_n = n;
561
562 q = dns_label_unescape(&s, ls, sizeof(ls));
563 if (q < 0)
564 return q;
565
566 if (r == 0 && q == 0)
567 return true;
568 if (r == 0 && saved_n == n)
569 return false;
570
571 if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
572
573 /* Not the same, let's jump back, and try with the next label again */
574 s = suffix;
575 n = TAKE_PTR(saved_n);
576 }
577 }
578 }
579
580 int dns_name_startswith(const char *name, const char *prefix) {
581 const char *n, *p;
582 int r, q;
583
584 assert(name);
585 assert(prefix);
586
587 n = name;
588 p = prefix;
589
590 for (;;) {
591 char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
592
593 r = dns_label_unescape(&p, lp, sizeof(lp));
594 if (r < 0)
595 return r;
596 if (r == 0)
597 return true;
598
599 q = dns_label_unescape(&n, ln, sizeof(ln));
600 if (q < 0)
601 return q;
602
603 if (r != q)
604 return false;
605 if (ascii_strcasecmp_n(ln, lp, r) != 0)
606 return false;
607 }
608 }
609
610 int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
611 const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
612 int r, q;
613
614 assert(name);
615 assert(old_suffix);
616 assert(new_suffix);
617 assert(ret);
618
619 n = name;
620 s = old_suffix;
621
622 for (;;) {
623 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
624
625 if (!saved_before)
626 saved_before = n;
627
628 r = dns_label_unescape(&n, ln, sizeof(ln));
629 if (r < 0)
630 return r;
631
632 if (!saved_after)
633 saved_after = n;
634
635 q = dns_label_unescape(&s, ls, sizeof(ls));
636 if (q < 0)
637 return q;
638
639 if (r == 0 && q == 0)
640 break;
641 if (r == 0 && saved_after == n) {
642 *ret = NULL; /* doesn't match */
643 return 0;
644 }
645
646 if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
647
648 /* Not the same, let's jump back, and try with the next label again */
649 s = old_suffix;
650 n = TAKE_PTR(saved_after);
651 saved_before = NULL;
652 }
653 }
654
655 /* Found it! Now generate the new name */
656 prefix = strndupa(name, saved_before - name);
657
658 r = dns_name_concat(prefix, new_suffix, ret);
659 if (r < 0)
660 return r;
661
662 return 1;
663 }
664
665 int dns_name_between(const char *a, const char *b, const char *c) {
666 /* Determine if b is strictly greater than a and strictly smaller than c.
667 We consider the order of names to be circular, so that if a is
668 strictly greater than c, we consider b to be between them if it is
669 either greater than a or smaller than c. This is how the canonical
670 DNS name order used in NSEC records work. */
671
672 if (dns_name_compare_func(a, c) < 0)
673 /*
674 a and c are properly ordered:
675 a<---b--->c
676 */
677 return dns_name_compare_func(a, b) < 0 &&
678 dns_name_compare_func(b, c) < 0;
679 else
680 /*
681 a and c are equal or 'reversed':
682 <--b--c a----->
683 or:
684 <-----c a--b-->
685 */
686 return dns_name_compare_func(b, c) < 0 ||
687 dns_name_compare_func(a, b) < 0;
688 }
689
690 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
691 const uint8_t *p;
692 int r;
693
694 assert(a);
695 assert(ret);
696
697 p = (const uint8_t*) a;
698
699 if (family == AF_INET)
700 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
701 else if (family == AF_INET6)
702 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",
703 hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
704 hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
705 hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
706 hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
707 hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
708 hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
709 hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
710 hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
711 else
712 return -EAFNOSUPPORT;
713 if (r < 0)
714 return -ENOMEM;
715
716 return 0;
717 }
718
719 int dns_name_address(const char *p, int *family, union in_addr_union *address) {
720 int r;
721
722 assert(p);
723 assert(family);
724 assert(address);
725
726 r = dns_name_endswith(p, "in-addr.arpa");
727 if (r < 0)
728 return r;
729 if (r > 0) {
730 uint8_t a[4];
731 unsigned i;
732
733 for (i = 0; i < ELEMENTSOF(a); i++) {
734 char label[DNS_LABEL_MAX+1];
735
736 r = dns_label_unescape(&p, label, sizeof(label));
737 if (r < 0)
738 return r;
739 if (r == 0)
740 return -EINVAL;
741 if (r > 3)
742 return -EINVAL;
743
744 r = safe_atou8(label, &a[i]);
745 if (r < 0)
746 return r;
747 }
748
749 r = dns_name_equal(p, "in-addr.arpa");
750 if (r <= 0)
751 return r;
752
753 *family = AF_INET;
754 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
755 ((uint32_t) a[2] << 16) |
756 ((uint32_t) a[1] << 8) |
757 (uint32_t) a[0]);
758
759 return 1;
760 }
761
762 r = dns_name_endswith(p, "ip6.arpa");
763 if (r < 0)
764 return r;
765 if (r > 0) {
766 struct in6_addr a;
767 unsigned i;
768
769 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
770 char label[DNS_LABEL_MAX+1];
771 int x, y;
772
773 r = dns_label_unescape(&p, label, sizeof(label));
774 if (r <= 0)
775 return r;
776 if (r != 1)
777 return -EINVAL;
778 x = unhexchar(label[0]);
779 if (x < 0)
780 return -EINVAL;
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 y = unhexchar(label[0]);
788 if (y < 0)
789 return -EINVAL;
790
791 a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
792 }
793
794 r = dns_name_equal(p, "ip6.arpa");
795 if (r <= 0)
796 return r;
797
798 *family = AF_INET6;
799 address->in6 = a;
800 return 1;
801 }
802
803 return 0;
804 }
805
806 bool dns_name_is_root(const char *name) {
807
808 assert(name);
809
810 /* There are exactly two ways to encode the root domain name:
811 * as empty string, or with a single dot. */
812
813 return STR_IN_SET(name, "", ".");
814 }
815
816 bool dns_name_is_single_label(const char *name) {
817 int r;
818
819 assert(name);
820
821 r = dns_name_parent(&name);
822 if (r <= 0)
823 return false;
824
825 return dns_name_is_root(name);
826 }
827
828 /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
829 int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
830 uint8_t *label_length, *out;
831 int r;
832
833 assert(domain);
834 assert(buffer);
835
836 out = buffer;
837
838 do {
839 /* Reserve a byte for label length */
840 if (len <= 0)
841 return -ENOBUFS;
842 len--;
843 label_length = out;
844 out++;
845
846 /* Convert and copy a single label. Note that
847 * dns_label_unescape() returns 0 when it hits the end
848 * of the domain name, which we rely on here to encode
849 * the trailing NUL byte. */
850 r = dns_label_unescape(&domain, (char *) out, len);
851 if (r < 0)
852 return r;
853
854 /* Optionally, output the name in DNSSEC canonical
855 * format, as described in RFC 4034, section 6.2. Or
856 * in other words: in lower-case. */
857 if (canonical)
858 ascii_strlower_n((char*) out, (size_t) r);
859
860 /* Fill label length, move forward */
861 *label_length = r;
862 out += r;
863 len -= r;
864
865 } while (r != 0);
866
867 /* Verify the maximum size of the encoded name. The trailing
868 * dot + NUL byte account are included this time, hence
869 * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
870 * time. */
871 if (out - buffer > DNS_HOSTNAME_MAX + 2)
872 return -EINVAL;
873
874 return out - buffer;
875 }
876
877 static bool srv_type_label_is_valid(const char *label, size_t n) {
878 size_t k;
879
880 assert(label);
881
882 if (n < 2) /* Label needs to be at least 2 chars long */
883 return false;
884
885 if (label[0] != '_') /* First label char needs to be underscore */
886 return false;
887
888 /* Second char must be a letter */
889 if (!(label[1] >= 'A' && label[1] <= 'Z') &&
890 !(label[1] >= 'a' && label[1] <= 'z'))
891 return false;
892
893 /* Third and further chars must be alphanumeric or a hyphen */
894 for (k = 2; k < n; k++) {
895 if (!(label[k] >= 'A' && label[k] <= 'Z') &&
896 !(label[k] >= 'a' && label[k] <= 'z') &&
897 !(label[k] >= '0' && label[k] <= '9') &&
898 label[k] != '-')
899 return false;
900 }
901
902 return true;
903 }
904
905 bool dns_srv_type_is_valid(const char *name) {
906 unsigned c = 0;
907 int r;
908
909 if (!name)
910 return false;
911
912 for (;;) {
913 char label[DNS_LABEL_MAX];
914
915 /* This more or less implements RFC 6335, Section 5.1 */
916
917 r = dns_label_unescape(&name, label, sizeof(label));
918 if (r < 0)
919 return false;
920 if (r == 0)
921 break;
922
923 if (c >= 2)
924 return false;
925
926 if (!srv_type_label_is_valid(label, r))
927 return false;
928
929 c++;
930 }
931
932 return c == 2; /* exactly two labels */
933 }
934
935 bool dnssd_srv_type_is_valid(const char *name) {
936 return dns_srv_type_is_valid(name) &&
937 ((dns_name_endswith(name, "_tcp") > 0) ||
938 (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */
939 }
940
941 bool dns_service_name_is_valid(const char *name) {
942 size_t l;
943
944 /* This more or less implements RFC 6763, Section 4.1.1 */
945
946 if (!name)
947 return false;
948
949 if (!utf8_is_valid(name))
950 return false;
951
952 if (string_has_cc(name, NULL))
953 return false;
954
955 l = strlen(name);
956 if (l <= 0)
957 return false;
958 if (l > 63)
959 return false;
960
961 return true;
962 }
963
964 int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
965 char escaped[DNS_LABEL_ESCAPED_MAX];
966 _cleanup_free_ char *n = NULL;
967 int r;
968
969 assert(type);
970 assert(domain);
971 assert(ret);
972
973 if (!dns_srv_type_is_valid(type))
974 return -EINVAL;
975
976 if (!name)
977 return dns_name_concat(type, domain, ret);
978
979 if (!dns_service_name_is_valid(name))
980 return -EINVAL;
981
982 r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
983 if (r < 0)
984 return r;
985
986 r = dns_name_concat(type, domain, &n);
987 if (r < 0)
988 return r;
989
990 return dns_name_concat(escaped, n, ret);
991 }
992
993 static bool dns_service_name_label_is_valid(const char *label, size_t n) {
994 char *s;
995
996 assert(label);
997
998 if (memchr(label, 0, n))
999 return false;
1000
1001 s = strndupa(label, n);
1002 return dns_service_name_is_valid(s);
1003 }
1004
1005 int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
1006 _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
1007 const char *p = joined, *q = NULL, *d = NULL;
1008 char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
1009 int an, bn, cn, r;
1010 unsigned x = 0;
1011
1012 assert(joined);
1013
1014 /* Get first label from the full name */
1015 an = dns_label_unescape(&p, a, sizeof(a));
1016 if (an < 0)
1017 return an;
1018
1019 if (an > 0) {
1020 x++;
1021
1022 /* If there was a first label, try to get the second one */
1023 bn = dns_label_unescape(&p, b, sizeof(b));
1024 if (bn < 0)
1025 return bn;
1026
1027 if (bn > 0) {
1028 x++;
1029
1030 /* If there was a second label, try to get the third one */
1031 q = p;
1032 cn = dns_label_unescape(&p, c, sizeof(c));
1033 if (cn < 0)
1034 return cn;
1035
1036 if (cn > 0)
1037 x++;
1038 } else
1039 cn = 0;
1040 } else
1041 an = 0;
1042
1043 if (x >= 2 && srv_type_label_is_valid(b, bn)) {
1044
1045 if (x >= 3 && srv_type_label_is_valid(c, cn)) {
1046
1047 if (dns_service_name_label_is_valid(a, an)) {
1048 /* OK, got <name> . <type> . <type2> . <domain> */
1049
1050 name = strndup(a, an);
1051 if (!name)
1052 return -ENOMEM;
1053
1054 type = strjoin(b, ".", c);
1055 if (!type)
1056 return -ENOMEM;
1057
1058 d = p;
1059 goto finish;
1060 }
1061
1062 } else if (srv_type_label_is_valid(a, an)) {
1063
1064 /* OK, got <type> . <type2> . <domain> */
1065
1066 name = NULL;
1067
1068 type = strjoin(a, ".", b);
1069 if (!type)
1070 return -ENOMEM;
1071
1072 d = q;
1073 goto finish;
1074 }
1075 }
1076
1077 name = NULL;
1078 type = NULL;
1079 d = joined;
1080
1081 finish:
1082 r = dns_name_normalize(d, &domain);
1083 if (r < 0)
1084 return r;
1085
1086 if (_domain)
1087 *_domain = TAKE_PTR(domain);
1088
1089 if (_type)
1090 *_type = TAKE_PTR(type);
1091
1092 if (_name)
1093 *_name = TAKE_PTR(name);
1094
1095 return 0;
1096 }
1097
1098 static int dns_name_build_suffix_table(const char *name, const char *table[]) {
1099 const char *p;
1100 unsigned n = 0;
1101 int r;
1102
1103 assert(name);
1104 assert(table);
1105
1106 p = name;
1107 for (;;) {
1108 if (n > DNS_N_LABELS_MAX)
1109 return -EINVAL;
1110
1111 table[n] = p;
1112 r = dns_name_parent(&p);
1113 if (r < 0)
1114 return r;
1115 if (r == 0)
1116 break;
1117
1118 n++;
1119 }
1120
1121 return (int) n;
1122 }
1123
1124 int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
1125 const char* labels[DNS_N_LABELS_MAX+1];
1126 int n;
1127
1128 assert(name);
1129 assert(ret);
1130
1131 n = dns_name_build_suffix_table(name, labels);
1132 if (n < 0)
1133 return n;
1134
1135 if ((unsigned) n < n_labels)
1136 return -EINVAL;
1137
1138 *ret = labels[n - n_labels];
1139 return (int) (n - n_labels);
1140 }
1141
1142 int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
1143 int r;
1144
1145 assert(a);
1146 assert(ret);
1147
1148 for (; n_labels > 0; n_labels--) {
1149 r = dns_name_parent(&a);
1150 if (r < 0)
1151 return r;
1152 if (r == 0) {
1153 *ret = "";
1154 return 0;
1155 }
1156 }
1157
1158 *ret = a;
1159 return 1;
1160 }
1161
1162 int dns_name_count_labels(const char *name) {
1163 unsigned n = 0;
1164 const char *p;
1165 int r;
1166
1167 assert(name);
1168
1169 p = name;
1170 for (;;) {
1171 r = dns_name_parent(&p);
1172 if (r < 0)
1173 return r;
1174 if (r == 0)
1175 break;
1176
1177 if (n >= DNS_N_LABELS_MAX)
1178 return -EINVAL;
1179
1180 n++;
1181 }
1182
1183 return (int) n;
1184 }
1185
1186 int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
1187 int r;
1188
1189 assert(a);
1190 assert(b);
1191
1192 r = dns_name_skip(a, n_labels, &a);
1193 if (r <= 0)
1194 return r;
1195
1196 return dns_name_equal(a, b);
1197 }
1198
1199 int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
1200 const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1];
1201 int n = 0, m = 0, k = 0, r, q;
1202
1203 assert(a);
1204 assert(b);
1205 assert(ret);
1206
1207 /* Determines the common suffix of domain names a and b */
1208
1209 n = dns_name_build_suffix_table(a, a_labels);
1210 if (n < 0)
1211 return n;
1212
1213 m = dns_name_build_suffix_table(b, b_labels);
1214 if (m < 0)
1215 return m;
1216
1217 for (;;) {
1218 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
1219 const char *x, *y;
1220
1221 if (k >= n || k >= m) {
1222 *ret = a_labels[n - k];
1223 return 0;
1224 }
1225
1226 x = a_labels[n - 1 - k];
1227 r = dns_label_unescape(&x, la, sizeof(la));
1228 if (r < 0)
1229 return r;
1230
1231 y = b_labels[m - 1 - k];
1232 q = dns_label_unescape(&y, lb, sizeof(lb));
1233 if (q < 0)
1234 return q;
1235
1236 if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) {
1237 *ret = a_labels[n - k];
1238 return 0;
1239 }
1240
1241 k++;
1242 }
1243 }
1244
1245 int dns_name_apply_idna(const char *name, char **ret) {
1246 /* Return negative on error, 0 if not implemented, positive on success. */
1247
1248 #if HAVE_LIBIDN2
1249 int r;
1250 _cleanup_free_ char *t = NULL;
1251
1252 assert(name);
1253 assert(ret);
1254
1255 r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
1256 IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
1257 log_debug("idn2_lookup_u8: %s → %s", name, t);
1258 if (r == IDN2_OK) {
1259 if (!startswith(name, "xn--")) {
1260 _cleanup_free_ char *s = NULL;
1261
1262 r = idn2_to_unicode_8z8z(t, &s, 0);
1263 if (r != IDN2_OK) {
1264 log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s",
1265 t, r, idn2_strerror(r));
1266 return 0;
1267 }
1268
1269 if (!streq_ptr(name, s)) {
1270 log_debug("idn2 roundtrip failed: \"%s\" \"%s\" \"%s\", ignoring.",
1271 name, t, s);
1272 return 0;
1273 }
1274 }
1275
1276 *ret = TAKE_PTR(t);
1277
1278 return 1; /* *ret has been written */
1279 }
1280
1281 log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
1282 if (r == IDN2_2HYPHEN)
1283 /* The name has two hyphens — forbidden by IDNA2008 in some cases */
1284 return 0;
1285 if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
1286 return -ENOSPC;
1287 return -EINVAL;
1288 #elif HAVE_LIBIDN
1289 _cleanup_free_ char *buf = NULL;
1290 size_t n = 0, allocated = 0;
1291 bool first = true;
1292 int r, q;
1293
1294 assert(name);
1295 assert(ret);
1296
1297 for (;;) {
1298 char label[DNS_LABEL_MAX];
1299
1300 r = dns_label_unescape(&name, label, sizeof(label));
1301 if (r < 0)
1302 return r;
1303 if (r == 0)
1304 break;
1305
1306 q = dns_label_apply_idna(label, r, label, sizeof(label));
1307 if (q < 0)
1308 return q;
1309 if (q > 0)
1310 r = q;
1311
1312 if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
1313 return -ENOMEM;
1314
1315 r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
1316 if (r < 0)
1317 return r;
1318
1319 if (first)
1320 first = false;
1321 else
1322 buf[n++] = '.';
1323
1324 n += r;
1325 }
1326
1327 if (n > DNS_HOSTNAME_MAX)
1328 return -EINVAL;
1329
1330 if (!GREEDY_REALLOC(buf, allocated, n + 1))
1331 return -ENOMEM;
1332
1333 buf[n] = 0;
1334 *ret = TAKE_PTR(buf);
1335
1336 return 1;
1337 #else
1338 return 0;
1339 #endif
1340 }
1341
1342 int dns_name_is_valid_or_address(const char *name) {
1343 /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */
1344
1345 if (isempty(name))
1346 return 0;
1347
1348 if (in_addr_from_string_auto(name, NULL, NULL) >= 0)
1349 return 1;
1350
1351 return dns_name_is_valid(name);
1352 }