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