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