chiark / gitweb /
Unifiy free() usage
[elogind.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 "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 /* @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 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         assert(name);
125         assert(label_terminal);
126         assert(dest);
127
128         /* no more labels */
129         if (!*label_terminal) {
130                 if (sz >= 1)
131                         *dest = 0;
132
133                 return 0;
134         }
135
136         assert(**label_terminal == '.' || **label_terminal == 0);
137
138         /* skip current terminal character */
139         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                 if (terminal < name) {
144                         /* reached the first label, so indicate that there are no more */
145                         terminal = NULL;
146                         break;
147                 }
148
149                 /* find the start of the last label */
150                 if (*terminal == '.') {
151                         const char *y;
152                         unsigned slashes = 0;
153
154                         for (y = terminal - 1; y >= name && *y == '\\'; y--)
155                                 slashes ++;
156
157                         if (slashes % 2 == 0) {
158                                 /* the '.' was not escaped */
159                                 name = terminal + 1;
160                                 break;
161                         } else {
162                                 terminal = y;
163                                 continue;
164                         }
165                 }
166
167                 terminal --;
168         }
169
170         r = dns_label_unescape(&name, dest, sz);
171         if (r < 0)
172                 return r;
173
174         *label_terminal = terminal;
175
176         return r;
177 }
178
179 int dns_label_escape(const char *p, size_t l, char **ret) {
180         _cleanup_free_ char *s = NULL;
181         char *q;
182         int r;
183
184         assert(p);
185         assert(ret);
186
187         if (l > DNS_LABEL_MAX)
188                 return -EINVAL;
189
190         s = malloc(l * 4 + 1);
191         if (!s)
192                 return -ENOMEM;
193
194         q = s;
195         while (l > 0) {
196
197                 if (*p == '.' || *p == '\\') {
198
199                         /* Dot or backslash */
200                         *(q++) = '\\';
201                         *(q++) = *p;
202
203                 } else if (*p == '_' ||
204                            *p == '-' ||
205                            (*p >= '0' && *p <= '9') ||
206                            (*p >= 'a' && *p <= 'z') ||
207                            (*p >= 'A' && *p <= 'Z')) {
208
209                         /* Proper character */
210                         *(q++) = *p;
211                 } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
212
213                         /* Everything else */
214                         *(q++) = '\\';
215                         *(q++) = '0' + (char) ((uint8_t) *p / 100);
216                         *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
217                         *(q++) = '0' + (char) ((uint8_t) *p % 10);
218
219                 } else
220                         return -EINVAL;
221
222                 p++;
223                 l--;
224         }
225
226         *q = 0;
227         *ret = s;
228         r = q - s;
229         s = NULL;
230
231         return r;
232 }
233
234 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
235 #ifdef HAVE_LIBIDN
236         _cleanup_free_ uint32_t *input = NULL;
237         size_t input_size;
238         const char *p;
239         bool contains_8bit = false;
240
241         assert(encoded);
242         assert(decoded);
243         assert(decoded_max >= DNS_LABEL_MAX);
244
245         if (encoded_size <= 0)
246                 return 0;
247
248         for (p = encoded; p < encoded + encoded_size; p++)
249                 if ((uint8_t) *p > 127)
250                         contains_8bit = true;
251
252         if (!contains_8bit)
253                 return 0;
254
255         input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
256         if (!input)
257                 return -ENOMEM;
258
259         if (idna_to_ascii_4i(input, input_size, decoded, 0) != 0)
260                 return -EINVAL;
261
262         return strlen(decoded);
263 #else
264         return 0;
265 #endif
266 }
267
268 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         _cleanup_free_ uint32_t *input = NULL;
272         _cleanup_free_ char *result = NULL;
273         uint32_t *output = NULL;
274         size_t w;
275
276         /* To be invoked after unescaping */
277
278         assert(encoded);
279         assert(decoded);
280
281         if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1)
282                 return 0;
283
284         if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0)
285                 return 0;
286
287         input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
288         if (!input)
289                 return -ENOMEM;
290
291         output_size = input_size;
292         output = newa(uint32_t, output_size);
293
294         idna_to_unicode_44i(input, input_size, output, &output_size, 0);
295
296         result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
297         if (!result)
298                 return -ENOMEM;
299         if (w <= 0)
300                 return 0;
301         if (w+1 > decoded_max)
302                 return -EINVAL;
303
304         memcpy(decoded, result, w+1);
305         return w;
306 #else
307         return 0;
308 #endif
309 }
310
311 int dns_name_normalize(const char *s, char **_ret) {
312         _cleanup_free_ char *ret = NULL;
313         size_t n = 0, allocated = 0;
314         const char *p = s;
315         bool first = true;
316         int r;
317
318         assert(s);
319
320         for (;;) {
321                 _cleanup_free_ char *t = NULL;
322                 char label[DNS_LABEL_MAX];
323                 int k;
324
325                 r = dns_label_unescape(&p, label, sizeof(label));
326                 if (r < 0)
327                         return r;
328                 if (r == 0) {
329                         if (*p != 0)
330                                 return -EINVAL;
331                         break;
332                 }
333
334                 k = dns_label_undo_idna(label, r, label, sizeof(label));
335                 if (k < 0)
336                         return k;
337                 if (k > 0)
338                         r = k;
339
340                 r = dns_label_escape(label, r, &t);
341                 if (r < 0)
342                         return r;
343
344                 if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
345                         return -ENOMEM;
346
347                 if (!first)
348                         ret[n++] = '.';
349                 else
350                         first = false;
351
352                 memcpy(ret + n, t, r);
353                 n += r;
354         }
355
356         if (n > DNS_NAME_MAX)
357                 return -EINVAL;
358
359         if (!GREEDY_REALLOC(ret, allocated, n + 1))
360                 return -ENOMEM;
361
362         ret[n] = 0;
363
364         if (_ret) {
365                 *_ret = ret;
366                 ret = NULL;
367         }
368
369         return 0;
370 }
371
372 unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
373         const char *p = s;
374         unsigned long ul = hash_key[0];
375         int r;
376
377         assert(p);
378
379         while (*p) {
380                 char label[DNS_LABEL_MAX+1];
381                 int k;
382
383                 r = dns_label_unescape(&p, label, sizeof(label));
384                 if (r < 0)
385                         break;
386
387                 k = dns_label_undo_idna(label, r, label, sizeof(label));
388                 if (k < 0)
389                         break;
390                 if (k > 0)
391                         r = k;
392
393                 label[r] = 0;
394                 ascii_strlower(label);
395
396                 ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
397         }
398
399         return ul;
400 }
401
402 int dns_name_compare_func(const void *a, const void *b) {
403         const char *x, *y;
404         int r, q, k, w;
405
406         assert(a);
407         assert(b);
408
409         x = (const char *) a + strlen(a);
410         y = (const char *) b + strlen(b);
411
412         for (;;) {
413                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
414
415                 if (x == NULL && y == NULL)
416                         return 0;
417
418                 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
419                 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
420                 if (r < 0 || q < 0)
421                         return r - q;
422
423                 k = dns_label_undo_idna(la, r, la, sizeof(la));
424                 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
425                 if (k < 0 || w < 0)
426                         return k - w;
427                 if (k > 0)
428                         r = k;
429                 if (w > 0)
430                         r = w;
431
432                 la[r] = lb[q] = 0;
433                 r = strcasecmp(la, lb);
434                 if (r != 0)
435                         return r;
436         }
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 int dns_name_equal(const char *x, const char *y) {
445         int r, q, k, w;
446
447         assert(x);
448         assert(y);
449
450         for (;;) {
451                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
452
453                 if (*x == 0 && *y == 0)
454                         return true;
455
456                 r = dns_label_unescape(&x, la, sizeof(la));
457                 if (r < 0)
458                         return r;
459
460                 k = dns_label_undo_idna(la, r, la, sizeof(la));
461                 if (k < 0)
462                         return k;
463                 if (k > 0)
464                         r = k;
465
466                 q = dns_label_unescape(&y, lb, sizeof(lb));
467                 if (q < 0)
468                         return q;
469                 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
470                 if (w < 0)
471                         return w;
472                 if (w > 0)
473                         q = w;
474
475                 la[r] = lb[q] = 0;
476                 if (strcasecmp(la, lb))
477                         return false;
478         }
479 }
480
481 int dns_name_endswith(const char *name, const char *suffix) {
482         const char *n, *s, *saved_n = NULL;
483         int r, q, k, w;
484
485         assert(name);
486         assert(suffix);
487
488         n = name;
489         s = suffix;
490
491         for (;;) {
492                 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
493
494                 r = dns_label_unescape(&n, ln, sizeof(ln));
495                 if (r < 0)
496                         return r;
497                 k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
498                 if (k < 0)
499                         return k;
500                 if (k > 0)
501                         r = k;
502
503                 if (!saved_n)
504                         saved_n = n;
505
506                 q = dns_label_unescape(&s, ls, sizeof(ls));
507                 if (q < 0)
508                         return q;
509                 w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
510                 if (w < 0)
511                         return w;
512                 if (w > 0)
513                         q = w;
514
515                 if (r == 0 && q == 0)
516                         return true;
517                 if (r == 0 && saved_n == n)
518                         return false;
519
520                 ln[r] = ls[q] = 0;
521
522                 if (r != q || strcasecmp(ln, ls)) {
523
524                         /* Not the same, let's jump back, and try with the next label again */
525                         s = suffix;
526                         n = saved_n;
527                         saved_n = NULL;
528                 }
529         }
530 }
531
532 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         n = dns_name_compare_func(a, c);
542         if (n == 0)
543                 return -EINVAL;
544         else if (n < 0)
545                 /*       a<---b--->c       */
546                 return dns_name_compare_func(a, b) < 0 &&
547                        dns_name_compare_func(b, c) < 0;
548         else
549                 /* <--b--c         a--b--> */
550                 return dns_name_compare_func(b, c) < 0 ||
551                        dns_name_compare_func(a, b) < 0;
552 }
553
554 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
555         const uint8_t *p;
556         int r;
557
558         assert(a);
559         assert(ret);
560
561         p = (const uint8_t*) a;
562
563         if (family == AF_INET)
564                 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
565         else if (family == AF_INET6)
566                 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                              hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
568                              hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
569                              hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
570                              hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
571                              hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
572                              hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
573                              hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
574                              hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
575         else
576                 return -EAFNOSUPPORT;
577         if (r < 0)
578                 return -ENOMEM;
579
580         return 0;
581 }
582
583 int dns_name_address(const char *p, int *family, union in_addr_union *address) {
584         int r;
585
586         assert(p);
587         assert(family);
588         assert(address);
589
590         r = dns_name_endswith(p, "in-addr.arpa");
591         if (r < 0)
592                 return r;
593         if (r > 0) {
594                 uint8_t a[4];
595                 unsigned i;
596
597                 for (i = 0; i < ELEMENTSOF(a); i++) {
598                         char label[DNS_LABEL_MAX+1];
599
600                         r = dns_label_unescape(&p, label, sizeof(label));
601                         if (r < 0)
602                                 return r;
603                         if (r == 0)
604                                 return -EINVAL;
605                         if (r > 3)
606                                 return -EINVAL;
607
608                         r = safe_atou8(label, &a[i]);
609                         if (r < 0)
610                                 return r;
611                 }
612
613                 r = dns_name_equal(p, "in-addr.arpa");
614                 if (r <= 0)
615                         return r;
616
617                 *family = AF_INET;
618                 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                 return 1;
624         }
625
626         r = dns_name_endswith(p, "ip6.arpa");
627         if (r < 0)
628                 return r;
629         if (r > 0) {
630                 struct in6_addr a;
631                 unsigned i;
632
633                 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
634                         char label[DNS_LABEL_MAX+1];
635                         int x, y;
636
637                         r = dns_label_unescape(&p, label, sizeof(label));
638                         if (r <= 0)
639                                 return r;
640                         if (r != 1)
641                                 return -EINVAL;
642                         x = unhexchar(label[0]);
643                         if (x < 0)
644                                 return -EINVAL;
645
646                         r = dns_label_unescape(&p, label, sizeof(label));
647                         if (r <= 0)
648                                 return r;
649                         if (r != 1)
650                                 return -EINVAL;
651                         y = unhexchar(label[0]);
652                         if (y < 0)
653                                 return -EINVAL;
654
655                         a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
656                 }
657
658                 r = dns_name_equal(p, "ip6.arpa");
659                 if (r <= 0)
660                         return r;
661
662                 *family = AF_INET6;
663                 address->in6 = a;
664                 return 1;
665         }
666
667         return 0;
668 }
669
670 int dns_name_root(const char *name) {
671         char label[DNS_LABEL_MAX+1];
672         int r;
673
674         assert(name);
675
676         r = dns_label_unescape(&name, label, sizeof(label));
677         if (r < 0)
678                 return r;
679
680         return r == 0 && *name == 0;
681 }
682
683 int dns_name_single_label(const char *name) {
684         char label[DNS_LABEL_MAX+1];
685         int r;
686
687         assert(name);
688
689         r = dns_label_unescape(&name, label, sizeof(label));
690         if (r < 0)
691                 return r;
692         if (r == 0)
693                 return 0;
694
695         r = dns_label_unescape(&name, label, sizeof(label));
696         if (r < 0)
697                 return r;
698
699         return r == 0 && *name == 0;
700 }