]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/dns-domain.c
Update mailmap and contributor list (#7006)
[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
349cc4a5 20#if HAVE_LIBIDN2
87057e24 21# include <idn2.h>
349cc4a5 22#elif HAVE_LIBIDN
87057e24
ZJS
23# include <idna.h>
24# include <stringprep.h>
bdf10b5b
LP
25#endif
26
a8fbdf54
TA
27#include <endian.h>
28#include <netinet/in.h>
29#include <stdio.h>
30#include <string.h>
31#include <sys/socket.h>
32
b5efdb8a 33#include "alloc-util.h"
4ad7f276 34#include "dns-domain.h"
a8fbdf54 35#include "hashmap.h"
e4e73a63 36#include "hexdecoct.h"
a8fbdf54
TA
37#include "in-addr-util.h"
38#include "macro.h"
6bedfcbb
LP
39#include "parse-util.h"
40#include "string-util.h"
dc477e73 41#include "strv.h"
0a49b6b6 42#include "utf8.h"
74b2466e
LP
43
44int dns_label_unescape(const char **name, char *dest, size_t sz) {
45 const char *n;
46 char *d;
47 int r = 0;
48
49 assert(name);
50 assert(*name);
74b2466e
LP
51
52 n = *name;
53 d = dest;
54
55 for (;;) {
56 if (*n == '.') {
57 n++;
58 break;
59 }
60
61 if (*n == 0)
62 break;
63
1fa65c59
LP
64 if (r >= DNS_LABEL_MAX)
65 return -EINVAL;
66
37ade128
LP
67 if (sz <= 0)
68 return -ENOBUFS;
69
74b2466e
LP
70 if (*n == '\\') {
71 /* Escaped character */
72
73 n++;
74
75 if (*n == 0)
76 /* Ending NUL */
77 return -EINVAL;
78
4c701096 79 else if (IN_SET(*n, '\\', '.')) {
74b2466e 80 /* Escaped backslash or dot */
f7455baa
LP
81
82 if (d)
83 *(d++) = *n;
74b2466e
LP
84 sz--;
85 r++;
f7455baa 86 n++;
74b2466e
LP
87
88 } else if (n[0] >= '0' && n[0] <= '9') {
89 unsigned k;
90
91 /* Escaped literal ASCII character */
92
93 if (!(n[1] >= '0' && n[1] <= '9') ||
94 !(n[2] >= '0' && n[2] <= '9'))
95 return -EINVAL;
96
97 k = ((unsigned) (n[0] - '0') * 100) +
98 ((unsigned) (n[1] - '0') * 10) +
99 ((unsigned) (n[2] - '0'));
100
c7feab76
LP
101 /* Don't allow anything that doesn't
102 * fit in 8bit. Note that we do allow
103 * control characters, as some servers
104 * (e.g. cloudflare) are happy to
105 * generate labels with them
106 * inside. */
107 if (k > 255)
74b2466e
LP
108 return -EINVAL;
109
f7455baa
LP
110 if (d)
111 *(d++) = (char) k;
74b2466e
LP
112 sz--;
113 r++;
114
115 n += 3;
116 } else
117 return -EINVAL;
118
07bed172 119 } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
74b2466e
LP
120
121 /* Normal character */
f7455baa
LP
122
123 if (d)
124 *(d++) = *n;
74b2466e
LP
125 sz--;
126 r++;
f7455baa 127 n++;
74b2466e
LP
128 } else
129 return -EINVAL;
130 }
131
132 /* Empty label that is not at the end? */
133 if (r == 0 && *n)
134 return -EINVAL;
135
f35c467d
MP
136 /* More than one trailing dot? */
137 if (*n == '.')
138 return -EINVAL;
139
f7455baa 140 if (sz >= 1 && d)
74b2466e
LP
141 *d = 0;
142
143 *name = n;
144 return r;
145}
146
642900d3
TG
147/* @label_terminal: terminal character of a label, updated to point to the terminal character of
148 * the previous label (always skipping one dot) or to NULL if there are no more
149 * labels. */
150int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
151 const char *terminal;
152 int r;
153
154 assert(name);
155 assert(label_terminal);
156 assert(dest);
157
158 /* no more labels */
159 if (!*label_terminal) {
160 if (sz >= 1)
161 *dest = 0;
162
163 return 0;
164 }
165
56512859 166 terminal = *label_terminal;
4c701096 167 assert(IN_SET(*terminal, 0, '.'));
642900d3 168
56512859
LP
169 /* Skip current terminal character (and accept domain names ending it ".") */
170 if (*terminal == 0)
171 terminal--;
172 if (terminal >= name && *terminal == '.')
173 terminal--;
642900d3 174
56512859 175 /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
642900d3
TG
176 for (;;) {
177 if (terminal < name) {
56512859 178 /* Reached the first label, so indicate that there are no more */
642900d3
TG
179 terminal = NULL;
180 break;
181 }
182
56512859 183 /* Find the start of the last label */
642900d3
TG
184 if (*terminal == '.') {
185 const char *y;
186 unsigned slashes = 0;
187
188 for (y = terminal - 1; y >= name && *y == '\\'; y--)
313cefa1 189 slashes++;
642900d3
TG
190
191 if (slashes % 2 == 0) {
56512859 192 /* The '.' was not escaped */
642900d3
TG
193 name = terminal + 1;
194 break;
195 } else {
196 terminal = y;
197 continue;
198 }
199 }
200
313cefa1 201 terminal--;
642900d3
TG
202 }
203
204 r = dns_label_unescape(&name, dest, sz);
205 if (r < 0)
206 return r;
207
208 *label_terminal = terminal;
209
210 return r;
211}
212
422baca0 213int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
74b2466e 214 char *q;
74b2466e 215
3b37fa73
LP
216 /* DNS labels must be between 1 and 63 characters long. A
217 * zero-length label does not exist. See RFC 2182, Section
218 * 11. */
219
220 if (l <= 0 || l > DNS_LABEL_MAX)
1fa65c59 221 return -EINVAL;
422baca0 222 if (sz < 1)
37ade128 223 return -ENOBUFS;
1fa65c59 224
422baca0
LP
225 assert(p);
226 assert(dest);
74b2466e 227
422baca0 228 q = dest;
74b2466e
LP
229 while (l > 0) {
230
4c701096 231 if (IN_SET(*p, '.', '\\')) {
74b2466e 232
3b37fa73
LP
233 /* Dot or backslash */
234
422baca0 235 if (sz < 3)
37ade128 236 return -ENOBUFS;
422baca0 237
74b2466e
LP
238 *(q++) = '\\';
239 *(q++) = *p;
240
422baca0
LP
241 sz -= 2;
242
4c701096 243 } else if (IN_SET(*p, '_', '-') ||
74b2466e
LP
244 (*p >= '0' && *p <= '9') ||
245 (*p >= 'a' && *p <= 'z') ||
246 (*p >= 'A' && *p <= 'Z')) {
247
248 /* Proper character */
422baca0
LP
249
250 if (sz < 2)
37ade128 251 return -ENOBUFS;
422baca0 252
74b2466e 253 *(q++) = *p;
422baca0
LP
254 sz -= 1;
255
c7feab76 256 } else {
74b2466e
LP
257
258 /* Everything else */
422baca0
LP
259
260 if (sz < 5)
37ade128 261 return -ENOBUFS;
422baca0 262
74b2466e 263 *(q++) = '\\';
07bed172
LP
264 *(q++) = '0' + (char) ((uint8_t) *p / 100);
265 *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
266 *(q++) = '0' + (char) ((uint8_t) *p % 10);
74b2466e 267
422baca0 268 sz -= 4;
c7feab76 269 }
74b2466e
LP
270
271 p++;
272 l--;
273 }
274
275 *q = 0;
422baca0
LP
276 return (int) (q - dest);
277}
278
279int dns_label_escape_new(const char *p, size_t l, char **ret) {
280 _cleanup_free_ char *s = NULL;
281 int r;
282
283 assert(p);
284 assert(ret);
285
3b37fa73 286 if (l <= 0 || l > DNS_LABEL_MAX)
422baca0
LP
287 return -EINVAL;
288
289 s = new(char, DNS_LABEL_ESCAPED_MAX);
290 if (!s)
291 return -ENOMEM;
292
293 r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
294 if (r < 0)
295 return r;
296
74b2466e 297 *ret = s;
74b2466e
LP
298 s = NULL;
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;
603 n = saved_n;
604 saved_n = NULL;
605 }
606 }
607}
608
eb241cdb
LP
609int dns_name_startswith(const char *name, const char *prefix) {
610 const char *n, *p;
611 int r, q;
612
613 assert(name);
614 assert(prefix);
615
616 n = name;
617 p = prefix;
618
619 for (;;) {
620 char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
621
23b298bc 622 r = dns_label_unescape(&p, lp, sizeof(lp));
eb241cdb
LP
623 if (r < 0)
624 return r;
625 if (r == 0)
626 return true;
627
23b298bc 628 q = dns_label_unescape(&n, ln, sizeof(ln));
eb241cdb
LP
629 if (q < 0)
630 return q;
631
632 if (r != q)
633 return false;
634 if (ascii_strcasecmp_n(ln, lp, r) != 0)
635 return false;
636 }
637}
638
58db254a
LP
639int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
640 const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
81ec9e08 641 int r, q;
58db254a
LP
642
643 assert(name);
644 assert(old_suffix);
645 assert(new_suffix);
646 assert(ret);
647
648 n = name;
649 s = old_suffix;
650
651 for (;;) {
3095011d 652 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
58db254a
LP
653
654 if (!saved_before)
655 saved_before = n;
656
23b298bc 657 r = dns_label_unescape(&n, ln, sizeof(ln));
58db254a
LP
658 if (r < 0)
659 return r;
58db254a
LP
660
661 if (!saved_after)
662 saved_after = n;
663
23b298bc 664 q = dns_label_unescape(&s, ls, sizeof(ls));
58db254a
LP
665 if (q < 0)
666 return q;
58db254a
LP
667
668 if (r == 0 && q == 0)
669 break;
670 if (r == 0 && saved_after == n) {
671 *ret = NULL; /* doesn't match */
672 return 0;
673 }
674
3095011d 675 if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
58db254a
LP
676
677 /* Not the same, let's jump back, and try with the next label again */
678 s = old_suffix;
679 n = saved_after;
680 saved_after = saved_before = NULL;
681 }
682 }
683
684 /* Found it! Now generate the new name */
685 prefix = strndupa(name, saved_before - name);
686
687 r = dns_name_concat(prefix, new_suffix, ret);
688 if (r < 0)
689 return r;
690
691 return 1;
692}
693
ae72b22c
TG
694int dns_name_between(const char *a, const char *b, const char *c) {
695 int n;
696
697 /* Determine if b is strictly greater than a and strictly smaller than c.
698 We consider the order of names to be circular, so that if a is
699 strictly greater than c, we consider b to be between them if it is
700 either greater than a or smaller than c. This is how the canonical
701 DNS name order used in NSEC records work. */
702
703 n = dns_name_compare_func(a, c);
704 if (n == 0)
705 return -EINVAL;
706 else if (n < 0)
707 /* a<---b--->c */
708 return dns_name_compare_func(a, b) < 0 &&
709 dns_name_compare_func(b, c) < 0;
710 else
711 /* <--b--c a--b--> */
712 return dns_name_compare_func(b, c) < 0 ||
713 dns_name_compare_func(a, b) < 0;
714}
715
74b2466e
LP
716int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
717 const uint8_t *p;
718 int r;
719
720 assert(a);
721 assert(ret);
722
723 p = (const uint8_t*) a;
724
725 if (family == AF_INET)
726 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
727 else if (family == AF_INET6)
3fe1169f
LP
728 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",
729 hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
730 hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
731 hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
732 hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
733 hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
734 hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
735 hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
736 hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
74b2466e
LP
737 else
738 return -EAFNOSUPPORT;
739 if (r < 0)
740 return -ENOMEM;
741
742 return 0;
743}
744
b914e211
LP
745int dns_name_address(const char *p, int *family, union in_addr_union *address) {
746 int r;
747
748 assert(p);
749 assert(family);
750 assert(address);
751
752 r = dns_name_endswith(p, "in-addr.arpa");
753 if (r < 0)
754 return r;
755 if (r > 0) {
756 uint8_t a[4];
757 unsigned i;
758
759 for (i = 0; i < ELEMENTSOF(a); i++) {
760 char label[DNS_LABEL_MAX+1];
761
762 r = dns_label_unescape(&p, label, sizeof(label));
763 if (r < 0)
764 return r;
765 if (r == 0)
766 return -EINVAL;
767 if (r > 3)
768 return -EINVAL;
769
770 r = safe_atou8(label, &a[i]);
771 if (r < 0)
772 return r;
773 }
774
775 r = dns_name_equal(p, "in-addr.arpa");
776 if (r <= 0)
777 return r;
778
779 *family = AF_INET;
780 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
781 ((uint32_t) a[2] << 16) |
782 ((uint32_t) a[1] << 8) |
783 (uint32_t) a[0]);
784
785 return 1;
786 }
787
788 r = dns_name_endswith(p, "ip6.arpa");
789 if (r < 0)
790 return r;
791 if (r > 0) {
792 struct in6_addr a;
793 unsigned i;
794
795 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
796 char label[DNS_LABEL_MAX+1];
797 int x, y;
798
799 r = dns_label_unescape(&p, label, sizeof(label));
800 if (r <= 0)
801 return r;
802 if (r != 1)
803 return -EINVAL;
804 x = unhexchar(label[0]);
805 if (x < 0)
806 return -EINVAL;
807
808 r = dns_label_unescape(&p, label, sizeof(label));
809 if (r <= 0)
810 return r;
811 if (r != 1)
812 return -EINVAL;
813 y = unhexchar(label[0]);
814 if (y < 0)
815 return -EINVAL;
816
817 a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
818 }
819
820 r = dns_name_equal(p, "ip6.arpa");
821 if (r <= 0)
822 return r;
823
824 *family = AF_INET6;
825 address->in6 = a;
826 return 1;
827 }
828
829 return 0;
830}
831
dc477e73 832bool dns_name_is_root(const char *name) {
74b2466e
LP
833
834 assert(name);
835
dc477e73
LP
836 /* There are exactly two ways to encode the root domain name:
837 * as empty string, or with a single dot. */
74b2466e 838
dc477e73 839 return STR_IN_SET(name, "", ".");
74b2466e
LP
840}
841
dc477e73 842bool dns_name_is_single_label(const char *name) {
74b2466e
LP
843 int r;
844
845 assert(name);
846
f6fbd9c2 847 r = dns_name_parent(&name);
dc477e73
LP
848 if (r <= 0)
849 return false;
74b2466e 850
dc477e73 851 return dns_name_is_root(name);
74b2466e 852}
54adabf7 853
50dee79b 854/* Encode a domain name according to RFC 1035 Section 3.1, without compression */
3cd03457 855int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
c6cefd13 856 uint8_t *label_length, *out;
54adabf7
BG
857 int r;
858
c6cefd13
LP
859 assert(domain);
860 assert(buffer);
54adabf7
BG
861
862 out = buffer;
863
864 do {
50dee79b 865 /* Reserve a byte for label length */
c6cefd13 866 if (len <= 0)
54adabf7
BG
867 return -ENOBUFS;
868 len--;
869 label_length = out;
870 out++;
871
50dee79b
LP
872 /* Convert and copy a single label. Note that
873 * dns_label_unescape() returns 0 when it hits the end
874 * of the domain name, which we rely on here to encode
875 * the trailing NUL byte. */
54adabf7
BG
876 r = dns_label_unescape(&domain, (char *) out, len);
877 if (r < 0)
878 return r;
879
b577e3d5
LP
880 /* Optionally, output the name in DNSSEC canonical
881 * format, as described in RFC 4034, section 6.2. Or
882 * in other words: in lower-case. */
883 if (canonical)
884 ascii_strlower_n((char*) out, (size_t) r);
3cd03457 885
50dee79b 886 /* Fill label length, move forward */
54adabf7
BG
887 *label_length = r;
888 out += r;
889 len -= r;
50dee79b 890
54adabf7
BG
891 } while (r != 0);
892
50dee79b
LP
893 /* Verify the maximum size of the encoded name. The trailing
894 * dot + NUL byte account are included this time, hence
895 * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
896 * time. */
897 if (out - buffer > DNS_HOSTNAME_MAX + 2)
898 return -EINVAL;
899
54adabf7
BG
900 return out - buffer;
901}
0a49b6b6 902
0e8eedbb
LP
903static bool srv_type_label_is_valid(const char *label, size_t n) {
904 size_t k;
905
906 assert(label);
907
908 if (n < 2) /* Label needs to be at least 2 chars long */
909 return false;
910
911 if (label[0] != '_') /* First label char needs to be underscore */
912 return false;
913
914 /* Second char must be a letter */
915 if (!(label[1] >= 'A' && label[1] <= 'Z') &&
916 !(label[1] >= 'a' && label[1] <= 'z'))
917 return false;
918
919 /* Third and further chars must be alphanumeric or a hyphen */
920 for (k = 2; k < n; k++) {
921 if (!(label[k] >= 'A' && label[k] <= 'Z') &&
922 !(label[k] >= 'a' && label[k] <= 'z') &&
923 !(label[k] >= '0' && label[k] <= '9') &&
924 label[k] != '-')
925 return false;
926 }
927
928 return true;
929}
930
7e8131e9 931bool dns_srv_type_is_valid(const char *name) {
0a49b6b6
LP
932 unsigned c = 0;
933 int r;
934
935 if (!name)
7e8131e9 936 return false;
0a49b6b6
LP
937
938 for (;;) {
939 char label[DNS_LABEL_MAX];
0a49b6b6
LP
940
941 /* This more or less implements RFC 6335, Section 5.1 */
942
943 r = dns_label_unescape(&name, label, sizeof(label));
0a49b6b6 944 if (r < 0)
7e8131e9 945 return false;
0a49b6b6 946 if (r == 0)
0e8eedbb 947 break;
0a49b6b6 948
0e8eedbb 949 if (c >= 2)
7e8131e9 950 return false;
0a49b6b6 951
0e8eedbb 952 if (!srv_type_label_is_valid(label, r))
7e8131e9 953 return false;
0a49b6b6
LP
954
955 c++;
956 }
0e8eedbb
LP
957
958 return c == 2; /* exactly two labels */
0a49b6b6
LP
959}
960
961bool dns_service_name_is_valid(const char *name) {
962 size_t l;
963
964 /* This more or less implements RFC 6763, Section 4.1.1 */
965
966 if (!name)
967 return false;
968
969 if (!utf8_is_valid(name))
970 return false;
971
972 if (string_has_cc(name, NULL))
973 return false;
974
975 l = strlen(name);
976 if (l <= 0)
977 return false;
978 if (l > 63)
979 return false;
980
981 return true;
982}
0e8eedbb
LP
983
984int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
422baca0
LP
985 char escaped[DNS_LABEL_ESCAPED_MAX];
986 _cleanup_free_ char *n = NULL;
0e8eedbb
LP
987 int r;
988
989 assert(type);
990 assert(domain);
991 assert(ret);
992
7e8131e9 993 if (!dns_srv_type_is_valid(type))
0e8eedbb
LP
994 return -EINVAL;
995
996 if (!name)
997 return dns_name_concat(type, domain, ret);
998
999 if (!dns_service_name_is_valid(name))
1000 return -EINVAL;
1001
422baca0 1002 r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
0e8eedbb
LP
1003 if (r < 0)
1004 return r;
1005
1006 r = dns_name_concat(type, domain, &n);
1007 if (r < 0)
1008 return r;
1009
1010 return dns_name_concat(escaped, n, ret);
1011}
1012
1013static bool dns_service_name_label_is_valid(const char *label, size_t n) {
1014 char *s;
1015
1016 assert(label);
1017
1018 if (memchr(label, 0, n))
1019 return false;
1020
1021 s = strndupa(label, n);
1022 return dns_service_name_is_valid(s);
1023}
1024
1025int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
1026 _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
1027 const char *p = joined, *q = NULL, *d = NULL;
1028 char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
1029 int an, bn, cn, r;
1030 unsigned x = 0;
1031
1032 assert(joined);
1033
1034 /* Get first label from the full name */
1035 an = dns_label_unescape(&p, a, sizeof(a));
1036 if (an < 0)
1037 return an;
1038
1039 if (an > 0) {
1040 x++;
1041
1042 /* If there was a first label, try to get the second one */
1043 bn = dns_label_unescape(&p, b, sizeof(b));
1044 if (bn < 0)
1045 return bn;
1046
1047 if (bn > 0) {
1048 x++;
1049
1050 /* If there was a second label, try to get the third one */
1051 q = p;
1052 cn = dns_label_unescape(&p, c, sizeof(c));
1053 if (cn < 0)
1054 return cn;
1055
1056 if (cn > 0)
1057 x++;
1058 } else
1059 cn = 0;
1060 } else
1061 an = 0;
1062
1063 if (x >= 2 && srv_type_label_is_valid(b, bn)) {
1064
1065 if (x >= 3 && srv_type_label_is_valid(c, cn)) {
1066
1067 if (dns_service_name_label_is_valid(a, an)) {
0e8eedbb
LP
1068 /* OK, got <name> . <type> . <type2> . <domain> */
1069
1070 name = strndup(a, an);
1071 if (!name)
1072 return -ENOMEM;
1073
605405c6 1074 type = strjoin(b, ".", c);
0e8eedbb
LP
1075 if (!type)
1076 return -ENOMEM;
0e8eedbb
LP
1077
1078 d = p;
1079 goto finish;
1080 }
1081
1082 } else if (srv_type_label_is_valid(a, an)) {
1083
1084 /* OK, got <type> . <type2> . <domain> */
1085
1086 name = NULL;
1087
605405c6 1088 type = strjoin(a, ".", b);
0e8eedbb
LP
1089 if (!type)
1090 return -ENOMEM;
0e8eedbb
LP
1091
1092 d = q;
1093 goto finish;
1094 }
1095 }
1096
1097 name = NULL;
1098 type = NULL;
1099 d = joined;
1100
1101finish:
1102 r = dns_name_normalize(d, &domain);
1103 if (r < 0)
1104 return r;
1105
1106 if (_domain) {
1107 *_domain = domain;
1108 domain = NULL;
1109 }
1110
1111 if (_type) {
1112 *_type = type;
1113 type = NULL;
1114 }
1115
1116 if (_name) {
1117 *_name = name;
1118 name = NULL;
1119 }
1120
1121 return 0;
1122}
e7ff0e0b 1123
b9282bc1 1124static int dns_name_build_suffix_table(const char *name, const char*table[]) {
e7ff0e0b 1125 const char *p;
b9282bc1 1126 unsigned n = 0;
e7ff0e0b
LP
1127 int r;
1128
1129 assert(name);
b9282bc1 1130 assert(table);
e7ff0e0b
LP
1131
1132 p = name;
1133 for (;;) {
1134 if (n > DNS_N_LABELS_MAX)
1135 return -EINVAL;
1136
b9282bc1 1137 table[n] = p;
e7ff0e0b
LP
1138 r = dns_name_parent(&p);
1139 if (r < 0)
1140 return r;
1141 if (r == 0)
1142 break;
1143
1144 n++;
1145 }
1146
b9282bc1
LP
1147 return (int) n;
1148}
1149
1150int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
1151 const char* labels[DNS_N_LABELS_MAX+1];
1152 int n;
1153
1154 assert(name);
1155 assert(ret);
1156
1157 n = dns_name_build_suffix_table(name, labels);
1158 if (n < 0)
1159 return n;
1160
1161 if ((unsigned) n < n_labels)
e7ff0e0b
LP
1162 return -EINVAL;
1163
1164 *ret = labels[n - n_labels];
1165 return (int) (n - n_labels);
1166}
1167
97c67192
LP
1168int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
1169 int r;
1170
1171 assert(a);
1172 assert(ret);
1173
313cefa1 1174 for (; n_labels > 0; n_labels--) {
97c67192
LP
1175 r = dns_name_parent(&a);
1176 if (r < 0)
1177 return r;
1178 if (r == 0) {
1179 *ret = "";
1180 return 0;
1181 }
1182 }
1183
1184 *ret = a;
1185 return 1;
1186}
1187
e7ff0e0b
LP
1188int dns_name_count_labels(const char *name) {
1189 unsigned n = 0;
1190 const char *p;
1191 int r;
1192
1193 assert(name);
1194
1195 p = name;
1196 for (;;) {
1197 r = dns_name_parent(&p);
1198 if (r < 0)
1199 return r;
1200 if (r == 0)
1201 break;
1202
1203 if (n >= DNS_N_LABELS_MAX)
1204 return -EINVAL;
1205
1206 n++;
1207 }
1208
1209 return (int) n;
1210}
db5b0e92
LP
1211
1212int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
1213 int r;
1214
1215 assert(a);
1216 assert(b);
1217
97c67192
LP
1218 r = dns_name_skip(a, n_labels, &a);
1219 if (r <= 0)
1220 return r;
db5b0e92
LP
1221
1222 return dns_name_equal(a, b);
1223}
b9282bc1
LP
1224
1225int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
1226 const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1];
1227 int n = 0, m = 0, k = 0, r, q;
1228
1229 assert(a);
1230 assert(b);
1231 assert(ret);
1232
1233 /* Determines the common suffix of domain names a and b */
1234
1235 n = dns_name_build_suffix_table(a, a_labels);
1236 if (n < 0)
1237 return n;
1238
1239 m = dns_name_build_suffix_table(b, b_labels);
1240 if (m < 0)
1241 return m;
1242
1243 for (;;) {
1244 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
1245 const char *x, *y;
1246
1247 if (k >= n || k >= m) {
1248 *ret = a_labels[n - k];
1249 return 0;
1250 }
1251
1252 x = a_labels[n - 1 - k];
23b298bc 1253 r = dns_label_unescape(&x, la, sizeof(la));
b9282bc1
LP
1254 if (r < 0)
1255 return r;
1256
1257 y = b_labels[m - 1 - k];
23b298bc 1258 q = dns_label_unescape(&y, lb, sizeof(lb));
b9282bc1
LP
1259 if (q < 0)
1260 return q;
1261
1262 if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) {
1263 *ret = a_labels[n - k];
1264 return 0;
1265 }
1266
1267 k++;
1268 }
1269}
0cf40f55
LP
1270
1271int dns_name_apply_idna(const char *name, char **ret) {
87057e24
ZJS
1272 /* Return negative on error, 0 if not implemented, positive on success. */
1273
349cc4a5 1274#if HAVE_LIBIDN2
87057e24 1275 int r;
0926f348 1276 _cleanup_free_ char *t = NULL;
87057e24
ZJS
1277
1278 assert(name);
1279 assert(ret);
1280
0926f348 1281 r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
87057e24 1282 IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
0926f348
ZJS
1283 log_debug("idn2_lookup_u8: %s → %s", name, t);
1284 if (r == IDN2_OK) {
1285 if (!startswith(name, "xn--")) {
1286 _cleanup_free_ char *s = NULL;
1287
1288 r = idn2_to_unicode_8z8z(t, &s, 0);
1289 if (r != IDN2_OK) {
1290 log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s",
1291 t, r, idn2_strerror(r));
1292 return 0;
1293 }
1294
1295 if (!streq_ptr(name, s)) {
1296 log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.",
1297 name, t, s);
1298 return 0;
1299 }
1300 }
1301
1302 *ret = t;
1303 t = NULL;
87057e24 1304 return 1; /* *ret has been written */
0926f348
ZJS
1305 }
1306
1307 log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
ad1f3fe6
ZJS
1308 if (r == IDN2_2HYPHEN)
1309 /* The name has two hypens — forbidden by IDNA2008 in some cases */
1310 return 0;
1311 if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
87057e24 1312 return -ENOSPC;
ad1f3fe6 1313 return -EINVAL;
349cc4a5 1314#elif HAVE_LIBIDN
0cf40f55
LP
1315 _cleanup_free_ char *buf = NULL;
1316 size_t n = 0, allocated = 0;
1317 bool first = true;
1318 int r, q;
1319
1320 assert(name);
1321 assert(ret);
1322
1323 for (;;) {
1324 char label[DNS_LABEL_MAX];
1325
1326 r = dns_label_unescape(&name, label, sizeof(label));
1327 if (r < 0)
1328 return r;
1329 if (r == 0)
1330 break;
1331
1332 q = dns_label_apply_idna(label, r, label, sizeof(label));
1333 if (q < 0)
1334 return q;
1335 if (q > 0)
1336 r = q;
1337
1338 if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
1339 return -ENOMEM;
1340
1341 r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
1342 if (r < 0)
1343 return r;
1344
1345 if (first)
1346 first = false;
1347 else
1348 buf[n++] = '.';
1349
ad1f3fe6 1350 n += r;
0cf40f55
LP
1351 }
1352
1353 if (n > DNS_HOSTNAME_MAX)
1354 return -EINVAL;
1355
1356 if (!GREEDY_REALLOC(buf, allocated, n + 1))
1357 return -ENOMEM;
1358
1359 buf[n] = 0;
1360 *ret = buf;
1361 buf = NULL;
1362
ad1f3fe6 1363 return 1;
87057e24
ZJS
1364#else
1365 return 0;
1366#endif
0cf40f55 1367}
08a4849e
LP
1368
1369int dns_name_is_valid_or_address(const char *name) {
1370 /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */
1371
1372 if (isempty(name))
1373 return 0;
1374
1375 if (in_addr_from_string_auto(name, NULL, NULL) >= 0)
1376 return 1;
1377
1378 return dns_name_is_valid(name);
1379}