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