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