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