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