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