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