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