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