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