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