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