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