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