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