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