]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/dns-domain.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / shared / dns-domain.c
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
22 #ifdef HAVE_LIBIDN
23 #include <idna.h>
24 #include <stringprep.h>
25 #endif
26
27 #include "alloc-util.h"
28 #include "dns-domain.h"
29 #include "hexdecoct.h"
30 #include "parse-util.h"
31 #include "string-util.h"
32
33 int dns_label_unescape(const char **name, char *dest, size_t sz) {
34 const char *n;
35 char *d;
36 int r = 0;
37
38 assert(name);
39 assert(*name);
40 assert(dest);
41
42 n = *name;
43 d = dest;
44
45 for (;;) {
46 if (*n == '.') {
47 n++;
48 break;
49 }
50
51 if (*n == 0)
52 break;
53
54 if (sz <= 0)
55 return -ENOSPC;
56
57 if (r >= DNS_LABEL_MAX)
58 return -EINVAL;
59
60 if (*n == '\\') {
61 /* Escaped character */
62
63 n++;
64
65 if (*n == 0)
66 /* Ending NUL */
67 return -EINVAL;
68
69 else if (*n == '\\' || *n == '.') {
70 /* Escaped backslash or dot */
71 *(d++) = *(n++);
72 sz--;
73 r++;
74
75 } else if (n[0] >= '0' && n[0] <= '9') {
76 unsigned k;
77
78 /* Escaped literal ASCII character */
79
80 if (!(n[1] >= '0' && n[1] <= '9') ||
81 !(n[2] >= '0' && n[2] <= '9'))
82 return -EINVAL;
83
84 k = ((unsigned) (n[0] - '0') * 100) +
85 ((unsigned) (n[1] - '0') * 10) +
86 ((unsigned) (n[2] - '0'));
87
88 /* Don't allow CC characters or anything that doesn't fit in 8bit */
89 if (k < ' ' || k > 255 || k == 127)
90 return -EINVAL;
91
92 *(d++) = (char) k;
93 sz--;
94 r++;
95
96 n += 3;
97 } else
98 return -EINVAL;
99
100 } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
101
102 /* Normal character */
103 *(d++) = *(n++);
104 sz--;
105 r++;
106 } else
107 return -EINVAL;
108 }
109
110 /* Empty label that is not at the end? */
111 if (r == 0 && *n)
112 return -EINVAL;
113
114 if (sz >= 1)
115 *d = 0;
116
117 *name = n;
118 return r;
119 }
120
121 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
122 * the previous label (always skipping one dot) or to NULL if there are no more
123 * labels. */
124 int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
125 const char *terminal;
126 int r;
127
128 assert(name);
129 assert(label_terminal);
130 assert(dest);
131
132 /* no more labels */
133 if (!*label_terminal) {
134 if (sz >= 1)
135 *dest = 0;
136
137 return 0;
138 }
139
140 assert(**label_terminal == '.' || **label_terminal == 0);
141
142 /* skip current terminal character */
143 terminal = *label_terminal - 1;
144
145 /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
146 for (;;) {
147 if (terminal < name) {
148 /* reached the first label, so indicate that there are no more */
149 terminal = NULL;
150 break;
151 }
152
153 /* find the start of the last label */
154 if (*terminal == '.') {
155 const char *y;
156 unsigned slashes = 0;
157
158 for (y = terminal - 1; y >= name && *y == '\\'; y--)
159 slashes ++;
160
161 if (slashes % 2 == 0) {
162 /* the '.' was not escaped */
163 name = terminal + 1;
164 break;
165 } else {
166 terminal = y;
167 continue;
168 }
169 }
170
171 terminal --;
172 }
173
174 r = dns_label_unescape(&name, dest, sz);
175 if (r < 0)
176 return r;
177
178 *label_terminal = terminal;
179
180 return r;
181 }
182
183 int dns_label_escape(const char *p, size_t l, char **ret) {
184 _cleanup_free_ char *s = NULL;
185 char *q;
186 int r;
187
188 assert(p);
189 assert(ret);
190
191 if (l > DNS_LABEL_MAX)
192 return -EINVAL;
193
194 s = malloc(l * 4 + 1);
195 if (!s)
196 return -ENOMEM;
197
198 q = s;
199 while (l > 0) {
200
201 if (*p == '.' || *p == '\\') {
202
203 /* Dot or backslash */
204 *(q++) = '\\';
205 *(q++) = *p;
206
207 } else if (*p == '_' ||
208 *p == '-' ||
209 (*p >= '0' && *p <= '9') ||
210 (*p >= 'a' && *p <= 'z') ||
211 (*p >= 'A' && *p <= 'Z')) {
212
213 /* Proper character */
214 *(q++) = *p;
215 } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
216
217 /* Everything else */
218 *(q++) = '\\';
219 *(q++) = '0' + (char) ((uint8_t) *p / 100);
220 *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
221 *(q++) = '0' + (char) ((uint8_t) *p % 10);
222
223 } else
224 return -EINVAL;
225
226 p++;
227 l--;
228 }
229
230 *q = 0;
231 *ret = s;
232 r = q - s;
233 s = NULL;
234
235 return r;
236 }
237
238 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
239 #ifdef HAVE_LIBIDN
240 _cleanup_free_ uint32_t *input = NULL;
241 size_t input_size;
242 const char *p;
243 bool contains_8bit = false;
244
245 assert(encoded);
246 assert(decoded);
247 assert(decoded_max >= DNS_LABEL_MAX);
248
249 if (encoded_size <= 0)
250 return 0;
251
252 for (p = encoded; p < encoded + encoded_size; p++)
253 if ((uint8_t) *p > 127)
254 contains_8bit = true;
255
256 if (!contains_8bit)
257 return 0;
258
259 input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
260 if (!input)
261 return -ENOMEM;
262
263 if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0)
264 return -EINVAL;
265
266 return strlen(decoded);
267 #else
268 return 0;
269 #endif
270 }
271
272 int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
273 #ifdef HAVE_LIBIDN
274 size_t input_size, output_size;
275 _cleanup_free_ uint32_t *input = NULL;
276 _cleanup_free_ char *result = NULL;
277 uint32_t *output = NULL;
278 size_t w;
279
280 /* To be invoked after unescaping */
281
282 assert(encoded);
283 assert(decoded);
284
285 if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
286 return 0;
287
288 if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0)
289 return 0;
290
291 input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
292 if (!input)
293 return -ENOMEM;
294
295 output_size = input_size;
296 output = newa(uint32_t, output_size);
297
298 idna_to_unicode_44i(input, input_size, output, &output_size, 0);
299
300 result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
301 if (!result)
302 return -ENOMEM;
303 if (w <= 0)
304 return 0;
305 if (w+1 > decoded_max)
306 return -EINVAL;
307
308 memcpy(decoded, result, w+1);
309 return w;
310 #else
311 return 0;
312 #endif
313 }
314
315 int dns_name_concat(const char *a, const char *b, char **_ret) {
316 _cleanup_free_ char *ret = NULL;
317 size_t n = 0, allocated = 0;
318 const char *p = a;
319 bool first = true;
320 int r;
321
322 assert(a);
323
324 for (;;) {
325 _cleanup_free_ char *t = NULL;
326 char label[DNS_LABEL_MAX];
327 int k;
328
329 r = dns_label_unescape(&p, label, sizeof(label));
330 if (r < 0)
331 return r;
332 if (r == 0) {
333 if (*p != 0)
334 return -EINVAL;
335
336 if (b) {
337 /* Now continue with the second string, if there is one */
338 p = b;
339 b = NULL;
340 continue;
341 }
342
343 break;
344 }
345
346 k = dns_label_undo_idna(label, r, label, sizeof(label));
347 if (k < 0)
348 return k;
349 if (k > 0)
350 r = k;
351
352 r = dns_label_escape(label, r, &t);
353 if (r < 0)
354 return r;
355
356 if (_ret) {
357 if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
358 return -ENOMEM;
359
360 if (!first)
361 ret[n++] = '.';
362 else
363 first = false;
364
365 memcpy(ret + n, t, r);
366 }
367
368 n += r;
369 }
370
371 if (n > DNS_NAME_MAX)
372 return -EINVAL;
373
374 if (_ret) {
375 if (!GREEDY_REALLOC(ret, allocated, n + 1))
376 return -ENOMEM;
377
378 ret[n] = 0;
379 *_ret = ret;
380 ret = NULL;
381 }
382
383 return 0;
384 }
385
386 void dns_name_hash_func(const void *s, struct siphash *state) {
387 const char *p = s;
388 int r;
389
390 assert(p);
391
392 while (*p) {
393 char label[DNS_LABEL_MAX+1];
394 int k;
395
396 r = dns_label_unescape(&p, label, sizeof(label));
397 if (r < 0)
398 break;
399
400 k = dns_label_undo_idna(label, r, label, sizeof(label));
401 if (k < 0)
402 break;
403 if (k > 0)
404 r = k;
405
406 if (r == 0)
407 break;
408
409 label[r] = 0;
410 ascii_strlower(label);
411
412 string_hash_func(label, state);
413 }
414
415 /* enforce that all names are terminated by the empty label */
416 string_hash_func("", state);
417 }
418
419 int dns_name_compare_func(const void *a, const void *b) {
420 const char *x, *y;
421 int r, q, k, w;
422
423 assert(a);
424 assert(b);
425
426 x = (const char *) a + strlen(a);
427 y = (const char *) b + strlen(b);
428
429 for (;;) {
430 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
431
432 if (x == NULL && y == NULL)
433 return 0;
434
435 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
436 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
437 if (r < 0 || q < 0)
438 return r - q;
439
440 k = dns_label_undo_idna(la, r, la, sizeof(la));
441 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
442 if (k < 0 || w < 0)
443 return k - w;
444 if (k > 0)
445 r = k;
446 if (w > 0)
447 r = w;
448
449 la[r] = lb[q] = 0;
450 r = strcasecmp(la, lb);
451 if (r != 0)
452 return r;
453 }
454 }
455
456 const struct hash_ops dns_name_hash_ops = {
457 .hash = dns_name_hash_func,
458 .compare = dns_name_compare_func
459 };
460
461 int dns_name_equal(const char *x, const char *y) {
462 int r, q, k, w;
463
464 assert(x);
465 assert(y);
466
467 for (;;) {
468 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
469
470 if (*x == 0 && *y == 0)
471 return true;
472
473 r = dns_label_unescape(&x, la, sizeof(la));
474 if (r < 0)
475 return r;
476
477 k = dns_label_undo_idna(la, r, la, sizeof(la));
478 if (k < 0)
479 return k;
480 if (k > 0)
481 r = k;
482
483 q = dns_label_unescape(&y, lb, sizeof(lb));
484 if (q < 0)
485 return q;
486 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
487 if (w < 0)
488 return w;
489 if (w > 0)
490 q = w;
491
492 la[r] = lb[q] = 0;
493 if (strcasecmp(la, lb))
494 return false;
495 }
496 }
497
498 int dns_name_endswith(const char *name, const char *suffix) {
499 const char *n, *s, *saved_n = NULL;
500 int r, q, k, w;
501
502 assert(name);
503 assert(suffix);
504
505 n = name;
506 s = suffix;
507
508 for (;;) {
509 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
510
511 r = dns_label_unescape(&n, ln, sizeof(ln));
512 if (r < 0)
513 return r;
514 k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
515 if (k < 0)
516 return k;
517 if (k > 0)
518 r = k;
519
520 if (!saved_n)
521 saved_n = n;
522
523 q = dns_label_unescape(&s, ls, sizeof(ls));
524 if (q < 0)
525 return q;
526 w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
527 if (w < 0)
528 return w;
529 if (w > 0)
530 q = w;
531
532 if (r == 0 && q == 0)
533 return true;
534 if (r == 0 && saved_n == n)
535 return false;
536
537 ln[r] = ls[q] = 0;
538
539 if (r != q || strcasecmp(ln, ls)) {
540
541 /* Not the same, let's jump back, and try with the next label again */
542 s = suffix;
543 n = saved_n;
544 saved_n = NULL;
545 }
546 }
547 }
548
549 int dns_name_between(const char *a, const char *b, const char *c) {
550 int n;
551
552 /* Determine if b is strictly greater than a and strictly smaller than c.
553 We consider the order of names to be circular, so that if a is
554 strictly greater than c, we consider b to be between them if it is
555 either greater than a or smaller than c. This is how the canonical
556 DNS name order used in NSEC records work. */
557
558 n = dns_name_compare_func(a, c);
559 if (n == 0)
560 return -EINVAL;
561 else if (n < 0)
562 /* a<---b--->c */
563 return dns_name_compare_func(a, b) < 0 &&
564 dns_name_compare_func(b, c) < 0;
565 else
566 /* <--b--c a--b--> */
567 return dns_name_compare_func(b, c) < 0 ||
568 dns_name_compare_func(a, b) < 0;
569 }
570
571 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
572 const uint8_t *p;
573 int r;
574
575 assert(a);
576 assert(ret);
577
578 p = (const uint8_t*) a;
579
580 if (family == AF_INET)
581 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
582 else if (family == AF_INET6)
583 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",
584 hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
585 hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
586 hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
587 hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
588 hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
589 hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
590 hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
591 hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
592 else
593 return -EAFNOSUPPORT;
594 if (r < 0)
595 return -ENOMEM;
596
597 return 0;
598 }
599
600 int dns_name_address(const char *p, int *family, union in_addr_union *address) {
601 int r;
602
603 assert(p);
604 assert(family);
605 assert(address);
606
607 r = dns_name_endswith(p, "in-addr.arpa");
608 if (r < 0)
609 return r;
610 if (r > 0) {
611 uint8_t a[4];
612 unsigned i;
613
614 for (i = 0; i < ELEMENTSOF(a); i++) {
615 char label[DNS_LABEL_MAX+1];
616
617 r = dns_label_unescape(&p, label, sizeof(label));
618 if (r < 0)
619 return r;
620 if (r == 0)
621 return -EINVAL;
622 if (r > 3)
623 return -EINVAL;
624
625 r = safe_atou8(label, &a[i]);
626 if (r < 0)
627 return r;
628 }
629
630 r = dns_name_equal(p, "in-addr.arpa");
631 if (r <= 0)
632 return r;
633
634 *family = AF_INET;
635 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
636 ((uint32_t) a[2] << 16) |
637 ((uint32_t) a[1] << 8) |
638 (uint32_t) a[0]);
639
640 return 1;
641 }
642
643 r = dns_name_endswith(p, "ip6.arpa");
644 if (r < 0)
645 return r;
646 if (r > 0) {
647 struct in6_addr a;
648 unsigned i;
649
650 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
651 char label[DNS_LABEL_MAX+1];
652 int x, y;
653
654 r = dns_label_unescape(&p, label, sizeof(label));
655 if (r <= 0)
656 return r;
657 if (r != 1)
658 return -EINVAL;
659 x = unhexchar(label[0]);
660 if (x < 0)
661 return -EINVAL;
662
663 r = dns_label_unescape(&p, label, sizeof(label));
664 if (r <= 0)
665 return r;
666 if (r != 1)
667 return -EINVAL;
668 y = unhexchar(label[0]);
669 if (y < 0)
670 return -EINVAL;
671
672 a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
673 }
674
675 r = dns_name_equal(p, "ip6.arpa");
676 if (r <= 0)
677 return r;
678
679 *family = AF_INET6;
680 address->in6 = a;
681 return 1;
682 }
683
684 return 0;
685 }
686
687 int dns_name_root(const char *name) {
688 char label[DNS_LABEL_MAX+1];
689 int r;
690
691 assert(name);
692
693 r = dns_label_unescape(&name, label, sizeof(label));
694 if (r < 0)
695 return r;
696
697 return r == 0 && *name == 0;
698 }
699
700 int dns_name_single_label(const char *name) {
701 char label[DNS_LABEL_MAX+1];
702 int r;
703
704 assert(name);
705
706 r = dns_label_unescape(&name, label, sizeof(label));
707 if (r < 0)
708 return r;
709 if (r == 0)
710 return 0;
711
712 r = dns_label_unescape(&name, label, sizeof(label));
713 if (r < 0)
714 return r;
715
716 return r == 0 && *name == 0;
717 }