]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/dns-domain.c
basic: split hash functions into their own header files
[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
d12315a4 489 for (;;) {
74b2466e 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;
d12315a4
LP
496 if (r == 0)
497 break;
74b2466e 498
bdf10b5b
LP
499 k = dns_label_undo_idna(label, r, label, sizeof(label));
500 if (k < 0)
7da40fc1 501 break;
bdf10b5b
LP
502 if (k > 0)
503 r = k;
504
509eddd2
LP
505 ascii_strlower_n(label, r);
506 siphash24_compress(label, r, state);
74b2466e 507 }
1e2527a6
TG
508
509 /* enforce that all names are terminated by the empty label */
510 string_hash_func("", state);
74b2466e
LP
511}
512
513int dns_name_compare_func(const void *a, const void *b) {
5dfd7011 514 const char *x, *y;
bdf10b5b 515 int r, q, k, w;
74b2466e
LP
516
517 assert(a);
518 assert(b);
519
5dfd7011
TG
520 x = (const char *) a + strlen(a);
521 y = (const char *) b + strlen(b);
522
74b2466e
LP
523 for (;;) {
524 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
525
5dfd7011 526 if (x == NULL && y == NULL)
74b2466e
LP
527 return 0;
528
5dfd7011
TG
529 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
530 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
74b2466e
LP
531 if (r < 0 || q < 0)
532 return r - q;
533
bdf10b5b
LP
534 k = dns_label_undo_idna(la, r, la, sizeof(la));
535 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
536 if (k < 0 || w < 0)
537 return k - w;
538 if (k > 0)
539 r = k;
540 if (w > 0)
735323d9 541 q = w;
bdf10b5b 542
74b2466e
LP
543 la[r] = lb[q] = 0;
544 r = strcasecmp(la, lb);
545 if (r != 0)
546 return r;
547 }
548}
549
d5099efc
MS
550const struct hash_ops dns_name_hash_ops = {
551 .hash = dns_name_hash_func,
552 .compare = dns_name_compare_func
553};
554
74b2466e 555int dns_name_equal(const char *x, const char *y) {
bdf10b5b 556 int r, q, k, w;
74b2466e
LP
557
558 assert(x);
559 assert(y);
560
561 for (;;) {
562 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
563
564 if (*x == 0 && *y == 0)
565 return true;
566
567 r = dns_label_unescape(&x, la, sizeof(la));
568 if (r < 0)
569 return r;
3b37fa73
LP
570 if (r > 0) {
571 k = dns_label_undo_idna(la, r, la, sizeof(la));
572 if (k < 0)
573 return k;
574 if (k > 0)
575 r = k;
576 }
bdf10b5b 577
74b2466e
LP
578 q = dns_label_unescape(&y, lb, sizeof(lb));
579 if (q < 0)
580 return q;
3b37fa73
LP
581 if (q > 0) {
582 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
583 if (w < 0)
584 return w;
585 if (w > 0)
586 q = w;
587 }
588
589 /* If one name had fewer labels than the other, this
590 * will show up as empty label here, which the
591 * strcasecmp() below will properly consider different
592 * from a non-empty label. */
74b2466e
LP
593
594 la[r] = lb[q] = 0;
3b37fa73 595 if (strcasecmp(la, lb) != 0)
74b2466e
LP
596 return false;
597 }
598}
599
600int dns_name_endswith(const char *name, const char *suffix) {
601 const char *n, *s, *saved_n = NULL;
bdf10b5b 602 int r, q, k, w;
74b2466e
LP
603
604 assert(name);
605 assert(suffix);
606
607 n = name;
608 s = suffix;
609
610 for (;;) {
611 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
612
613 r = dns_label_unescape(&n, ln, sizeof(ln));
614 if (r < 0)
615 return r;
3b37fa73
LP
616 if (r > 0) {
617 k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
618 if (k < 0)
619 return k;
620 if (k > 0)
621 r = k;
622 }
74b2466e
LP
623
624 if (!saved_n)
625 saved_n = n;
626
627 q = dns_label_unescape(&s, ls, sizeof(ls));
be754d54
LP
628 if (q < 0)
629 return q;
3b37fa73
LP
630 if (q > 0) {
631 w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
632 if (w < 0)
633 return w;
634 if (w > 0)
635 q = w;
636 }
74b2466e
LP
637
638 if (r == 0 && q == 0)
639 return true;
640 if (r == 0 && saved_n == n)
641 return false;
642
643 ln[r] = ls[q] = 0;
644
645 if (r != q || strcasecmp(ln, ls)) {
646
647 /* Not the same, let's jump back, and try with the next label again */
648 s = suffix;
649 n = saved_n;
650 saved_n = NULL;
651 }
652 }
653}
654
58db254a
LP
655int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
656 const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
657 int r, q, k, w;
658
659 assert(name);
660 assert(old_suffix);
661 assert(new_suffix);
662 assert(ret);
663
664 n = name;
665 s = old_suffix;
666
667 for (;;) {
668 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
669
670 if (!saved_before)
671 saved_before = n;
672
673 r = dns_label_unescape(&n, ln, sizeof(ln));
674 if (r < 0)
675 return r;
3b37fa73
LP
676 if (r > 0) {
677 k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
678 if (k < 0)
679 return k;
680 if (k > 0)
681 r = k;
682 }
58db254a
LP
683
684 if (!saved_after)
685 saved_after = n;
686
687 q = dns_label_unescape(&s, ls, sizeof(ls));
688 if (q < 0)
689 return q;
3b37fa73
LP
690 if (q > 0) {
691 w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
692 if (w < 0)
693 return w;
694 if (w > 0)
695 q = w;
696 }
58db254a
LP
697
698 if (r == 0 && q == 0)
699 break;
700 if (r == 0 && saved_after == n) {
701 *ret = NULL; /* doesn't match */
702 return 0;
703 }
704
705 ln[r] = ls[q] = 0;
706
707 if (r != q || strcasecmp(ln, ls)) {
708
709 /* Not the same, let's jump back, and try with the next label again */
710 s = old_suffix;
711 n = saved_after;
712 saved_after = saved_before = NULL;
713 }
714 }
715
716 /* Found it! Now generate the new name */
717 prefix = strndupa(name, saved_before - name);
718
719 r = dns_name_concat(prefix, new_suffix, ret);
720 if (r < 0)
721 return r;
722
723 return 1;
724}
725
ae72b22c
TG
726int dns_name_between(const char *a, const char *b, const char *c) {
727 int n;
728
729 /* Determine if b is strictly greater than a and strictly smaller than c.
730 We consider the order of names to be circular, so that if a is
731 strictly greater than c, we consider b to be between them if it is
732 either greater than a or smaller than c. This is how the canonical
733 DNS name order used in NSEC records work. */
734
735 n = dns_name_compare_func(a, c);
736 if (n == 0)
737 return -EINVAL;
738 else if (n < 0)
739 /* a<---b--->c */
740 return dns_name_compare_func(a, b) < 0 &&
741 dns_name_compare_func(b, c) < 0;
742 else
743 /* <--b--c a--b--> */
744 return dns_name_compare_func(b, c) < 0 ||
745 dns_name_compare_func(a, b) < 0;
746}
747
74b2466e
LP
748int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
749 const uint8_t *p;
750 int r;
751
752 assert(a);
753 assert(ret);
754
755 p = (const uint8_t*) a;
756
757 if (family == AF_INET)
758 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
759 else if (family == AF_INET6)
3fe1169f
LP
760 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",
761 hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
762 hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
763 hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
764 hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
765 hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
766 hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
767 hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
768 hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
74b2466e
LP
769 else
770 return -EAFNOSUPPORT;
771 if (r < 0)
772 return -ENOMEM;
773
774 return 0;
775}
776
b914e211
LP
777int dns_name_address(const char *p, int *family, union in_addr_union *address) {
778 int r;
779
780 assert(p);
781 assert(family);
782 assert(address);
783
784 r = dns_name_endswith(p, "in-addr.arpa");
785 if (r < 0)
786 return r;
787 if (r > 0) {
788 uint8_t a[4];
789 unsigned i;
790
791 for (i = 0; i < ELEMENTSOF(a); i++) {
792 char label[DNS_LABEL_MAX+1];
793
794 r = dns_label_unescape(&p, label, sizeof(label));
795 if (r < 0)
796 return r;
797 if (r == 0)
798 return -EINVAL;
799 if (r > 3)
800 return -EINVAL;
801
802 r = safe_atou8(label, &a[i]);
803 if (r < 0)
804 return r;
805 }
806
807 r = dns_name_equal(p, "in-addr.arpa");
808 if (r <= 0)
809 return r;
810
811 *family = AF_INET;
812 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
813 ((uint32_t) a[2] << 16) |
814 ((uint32_t) a[1] << 8) |
815 (uint32_t) a[0]);
816
817 return 1;
818 }
819
820 r = dns_name_endswith(p, "ip6.arpa");
821 if (r < 0)
822 return r;
823 if (r > 0) {
824 struct in6_addr a;
825 unsigned i;
826
827 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
828 char label[DNS_LABEL_MAX+1];
829 int x, y;
830
831 r = dns_label_unescape(&p, label, sizeof(label));
832 if (r <= 0)
833 return r;
834 if (r != 1)
835 return -EINVAL;
836 x = unhexchar(label[0]);
837 if (x < 0)
838 return -EINVAL;
839
840 r = dns_label_unescape(&p, label, sizeof(label));
841 if (r <= 0)
842 return r;
843 if (r != 1)
844 return -EINVAL;
845 y = unhexchar(label[0]);
846 if (y < 0)
847 return -EINVAL;
848
849 a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
850 }
851
852 r = dns_name_equal(p, "ip6.arpa");
853 if (r <= 0)
854 return r;
855
856 *family = AF_INET6;
857 address->in6 = a;
858 return 1;
859 }
860
861 return 0;
862}
863
dc477e73 864bool dns_name_is_root(const char *name) {
74b2466e
LP
865
866 assert(name);
867
dc477e73
LP
868 /* There are exactly two ways to encode the root domain name:
869 * as empty string, or with a single dot. */
74b2466e 870
dc477e73 871 return STR_IN_SET(name, "", ".");
74b2466e
LP
872}
873
dc477e73 874bool dns_name_is_single_label(const char *name) {
74b2466e
LP
875 char label[DNS_LABEL_MAX+1];
876 int r;
877
878 assert(name);
879
880 r = dns_label_unescape(&name, label, sizeof(label));
dc477e73
LP
881 if (r <= 0)
882 return false;
74b2466e 883
dc477e73 884 return dns_name_is_root(name);
74b2466e 885}
54adabf7 886
50dee79b 887/* Encode a domain name according to RFC 1035 Section 3.1, without compression */
3cd03457 888int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
c6cefd13 889 uint8_t *label_length, *out;
54adabf7
BG
890 int r;
891
c6cefd13
LP
892 assert(domain);
893 assert(buffer);
54adabf7
BG
894
895 out = buffer;
896
897 do {
50dee79b 898 /* Reserve a byte for label length */
c6cefd13 899 if (len <= 0)
54adabf7
BG
900 return -ENOBUFS;
901 len--;
902 label_length = out;
903 out++;
904
50dee79b
LP
905 /* Convert and copy a single label. Note that
906 * dns_label_unescape() returns 0 when it hits the end
907 * of the domain name, which we rely on here to encode
908 * the trailing NUL byte. */
54adabf7
BG
909 r = dns_label_unescape(&domain, (char *) out, len);
910 if (r < 0)
911 return r;
912
b577e3d5
LP
913 /* Optionally, output the name in DNSSEC canonical
914 * format, as described in RFC 4034, section 6.2. Or
915 * in other words: in lower-case. */
916 if (canonical)
917 ascii_strlower_n((char*) out, (size_t) r);
3cd03457 918
50dee79b 919 /* Fill label length, move forward */
54adabf7
BG
920 *label_length = r;
921 out += r;
922 len -= r;
50dee79b 923
54adabf7
BG
924 } while (r != 0);
925
50dee79b
LP
926 /* Verify the maximum size of the encoded name. The trailing
927 * dot + NUL byte account are included this time, hence
928 * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
929 * time. */
930 if (out - buffer > DNS_HOSTNAME_MAX + 2)
931 return -EINVAL;
932
54adabf7
BG
933 return out - buffer;
934}
0a49b6b6 935
0e8eedbb
LP
936static bool srv_type_label_is_valid(const char *label, size_t n) {
937 size_t k;
938
939 assert(label);
940
941 if (n < 2) /* Label needs to be at least 2 chars long */
942 return false;
943
944 if (label[0] != '_') /* First label char needs to be underscore */
945 return false;
946
947 /* Second char must be a letter */
948 if (!(label[1] >= 'A' && label[1] <= 'Z') &&
949 !(label[1] >= 'a' && label[1] <= 'z'))
950 return false;
951
952 /* Third and further chars must be alphanumeric or a hyphen */
953 for (k = 2; k < n; k++) {
954 if (!(label[k] >= 'A' && label[k] <= 'Z') &&
955 !(label[k] >= 'a' && label[k] <= 'z') &&
956 !(label[k] >= '0' && label[k] <= '9') &&
957 label[k] != '-')
958 return false;
959 }
960
961 return true;
962}
963
7e8131e9 964bool dns_srv_type_is_valid(const char *name) {
0a49b6b6
LP
965 unsigned c = 0;
966 int r;
967
968 if (!name)
7e8131e9 969 return false;
0a49b6b6
LP
970
971 for (;;) {
972 char label[DNS_LABEL_MAX];
0a49b6b6
LP
973
974 /* This more or less implements RFC 6335, Section 5.1 */
975
976 r = dns_label_unescape(&name, label, sizeof(label));
0a49b6b6 977 if (r < 0)
7e8131e9 978 return false;
0a49b6b6 979 if (r == 0)
0e8eedbb 980 break;
0a49b6b6 981
0e8eedbb 982 if (c >= 2)
7e8131e9 983 return false;
0a49b6b6 984
0e8eedbb 985 if (!srv_type_label_is_valid(label, r))
7e8131e9 986 return false;
0a49b6b6
LP
987
988 c++;
989 }
0e8eedbb
LP
990
991 return c == 2; /* exactly two labels */
0a49b6b6
LP
992}
993
994bool dns_service_name_is_valid(const char *name) {
995 size_t l;
996
997 /* This more or less implements RFC 6763, Section 4.1.1 */
998
999 if (!name)
1000 return false;
1001
1002 if (!utf8_is_valid(name))
1003 return false;
1004
1005 if (string_has_cc(name, NULL))
1006 return false;
1007
1008 l = strlen(name);
1009 if (l <= 0)
1010 return false;
1011 if (l > 63)
1012 return false;
1013
1014 return true;
1015}
0e8eedbb
LP
1016
1017int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
422baca0
LP
1018 char escaped[DNS_LABEL_ESCAPED_MAX];
1019 _cleanup_free_ char *n = NULL;
0e8eedbb
LP
1020 int r;
1021
1022 assert(type);
1023 assert(domain);
1024 assert(ret);
1025
7e8131e9 1026 if (!dns_srv_type_is_valid(type))
0e8eedbb
LP
1027 return -EINVAL;
1028
1029 if (!name)
1030 return dns_name_concat(type, domain, ret);
1031
1032 if (!dns_service_name_is_valid(name))
1033 return -EINVAL;
1034
422baca0 1035 r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
0e8eedbb
LP
1036 if (r < 0)
1037 return r;
1038
1039 r = dns_name_concat(type, domain, &n);
1040 if (r < 0)
1041 return r;
1042
1043 return dns_name_concat(escaped, n, ret);
1044}
1045
1046static bool dns_service_name_label_is_valid(const char *label, size_t n) {
1047 char *s;
1048
1049 assert(label);
1050
1051 if (memchr(label, 0, n))
1052 return false;
1053
1054 s = strndupa(label, n);
1055 return dns_service_name_is_valid(s);
1056}
1057
1058int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
1059 _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
1060 const char *p = joined, *q = NULL, *d = NULL;
1061 char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
1062 int an, bn, cn, r;
1063 unsigned x = 0;
1064
1065 assert(joined);
1066
1067 /* Get first label from the full name */
1068 an = dns_label_unescape(&p, a, sizeof(a));
1069 if (an < 0)
1070 return an;
1071
1072 if (an > 0) {
1073 x++;
1074
1075 /* If there was a first label, try to get the second one */
1076 bn = dns_label_unescape(&p, b, sizeof(b));
1077 if (bn < 0)
1078 return bn;
1079
1080 if (bn > 0) {
1081 x++;
1082
1083 /* If there was a second label, try to get the third one */
1084 q = p;
1085 cn = dns_label_unescape(&p, c, sizeof(c));
1086 if (cn < 0)
1087 return cn;
1088
1089 if (cn > 0)
1090 x++;
1091 } else
1092 cn = 0;
1093 } else
1094 an = 0;
1095
1096 if (x >= 2 && srv_type_label_is_valid(b, bn)) {
1097
1098 if (x >= 3 && srv_type_label_is_valid(c, cn)) {
1099
1100 if (dns_service_name_label_is_valid(a, an)) {
1101
1102 /* OK, got <name> . <type> . <type2> . <domain> */
1103
1104 name = strndup(a, an);
1105 if (!name)
1106 return -ENOMEM;
1107
1108 type = new(char, bn+1+cn+1);
1109 if (!type)
1110 return -ENOMEM;
1111 strcpy(stpcpy(stpcpy(type, b), "."), c);
1112
1113 d = p;
1114 goto finish;
1115 }
1116
1117 } else if (srv_type_label_is_valid(a, an)) {
1118
1119 /* OK, got <type> . <type2> . <domain> */
1120
1121 name = NULL;
1122
1123 type = new(char, an+1+bn+1);
1124 if (!type)
1125 return -ENOMEM;
1126 strcpy(stpcpy(stpcpy(type, a), "."), b);
1127
1128 d = q;
1129 goto finish;
1130 }
1131 }
1132
1133 name = NULL;
1134 type = NULL;
1135 d = joined;
1136
1137finish:
1138 r = dns_name_normalize(d, &domain);
1139 if (r < 0)
1140 return r;
1141
1142 if (_domain) {
1143 *_domain = domain;
1144 domain = NULL;
1145 }
1146
1147 if (_type) {
1148 *_type = type;
1149 type = NULL;
1150 }
1151
1152 if (_name) {
1153 *_name = name;
1154 name = NULL;
1155 }
1156
1157 return 0;
1158}
e7ff0e0b
LP
1159
1160int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
1161 const char* labels[DNS_N_LABELS_MAX+1];
1162 unsigned n = 0;
1163 const char *p;
1164 int r;
1165
1166 assert(name);
1167 assert(ret);
1168
1169 p = name;
1170 for (;;) {
1171 if (n > DNS_N_LABELS_MAX)
1172 return -EINVAL;
1173
1174 labels[n] = p;
1175
1176 r = dns_name_parent(&p);
1177 if (r < 0)
1178 return r;
1179 if (r == 0)
1180 break;
1181
1182 n++;
1183 }
1184
1185 if (n < n_labels)
1186 return -EINVAL;
1187
1188 *ret = labels[n - n_labels];
1189 return (int) (n - n_labels);
1190}
1191
1192int dns_name_count_labels(const char *name) {
1193 unsigned n = 0;
1194 const char *p;
1195 int r;
1196
1197 assert(name);
1198
1199 p = name;
1200 for (;;) {
1201 r = dns_name_parent(&p);
1202 if (r < 0)
1203 return r;
1204 if (r == 0)
1205 break;
1206
1207 if (n >= DNS_N_LABELS_MAX)
1208 return -EINVAL;
1209
1210 n++;
1211 }
1212
1213 return (int) n;
1214}
db5b0e92
LP
1215
1216int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
1217 int r;
1218
1219 assert(a);
1220 assert(b);
1221
1222 while (n_labels > 0) {
1223
1224 r = dns_name_parent(&a);
1225 if (r <= 0)
1226 return r;
1227
1228 n_labels --;
1229 }
1230
1231 return dns_name_equal(a, b);
1232}