]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/dns-domain.c
shared: fix handling of suffix "." in dns_name_compare_func()
[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 CC characters or anything that doesn't fit in 8bit */
102 if (k < ' ' || k > 255 || k == 127)
103 return -EINVAL;
104
105 if (d)
106 *(d++) = (char) k;
107 sz--;
108 r++;
109
110 n += 3;
111 } else
112 return -EINVAL;
113
114 } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
115
116 /* Normal character */
117
118 if (d)
119 *(d++) = *n;
120 sz--;
121 r++;
122 n++;
123 } else
124 return -EINVAL;
125 }
126
127 /* Empty label that is not at the end? */
128 if (r == 0 && *n)
129 return -EINVAL;
130
131 if (sz >= 1 && d)
132 *d = 0;
133
134 *name = n;
135 return r;
136 }
137
138 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
139 * the previous label (always skipping one dot) or to NULL if there are no more
140 * labels. */
141 int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
142 const char *terminal;
143 int r;
144
145 assert(name);
146 assert(label_terminal);
147 assert(dest);
148
149 /* no more labels */
150 if (!*label_terminal) {
151 if (sz >= 1)
152 *dest = 0;
153
154 return 0;
155 }
156
157 terminal = *label_terminal;
158 assert(*terminal == '.' || *terminal == 0);
159
160 /* Skip current terminal character (and accept domain names ending it ".") */
161 if (*terminal == 0)
162 terminal--;
163 if (terminal >= name && *terminal == '.')
164 terminal--;
165
166 /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
167 for (;;) {
168 if (terminal < name) {
169 /* Reached the first label, so indicate that there are no more */
170 terminal = NULL;
171 break;
172 }
173
174 /* Find the start of the last label */
175 if (*terminal == '.') {
176 const char *y;
177 unsigned slashes = 0;
178
179 for (y = terminal - 1; y >= name && *y == '\\'; y--)
180 slashes ++;
181
182 if (slashes % 2 == 0) {
183 /* The '.' was not escaped */
184 name = terminal + 1;
185 break;
186 } else {
187 terminal = y;
188 continue;
189 }
190 }
191
192 terminal --;
193 }
194
195 r = dns_label_unescape(&name, dest, sz);
196 if (r < 0)
197 return r;
198
199 *label_terminal = terminal;
200
201 return r;
202 }
203
204 int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
205 char *q;
206
207 /* DNS labels must be between 1 and 63 characters long. A
208 * zero-length label does not exist. See RFC 2182, Section
209 * 11. */
210
211 if (l <= 0 || l > DNS_LABEL_MAX)
212 return -EINVAL;
213 if (sz < 1)
214 return -ENOBUFS;
215
216 assert(p);
217 assert(dest);
218
219 q = dest;
220 while (l > 0) {
221
222 if (*p == '.' || *p == '\\') {
223
224 /* Dot or backslash */
225
226 if (sz < 3)
227 return -ENOBUFS;
228
229 *(q++) = '\\';
230 *(q++) = *p;
231
232 sz -= 2;
233
234 } else if (*p == '_' ||
235 *p == '-' ||
236 (*p >= '0' && *p <= '9') ||
237 (*p >= 'a' && *p <= 'z') ||
238 (*p >= 'A' && *p <= 'Z')) {
239
240 /* Proper character */
241
242 if (sz < 2)
243 return -ENOBUFS;
244
245 *(q++) = *p;
246 sz -= 1;
247
248 } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
249
250 /* Everything else */
251
252 if (sz < 5)
253 return -ENOBUFS;
254
255 *(q++) = '\\';
256 *(q++) = '0' + (char) ((uint8_t) *p / 100);
257 *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
258 *(q++) = '0' + (char) ((uint8_t) *p % 10);
259
260 sz -= 4;
261
262 } else
263 return -EINVAL;
264
265 p++;
266 l--;
267 }
268
269 *q = 0;
270 return (int) (q - dest);
271 }
272
273 int dns_label_escape_new(const char *p, size_t l, char **ret) {
274 _cleanup_free_ char *s = NULL;
275 int r;
276
277 assert(p);
278 assert(ret);
279
280 if (l <= 0 || l > DNS_LABEL_MAX)
281 return -EINVAL;
282
283 s = new(char, DNS_LABEL_ESCAPED_MAX);
284 if (!s)
285 return -ENOMEM;
286
287 r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
288 if (r < 0)
289 return r;
290
291 *ret = s;
292 s = NULL;
293
294 return r;
295 }
296
297 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
298 #ifdef HAVE_LIBIDN
299 _cleanup_free_ uint32_t *input = NULL;
300 size_t input_size, l;
301 const char *p;
302 bool contains_8bit = false;
303 char buffer[DNS_LABEL_MAX+1];
304
305 assert(encoded);
306 assert(decoded);
307
308 /* Converts an U-label into an A-label */
309
310 if (encoded_size <= 0)
311 return -EINVAL;
312
313 for (p = encoded; p < encoded + encoded_size; p++)
314 if ((uint8_t) *p > 127)
315 contains_8bit = true;
316
317 if (!contains_8bit) {
318 if (encoded_size > DNS_LABEL_MAX)
319 return -EINVAL;
320
321 return 0;
322 }
323
324 input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
325 if (!input)
326 return -ENOMEM;
327
328 if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0)
329 return -EINVAL;
330
331 l = strlen(buffer);
332
333 /* Verify that the the result is not longer than one DNS label. */
334 if (l <= 0 || l > DNS_LABEL_MAX)
335 return -EINVAL;
336 if (l > decoded_max)
337 return -ENOBUFS;
338
339 memcpy(decoded, buffer, l);
340
341 /* If there's room, append a trailing NUL byte, but only then */
342 if (decoded_max > l)
343 decoded[l] = 0;
344
345 return (int) l;
346 #else
347 return 0;
348 #endif
349 }
350
351 int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
352 #ifdef HAVE_LIBIDN
353 size_t input_size, output_size;
354 _cleanup_free_ uint32_t *input = NULL;
355 _cleanup_free_ char *result = NULL;
356 uint32_t *output = NULL;
357 size_t w;
358
359 /* To be invoked after unescaping. Converts an A-label into an U-label. */
360
361 assert(encoded);
362 assert(decoded);
363
364 if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX)
365 return -EINVAL;
366
367 if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
368 return 0;
369
370 if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0)
371 return 0;
372
373 input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
374 if (!input)
375 return -ENOMEM;
376
377 output_size = input_size;
378 output = newa(uint32_t, output_size);
379
380 idna_to_unicode_44i(input, input_size, output, &output_size, 0);
381
382 result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
383 if (!result)
384 return -ENOMEM;
385 if (w <= 0)
386 return -EINVAL;
387 if (w > decoded_max)
388 return -ENOBUFS;
389
390 memcpy(decoded, result, w);
391
392 /* Append trailing NUL byte if there's space, but only then. */
393 if (decoded_max > w)
394 decoded[w] = 0;
395
396 return w;
397 #else
398 return 0;
399 #endif
400 }
401
402 int dns_name_concat(const char *a, const char *b, char **_ret) {
403 _cleanup_free_ char *ret = NULL;
404 size_t n = 0, allocated = 0;
405 const char *p = a;
406 bool first = true;
407 int r;
408
409 assert(a);
410
411 for (;;) {
412 char label[DNS_LABEL_MAX];
413 int k;
414
415 r = dns_label_unescape(&p, label, sizeof(label));
416 if (r < 0)
417 return r;
418 if (r == 0) {
419 if (*p != 0)
420 return -EINVAL;
421
422 if (b) {
423 /* Now continue with the second string, if there is one */
424 p = b;
425 b = NULL;
426 continue;
427 }
428
429 break;
430 }
431
432 k = dns_label_undo_idna(label, r, label, sizeof(label));
433 if (k < 0)
434 return k;
435 if (k > 0)
436 r = k;
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 if (n > DNS_HOSTNAME_MAX)
465 return -EINVAL;
466
467 if (_ret) {
468 if (!GREEDY_REALLOC(ret, allocated, n + 1))
469 return -ENOMEM;
470
471 ret[n] = 0;
472 *_ret = ret;
473 ret = NULL;
474 }
475
476 return 0;
477 }
478
479 void dns_name_hash_func(const void *s, struct siphash *state) {
480 const char *p = s;
481 int r;
482
483 assert(p);
484
485 while (*p) {
486 char label[DNS_LABEL_MAX+1];
487 int k;
488
489 r = dns_label_unescape(&p, label, sizeof(label));
490 if (r < 0)
491 break;
492
493 k = dns_label_undo_idna(label, r, label, sizeof(label));
494 if (k < 0)
495 break;
496 if (k > 0)
497 r = k;
498
499 if (r == 0)
500 break;
501
502 label[r] = 0;
503 ascii_strlower(label);
504
505 string_hash_func(label, state);
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, k, w;
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+1], lb[DNS_LABEL_MAX+1];
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 k = dns_label_undo_idna(la, r, la, sizeof(la));
534 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
535 if (k < 0 || w < 0)
536 return k - w;
537 if (k > 0)
538 r = k;
539 if (w > 0)
540 q = w;
541
542 la[r] = lb[q] = 0;
543 r = strcasecmp(la, lb);
544 if (r != 0)
545 return r;
546 }
547 }
548
549 const struct hash_ops dns_name_hash_ops = {
550 .hash = dns_name_hash_func,
551 .compare = dns_name_compare_func
552 };
553
554 int dns_name_equal(const char *x, const char *y) {
555 int r, q, k, w;
556
557 assert(x);
558 assert(y);
559
560 for (;;) {
561 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
562
563 if (*x == 0 && *y == 0)
564 return true;
565
566 r = dns_label_unescape(&x, la, sizeof(la));
567 if (r < 0)
568 return r;
569 if (r > 0) {
570 k = dns_label_undo_idna(la, r, la, sizeof(la));
571 if (k < 0)
572 return k;
573 if (k > 0)
574 r = k;
575 }
576
577 q = dns_label_unescape(&y, lb, sizeof(lb));
578 if (q < 0)
579 return q;
580 if (q > 0) {
581 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
582 if (w < 0)
583 return w;
584 if (w > 0)
585 q = w;
586 }
587
588 /* If one name had fewer labels than the other, this
589 * will show up as empty label here, which the
590 * strcasecmp() below will properly consider different
591 * from a non-empty label. */
592
593 la[r] = lb[q] = 0;
594 if (strcasecmp(la, lb) != 0)
595 return false;
596 }
597 }
598
599 int dns_name_endswith(const char *name, const char *suffix) {
600 const char *n, *s, *saved_n = NULL;
601 int r, q, k, w;
602
603 assert(name);
604 assert(suffix);
605
606 n = name;
607 s = suffix;
608
609 for (;;) {
610 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
611
612 r = dns_label_unescape(&n, ln, sizeof(ln));
613 if (r < 0)
614 return r;
615 if (r > 0) {
616 k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
617 if (k < 0)
618 return k;
619 if (k > 0)
620 r = k;
621 }
622
623 if (!saved_n)
624 saved_n = n;
625
626 q = dns_label_unescape(&s, ls, sizeof(ls));
627 if (q < 0)
628 return q;
629 if (q > 0) {
630 w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
631 if (w < 0)
632 return w;
633 if (w > 0)
634 q = w;
635 }
636
637 if (r == 0 && q == 0)
638 return true;
639 if (r == 0 && saved_n == n)
640 return false;
641
642 ln[r] = ls[q] = 0;
643
644 if (r != q || strcasecmp(ln, ls)) {
645
646 /* Not the same, let's jump back, and try with the next label again */
647 s = suffix;
648 n = saved_n;
649 saved_n = NULL;
650 }
651 }
652 }
653
654 int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
655 const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
656 int r, q, k, w;
657
658 assert(name);
659 assert(old_suffix);
660 assert(new_suffix);
661 assert(ret);
662
663 n = name;
664 s = old_suffix;
665
666 for (;;) {
667 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
668
669 if (!saved_before)
670 saved_before = n;
671
672 r = dns_label_unescape(&n, ln, sizeof(ln));
673 if (r < 0)
674 return r;
675 if (r > 0) {
676 k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
677 if (k < 0)
678 return k;
679 if (k > 0)
680 r = k;
681 }
682
683 if (!saved_after)
684 saved_after = n;
685
686 q = dns_label_unescape(&s, ls, sizeof(ls));
687 if (q < 0)
688 return q;
689 if (q > 0) {
690 w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
691 if (w < 0)
692 return w;
693 if (w > 0)
694 q = w;
695 }
696
697 if (r == 0 && q == 0)
698 break;
699 if (r == 0 && saved_after == n) {
700 *ret = NULL; /* doesn't match */
701 return 0;
702 }
703
704 ln[r] = ls[q] = 0;
705
706 if (r != q || strcasecmp(ln, ls)) {
707
708 /* Not the same, let's jump back, and try with the next label again */
709 s = old_suffix;
710 n = saved_after;
711 saved_after = saved_before = NULL;
712 }
713 }
714
715 /* Found it! Now generate the new name */
716 prefix = strndupa(name, saved_before - name);
717
718 r = dns_name_concat(prefix, new_suffix, ret);
719 if (r < 0)
720 return r;
721
722 return 1;
723 }
724
725 int dns_name_between(const char *a, const char *b, const char *c) {
726 int n;
727
728 /* Determine if b is strictly greater than a and strictly smaller than c.
729 We consider the order of names to be circular, so that if a is
730 strictly greater than c, we consider b to be between them if it is
731 either greater than a or smaller than c. This is how the canonical
732 DNS name order used in NSEC records work. */
733
734 n = dns_name_compare_func(a, c);
735 if (n == 0)
736 return -EINVAL;
737 else if (n < 0)
738 /* a<---b--->c */
739 return dns_name_compare_func(a, b) < 0 &&
740 dns_name_compare_func(b, c) < 0;
741 else
742 /* <--b--c a--b--> */
743 return dns_name_compare_func(b, c) < 0 ||
744 dns_name_compare_func(a, b) < 0;
745 }
746
747 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
748 const uint8_t *p;
749 int r;
750
751 assert(a);
752 assert(ret);
753
754 p = (const uint8_t*) a;
755
756 if (family == AF_INET)
757 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
758 else if (family == AF_INET6)
759 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",
760 hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
761 hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
762 hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
763 hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
764 hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
765 hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
766 hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
767 hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
768 else
769 return -EAFNOSUPPORT;
770 if (r < 0)
771 return -ENOMEM;
772
773 return 0;
774 }
775
776 int dns_name_address(const char *p, int *family, union in_addr_union *address) {
777 int r;
778
779 assert(p);
780 assert(family);
781 assert(address);
782
783 r = dns_name_endswith(p, "in-addr.arpa");
784 if (r < 0)
785 return r;
786 if (r > 0) {
787 uint8_t a[4];
788 unsigned i;
789
790 for (i = 0; i < ELEMENTSOF(a); i++) {
791 char label[DNS_LABEL_MAX+1];
792
793 r = dns_label_unescape(&p, label, sizeof(label));
794 if (r < 0)
795 return r;
796 if (r == 0)
797 return -EINVAL;
798 if (r > 3)
799 return -EINVAL;
800
801 r = safe_atou8(label, &a[i]);
802 if (r < 0)
803 return r;
804 }
805
806 r = dns_name_equal(p, "in-addr.arpa");
807 if (r <= 0)
808 return r;
809
810 *family = AF_INET;
811 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
812 ((uint32_t) a[2] << 16) |
813 ((uint32_t) a[1] << 8) |
814 (uint32_t) a[0]);
815
816 return 1;
817 }
818
819 r = dns_name_endswith(p, "ip6.arpa");
820 if (r < 0)
821 return r;
822 if (r > 0) {
823 struct in6_addr a;
824 unsigned i;
825
826 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
827 char label[DNS_LABEL_MAX+1];
828 int x, y;
829
830 r = dns_label_unescape(&p, label, sizeof(label));
831 if (r <= 0)
832 return r;
833 if (r != 1)
834 return -EINVAL;
835 x = unhexchar(label[0]);
836 if (x < 0)
837 return -EINVAL;
838
839 r = dns_label_unescape(&p, label, sizeof(label));
840 if (r <= 0)
841 return r;
842 if (r != 1)
843 return -EINVAL;
844 y = unhexchar(label[0]);
845 if (y < 0)
846 return -EINVAL;
847
848 a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
849 }
850
851 r = dns_name_equal(p, "ip6.arpa");
852 if (r <= 0)
853 return r;
854
855 *family = AF_INET6;
856 address->in6 = a;
857 return 1;
858 }
859
860 return 0;
861 }
862
863 bool dns_name_is_root(const char *name) {
864
865 assert(name);
866
867 /* There are exactly two ways to encode the root domain name:
868 * as empty string, or with a single dot. */
869
870 return STR_IN_SET(name, "", ".");
871 }
872
873 bool dns_name_is_single_label(const char *name) {
874 char label[DNS_LABEL_MAX+1];
875 int r;
876
877 assert(name);
878
879 r = dns_label_unescape(&name, label, sizeof(label));
880 if (r <= 0)
881 return false;
882
883 return dns_name_is_root(name);
884 }
885
886 /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
887 int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
888 uint8_t *label_length, *out;
889 int r;
890
891 assert(domain);
892 assert(buffer);
893
894 out = buffer;
895
896 do {
897 /* Reserve a byte for label length */
898 if (len <= 0)
899 return -ENOBUFS;
900 len--;
901 label_length = out;
902 out++;
903
904 /* Convert and copy a single label. Note that
905 * dns_label_unescape() returns 0 when it hits the end
906 * of the domain name, which we rely on here to encode
907 * the trailing NUL byte. */
908 r = dns_label_unescape(&domain, (char *) out, len);
909 if (r < 0)
910 return r;
911
912 if (canonical) {
913 size_t i;
914
915 /* Optionally, output the name in DNSSEC
916 * canonical format, as described in RFC 4034,
917 * section 6.2. Or in other words: in
918 * lower-case. */
919
920 for (i = 0; i < (size_t) r; i++) {
921 if (out[i] >= 'A' && out[i] <= 'Z')
922 out[i] = out[i] - 'A' + 'a';
923 }
924 }
925
926 /* Fill label length, move forward */
927 *label_length = r;
928 out += r;
929 len -= r;
930
931 } while (r != 0);
932
933 /* Verify the maximum size of the encoded name. The trailing
934 * dot + NUL byte account are included this time, hence
935 * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
936 * time. */
937 if (out - buffer > DNS_HOSTNAME_MAX + 2)
938 return -EINVAL;
939
940 return out - buffer;
941 }
942
943 static bool srv_type_label_is_valid(const char *label, size_t n) {
944 size_t k;
945
946 assert(label);
947
948 if (n < 2) /* Label needs to be at least 2 chars long */
949 return false;
950
951 if (label[0] != '_') /* First label char needs to be underscore */
952 return false;
953
954 /* Second char must be a letter */
955 if (!(label[1] >= 'A' && label[1] <= 'Z') &&
956 !(label[1] >= 'a' && label[1] <= 'z'))
957 return false;
958
959 /* Third and further chars must be alphanumeric or a hyphen */
960 for (k = 2; k < n; k++) {
961 if (!(label[k] >= 'A' && label[k] <= 'Z') &&
962 !(label[k] >= 'a' && label[k] <= 'z') &&
963 !(label[k] >= '0' && label[k] <= '9') &&
964 label[k] != '-')
965 return false;
966 }
967
968 return true;
969 }
970
971 bool dns_srv_type_is_valid(const char *name) {
972 unsigned c = 0;
973 int r;
974
975 if (!name)
976 return false;
977
978 for (;;) {
979 char label[DNS_LABEL_MAX];
980
981 /* This more or less implements RFC 6335, Section 5.1 */
982
983 r = dns_label_unescape(&name, label, sizeof(label));
984 if (r < 0)
985 return false;
986 if (r == 0)
987 break;
988
989 if (c >= 2)
990 return false;
991
992 if (!srv_type_label_is_valid(label, r))
993 return false;
994
995 c++;
996 }
997
998 return c == 2; /* exactly two labels */
999 }
1000
1001 bool dns_service_name_is_valid(const char *name) {
1002 size_t l;
1003
1004 /* This more or less implements RFC 6763, Section 4.1.1 */
1005
1006 if (!name)
1007 return false;
1008
1009 if (!utf8_is_valid(name))
1010 return false;
1011
1012 if (string_has_cc(name, NULL))
1013 return false;
1014
1015 l = strlen(name);
1016 if (l <= 0)
1017 return false;
1018 if (l > 63)
1019 return false;
1020
1021 return true;
1022 }
1023
1024 int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
1025 char escaped[DNS_LABEL_ESCAPED_MAX];
1026 _cleanup_free_ char *n = NULL;
1027 int r;
1028
1029 assert(type);
1030 assert(domain);
1031 assert(ret);
1032
1033 if (!dns_srv_type_is_valid(type))
1034 return -EINVAL;
1035
1036 if (!name)
1037 return dns_name_concat(type, domain, ret);
1038
1039 if (!dns_service_name_is_valid(name))
1040 return -EINVAL;
1041
1042 r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
1043 if (r < 0)
1044 return r;
1045
1046 r = dns_name_concat(type, domain, &n);
1047 if (r < 0)
1048 return r;
1049
1050 return dns_name_concat(escaped, n, ret);
1051 }
1052
1053 static bool dns_service_name_label_is_valid(const char *label, size_t n) {
1054 char *s;
1055
1056 assert(label);
1057
1058 if (memchr(label, 0, n))
1059 return false;
1060
1061 s = strndupa(label, n);
1062 return dns_service_name_is_valid(s);
1063 }
1064
1065 int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
1066 _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
1067 const char *p = joined, *q = NULL, *d = NULL;
1068 char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
1069 int an, bn, cn, r;
1070 unsigned x = 0;
1071
1072 assert(joined);
1073
1074 /* Get first label from the full name */
1075 an = dns_label_unescape(&p, a, sizeof(a));
1076 if (an < 0)
1077 return an;
1078
1079 if (an > 0) {
1080 x++;
1081
1082 /* If there was a first label, try to get the second one */
1083 bn = dns_label_unescape(&p, b, sizeof(b));
1084 if (bn < 0)
1085 return bn;
1086
1087 if (bn > 0) {
1088 x++;
1089
1090 /* If there was a second label, try to get the third one */
1091 q = p;
1092 cn = dns_label_unescape(&p, c, sizeof(c));
1093 if (cn < 0)
1094 return cn;
1095
1096 if (cn > 0)
1097 x++;
1098 } else
1099 cn = 0;
1100 } else
1101 an = 0;
1102
1103 if (x >= 2 && srv_type_label_is_valid(b, bn)) {
1104
1105 if (x >= 3 && srv_type_label_is_valid(c, cn)) {
1106
1107 if (dns_service_name_label_is_valid(a, an)) {
1108
1109 /* OK, got <name> . <type> . <type2> . <domain> */
1110
1111 name = strndup(a, an);
1112 if (!name)
1113 return -ENOMEM;
1114
1115 type = new(char, bn+1+cn+1);
1116 if (!type)
1117 return -ENOMEM;
1118 strcpy(stpcpy(stpcpy(type, b), "."), c);
1119
1120 d = p;
1121 goto finish;
1122 }
1123
1124 } else if (srv_type_label_is_valid(a, an)) {
1125
1126 /* OK, got <type> . <type2> . <domain> */
1127
1128 name = NULL;
1129
1130 type = new(char, an+1+bn+1);
1131 if (!type)
1132 return -ENOMEM;
1133 strcpy(stpcpy(stpcpy(type, a), "."), b);
1134
1135 d = q;
1136 goto finish;
1137 }
1138 }
1139
1140 name = NULL;
1141 type = NULL;
1142 d = joined;
1143
1144 finish:
1145 r = dns_name_normalize(d, &domain);
1146 if (r < 0)
1147 return r;
1148
1149 if (_domain) {
1150 *_domain = domain;
1151 domain = NULL;
1152 }
1153
1154 if (_type) {
1155 *_type = type;
1156 type = NULL;
1157 }
1158
1159 if (_name) {
1160 *_name = name;
1161 name = NULL;
1162 }
1163
1164 return 0;
1165 }
1166
1167 int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
1168 const char* labels[DNS_N_LABELS_MAX+1];
1169 unsigned n = 0;
1170 const char *p;
1171 int r;
1172
1173 assert(name);
1174 assert(ret);
1175
1176 p = name;
1177 for (;;) {
1178 if (n > DNS_N_LABELS_MAX)
1179 return -EINVAL;
1180
1181 labels[n] = p;
1182
1183 r = dns_name_parent(&p);
1184 if (r < 0)
1185 return r;
1186 if (r == 0)
1187 break;
1188
1189 n++;
1190 }
1191
1192 if (n < n_labels)
1193 return -EINVAL;
1194
1195 *ret = labels[n - n_labels];
1196 return (int) (n - n_labels);
1197 }
1198
1199 int dns_name_count_labels(const char *name) {
1200 unsigned n = 0;
1201 const char *p;
1202 int r;
1203
1204 assert(name);
1205
1206 p = name;
1207 for (;;) {
1208 r = dns_name_parent(&p);
1209 if (r < 0)
1210 return r;
1211 if (r == 0)
1212 break;
1213
1214 if (n >= DNS_N_LABELS_MAX)
1215 return -EINVAL;
1216
1217 n++;
1218 }
1219
1220 return (int) n;
1221 }
1222
1223 int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
1224 int r;
1225
1226 assert(a);
1227 assert(b);
1228
1229 while (n_labels > 0) {
1230
1231 r = dns_name_parent(&a);
1232 if (r <= 0)
1233 return r;
1234
1235 n_labels --;
1236 }
1237
1238 return dns_name_equal(a, b);
1239 }