chiark / gitweb /
shared: dns-name - use the canonical dns name ordering
[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 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, *y;
342         int r, q, k, w;
343
344         assert(a);
345         assert(b);
346
347         x = (const char *) a + strlen(a);
348         y = (const char *) b + strlen(b);
349
350         for (;;) {
351                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
352
353                 if (x == NULL && y == NULL)
354                         return 0;
355
356                 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
357                 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
358                 if (r < 0 || q < 0)
359                         return r - q;
360
361                 k = dns_label_undo_idna(la, r, la, sizeof(la));
362                 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
363                 if (k < 0 || w < 0)
364                         return k - w;
365                 if (k > 0)
366                         r = k;
367                 if (w > 0)
368                         r = w;
369
370                 la[r] = lb[q] = 0;
371                 r = strcasecmp(la, lb);
372                 if (r != 0)
373                         return r;
374         }
375 }
376
377 const struct hash_ops dns_name_hash_ops = {
378         .hash = dns_name_hash_func,
379         .compare = dns_name_compare_func
380 };
381
382 int dns_name_equal(const char *x, const char *y) {
383         int r, q, k, w;
384
385         assert(x);
386         assert(y);
387
388         for (;;) {
389                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
390
391                 if (*x == 0 && *y == 0)
392                         return true;
393
394                 r = dns_label_unescape(&x, la, sizeof(la));
395                 if (r < 0)
396                         return r;
397
398                 k = dns_label_undo_idna(la, r, la, sizeof(la));
399                 if (k < 0)
400                         return k;
401                 if (k > 0)
402                         r = k;
403
404                 q = dns_label_unescape(&y, lb, sizeof(lb));
405                 if (q < 0)
406                         return q;
407                 w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
408                 if (w < 0)
409                         return w;
410                 if (w > 0)
411                         q = w;
412
413                 la[r] = lb[q] = 0;
414                 if (strcasecmp(la, lb))
415                         return false;
416         }
417 }
418
419 int dns_name_endswith(const char *name, const char *suffix) {
420         const char *n, *s, *saved_n = NULL;
421         int r, q, k, w;
422
423         assert(name);
424         assert(suffix);
425
426         n = name;
427         s = suffix;
428
429         for (;;) {
430                 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
431
432                 r = dns_label_unescape(&n, ln, sizeof(ln));
433                 if (r < 0)
434                         return r;
435                 k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
436                 if (k < 0)
437                         return k;
438                 if (k > 0)
439                         r = k;
440
441                 if (!saved_n)
442                         saved_n = n;
443
444                 q = dns_label_unescape(&s, ls, sizeof(ls));
445                 if (q < 0)
446                         return q;
447                 w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
448                 if (w < 0)
449                         return w;
450                 if (w > 0)
451                         q = w;
452
453                 if (r == 0 && q == 0)
454                         return true;
455                 if (r == 0 && saved_n == n)
456                         return false;
457
458                 ln[r] = ls[q] = 0;
459
460                 if (r != q || strcasecmp(ln, ls)) {
461
462                         /* Not the same, let's jump back, and try with the next label again */
463                         s = suffix;
464                         n = saved_n;
465                         saved_n = NULL;
466                 }
467         }
468 }
469
470 int dns_name_between(const char *a, const char *b, const char *c) {
471         int n;
472
473         /* Determine if b is strictly greater than a and strictly smaller than c.
474            We consider the order of names to be circular, so that if a is
475            strictly greater than c, we consider b to be between them if it is
476            either greater than a or smaller than c. This is how the canonical
477            DNS name order used in NSEC records work. */
478
479         n = dns_name_compare_func(a, c);
480         if (n == 0)
481                 return -EINVAL;
482         else if (n < 0)
483                 /*       a<---b--->c       */
484                 return dns_name_compare_func(a, b) < 0 &&
485                        dns_name_compare_func(b, c) < 0;
486         else
487                 /* <--b--c         a--b--> */
488                 return dns_name_compare_func(b, c) < 0 ||
489                        dns_name_compare_func(a, b) < 0;
490 }
491
492 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
493         const uint8_t *p;
494         int r;
495
496         assert(a);
497         assert(ret);
498
499         p = (const uint8_t*) a;
500
501         if (family == AF_INET)
502                 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
503         else if (family == AF_INET6)
504                 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",
505                              hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
506                              hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
507                              hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
508                              hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
509                              hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
510                              hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
511                              hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
512                              hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
513         else
514                 return -EAFNOSUPPORT;
515         if (r < 0)
516                 return -ENOMEM;
517
518         return 0;
519 }
520
521 int dns_name_address(const char *p, int *family, union in_addr_union *address) {
522         int r;
523
524         assert(p);
525         assert(family);
526         assert(address);
527
528         r = dns_name_endswith(p, "in-addr.arpa");
529         if (r < 0)
530                 return r;
531         if (r > 0) {
532                 uint8_t a[4];
533                 unsigned i;
534
535                 for (i = 0; i < ELEMENTSOF(a); i++) {
536                         char label[DNS_LABEL_MAX+1];
537
538                         r = dns_label_unescape(&p, label, sizeof(label));
539                         if (r < 0)
540                                 return r;
541                         if (r == 0)
542                                 return -EINVAL;
543                         if (r > 3)
544                                 return -EINVAL;
545
546                         r = safe_atou8(label, &a[i]);
547                         if (r < 0)
548                                 return r;
549                 }
550
551                 r = dns_name_equal(p, "in-addr.arpa");
552                 if (r <= 0)
553                         return r;
554
555                 *family = AF_INET;
556                 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
557                                              ((uint32_t) a[2] << 16) |
558                                              ((uint32_t) a[1] << 8) |
559                                               (uint32_t) a[0]);
560
561                 return 1;
562         }
563
564         r = dns_name_endswith(p, "ip6.arpa");
565         if (r < 0)
566                 return r;
567         if (r > 0) {
568                 struct in6_addr a;
569                 unsigned i;
570
571                 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
572                         char label[DNS_LABEL_MAX+1];
573                         int x, y;
574
575                         r = dns_label_unescape(&p, label, sizeof(label));
576                         if (r <= 0)
577                                 return r;
578                         if (r != 1)
579                                 return -EINVAL;
580                         x = unhexchar(label[0]);
581                         if (x < 0)
582                                 return -EINVAL;
583
584                         r = dns_label_unescape(&p, label, sizeof(label));
585                         if (r <= 0)
586                                 return r;
587                         if (r != 1)
588                                 return -EINVAL;
589                         y = unhexchar(label[0]);
590                         if (y < 0)
591                                 return -EINVAL;
592
593                         a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
594                 }
595
596                 r = dns_name_equal(p, "ip6.arpa");
597                 if (r <= 0)
598                         return r;
599
600                 *family = AF_INET6;
601                 address->in6 = a;
602                 return 1;
603         }
604
605         return 0;
606 }
607
608 int dns_name_root(const char *name) {
609         char label[DNS_LABEL_MAX+1];
610         int r;
611
612         assert(name);
613
614         r = dns_label_unescape(&name, label, sizeof(label));
615         if (r < 0)
616                 return r;
617
618         return r == 0 && *name == 0;
619 }
620
621 int dns_name_single_label(const char *name) {
622         char label[DNS_LABEL_MAX+1];
623         int r;
624
625         assert(name);
626
627         r = dns_label_unescape(&name, label, sizeof(label));
628         if (r < 0)
629                 return r;
630         if (r == 0)
631                 return 0;
632
633         r = dns_label_unescape(&name, label, sizeof(label));
634         if (r < 0)
635                 return r;
636
637         return r == 0 && *name == 0;
638 }