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