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