chiark / gitweb /
resolved: fix serialization of LOC records, check correctness
[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 #include "resolved-dns-domain.h"
23
24 int dns_label_unescape(const char **name, char *dest, size_t sz) {
25         const char *n;
26         char *d;
27         int r = 0;
28
29         assert(name);
30         assert(*name);
31         assert(dest);
32
33         n = *name;
34         d = dest;
35
36         for (;;) {
37                 if (*n == '.') {
38                         n++;
39                         break;
40                 }
41
42                 if (*n == 0)
43                         break;
44
45                 if (sz <= 0)
46                         return -ENOSPC;
47
48                 if (r >= DNS_LABEL_MAX)
49                         return -EINVAL;
50
51                 if (*n == '\\') {
52                         /* Escaped character */
53
54                         n++;
55
56                         if (*n == 0)
57                                 /* Ending NUL */
58                                 return -EINVAL;
59
60                         else if (*n == '\\' || *n == '.') {
61                                 /* Escaped backslash or dot */
62                                 *(d++) = *(n++);
63                                 sz--;
64                                 r++;
65
66                         } else if (n[0] >= '0' && n[0] <= '9') {
67                                 unsigned k;
68
69                                 /* Escaped literal ASCII character */
70
71                                 if (!(n[1] >= '0' && n[1] <= '9') ||
72                                     !(n[2] >= '0' && n[2] <= '9'))
73                                         return -EINVAL;
74
75                                 k = ((unsigned) (n[0] - '0') * 100) +
76                                         ((unsigned) (n[1] - '0') * 10) +
77                                         ((unsigned) (n[2] - '0'));
78
79                                 /* Don't allow CC characters or anything that doesn't fit in 8bit */
80                                 if (k < ' ' || k > 255 || k == 127)
81                                         return -EINVAL;
82
83                                 *(d++) = (char) k;
84                                 sz--;
85                                 r++;
86
87                                 n += 3;
88                         } else
89                                 return -EINVAL;
90
91                 } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
92
93                         /* Normal character */
94                         *(d++) = *(n++);
95                         sz--;
96                         r++;
97                 } else
98                         return -EINVAL;
99         }
100
101         /* Empty label that is not at the end? */
102         if (r == 0 && *n)
103                 return -EINVAL;
104
105         if (sz >= 1)
106                 *d = 0;
107
108         *name = n;
109         return r;
110 }
111
112 int dns_label_escape(const char *p, size_t l, char **ret) {
113         _cleanup_free_ char *s = NULL;
114         char *q;
115         int r;
116
117         assert(p);
118         assert(ret);
119
120         if (l > DNS_LABEL_MAX)
121                 return -EINVAL;
122
123         s = malloc(l * 4 + 1);
124         if (!s)
125                 return -ENOMEM;
126
127         q = s;
128         while (l > 0) {
129
130                 if (*p == '.' || *p == '\\') {
131
132                         /* Dot or backslash */
133                         *(q++) = '\\';
134                         *(q++) = *p;
135
136                 } else if (*p == '_' ||
137                            *p == '-' ||
138                            (*p >= '0' && *p <= '9') ||
139                            (*p >= 'a' && *p <= 'z') ||
140                            (*p >= 'A' && *p <= 'Z')) {
141
142                         /* Proper character */
143                         *(q++) = *p;
144                 } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) {
145
146                         /* Everything else */
147                         *(q++) = '\\';
148                         *(q++) = '0' + (char) ((uint8_t) *p / 100);
149                         *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
150                         *(q++) = '0' + (char) ((uint8_t) *p % 10);
151
152                 } else
153                         return -EINVAL;
154
155                 p++;
156                 l--;
157         }
158
159         *q = 0;
160         *ret = s;
161         r = q - s;
162         s = NULL;
163
164         return r;
165 }
166
167 int dns_name_normalize(const char *s, char **_ret) {
168         _cleanup_free_ char *ret = NULL;
169         size_t n = 0, allocated = 0;
170         const char *p = s;
171         bool first = true;
172         int r;
173
174         assert(s);
175
176         for (;;) {
177                 _cleanup_free_ char *t = NULL;
178                 char label[DNS_LABEL_MAX];
179
180                 r = dns_label_unescape(&p, label, sizeof(label));
181                 if (r < 0)
182                         return r;
183                 if (r == 0) {
184                         if (*p != 0)
185                                 return -EINVAL;
186                         break;
187                 }
188
189                 r = dns_label_escape(label, r, &t);
190                 if (r < 0)
191                         return r;
192
193                 if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
194                         return -ENOMEM;
195
196                 if (!first)
197                         ret[n++] = '.';
198                 else
199                         first = false;
200
201                 memcpy(ret + n, t, r);
202                 n += r;
203         }
204
205         if (n > DNS_NAME_MAX)
206                 return -EINVAL;
207
208         if (!GREEDY_REALLOC(ret, allocated, n + 1))
209                 return -ENOMEM;
210
211         ret[n] = 0;
212
213         if (_ret) {
214                 *_ret = ret;
215                 ret = NULL;
216         }
217
218         return 0;
219 }
220
221 unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
222         const char *p = s;
223         unsigned long ul = hash_key[0];
224         int r;
225
226         assert(p);
227
228         while (*p) {
229                 char label[DNS_LABEL_MAX+1];
230
231                 r = dns_label_unescape(&p, label, sizeof(label));
232                 if (r < 0)
233                         break;
234
235                 label[r] = 0;
236                 ascii_strlower(label);
237
238                 ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
239         }
240
241         return ul;
242 }
243
244 int dns_name_compare_func(const void *a, const void *b) {
245         const char *x = a, *y = b;
246         int r, q;
247
248         assert(a);
249         assert(b);
250
251         for (;;) {
252                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
253
254                 if (*x == 0 && *y == 0)
255                         return 0;
256
257                 r = dns_label_unescape(&x, la, sizeof(la));
258                 q = dns_label_unescape(&y, lb, sizeof(lb));
259                 if (r < 0 || q < 0)
260                         return r - q;
261
262                 la[r] = lb[q] = 0;
263                 r = strcasecmp(la, lb);
264                 if (r != 0)
265                         return r;
266         }
267 }
268
269 int dns_name_equal(const char *x, const char *y) {
270         int r, q;
271
272         assert(x);
273         assert(y);
274
275         for (;;) {
276                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
277
278                 if (*x == 0 && *y == 0)
279                         return true;
280
281                 r = dns_label_unescape(&x, la, sizeof(la));
282                 if (r < 0)
283                         return r;
284
285                 q = dns_label_unescape(&y, lb, sizeof(lb));
286                 if (q < 0)
287                         return q;
288
289                 la[r] = lb[q] = 0;
290                 if (strcasecmp(la, lb))
291                         return false;
292         }
293 }
294
295 int dns_name_endswith(const char *name, const char *suffix) {
296         const char *n, *s, *saved_n = NULL;
297         int r, q;
298
299         assert(name);
300         assert(suffix);
301
302         n = name;
303         s = suffix;
304
305         for (;;) {
306                 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
307
308                 r = dns_label_unescape(&n, ln, sizeof(ln));
309                 if (r < 0)
310                         return r;
311
312                 if (!saved_n)
313                         saved_n = n;
314
315                 q = dns_label_unescape(&s, ls, sizeof(ls));
316                 if (r < 0)
317                         return r;
318
319                 if (r == 0 && q == 0)
320                         return true;
321                 if (r == 0 && saved_n == n)
322                         return false;
323
324                 ln[r] = ls[q] = 0;
325
326                 if (r != q || strcasecmp(ln, ls)) {
327
328                         /* Not the same, let's jump back, and try with the next label again */
329                         s = suffix;
330                         n = saved_n;
331                         saved_n = NULL;
332                 }
333         }
334 }
335
336 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
337         const uint8_t *p;
338         int r;
339
340         assert(a);
341         assert(ret);
342
343         p = (const uint8_t*) a;
344
345         if (family == AF_INET)
346                 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
347         else if (family == AF_INET6)
348                 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",
349                              hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
350                              hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
351                              hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
352                              hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
353                              hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
354                              hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
355                              hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
356                              hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
357         else
358                 return -EAFNOSUPPORT;
359         if (r < 0)
360                 return -ENOMEM;
361
362         return 0;
363 }
364
365 int dns_name_address(const char *p, int *family, union in_addr_union *address) {
366         int r;
367
368         assert(p);
369         assert(family);
370         assert(address);
371
372         r = dns_name_endswith(p, "in-addr.arpa");
373         if (r < 0)
374                 return r;
375         if (r > 0) {
376                 uint8_t a[4];
377                 unsigned i;
378
379                 for (i = 0; i < ELEMENTSOF(a); i++) {
380                         char label[DNS_LABEL_MAX+1];
381
382                         r = dns_label_unescape(&p, label, sizeof(label));
383                         if (r < 0)
384                                 return r;
385                         if (r == 0)
386                                 return -EINVAL;
387                         if (r > 3)
388                                 return -EINVAL;
389
390                         r = safe_atou8(label, &a[i]);
391                         if (r < 0)
392                                 return r;
393                 }
394
395                 r = dns_name_equal(p, "in-addr.arpa");
396                 if (r <= 0)
397                         return r;
398
399                 *family = AF_INET;
400                 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
401                                              ((uint32_t) a[2] << 16) |
402                                              ((uint32_t) a[1] << 8) |
403                                               (uint32_t) a[0]);
404
405                 return 1;
406         }
407
408         r = dns_name_endswith(p, "ip6.arpa");
409         if (r < 0)
410                 return r;
411         if (r > 0) {
412                 struct in6_addr a;
413                 unsigned i;
414
415                 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
416                         char label[DNS_LABEL_MAX+1];
417                         int x, y;
418
419                         r = dns_label_unescape(&p, label, sizeof(label));
420                         if (r <= 0)
421                                 return r;
422                         if (r != 1)
423                                 return -EINVAL;
424                         x = unhexchar(label[0]);
425                         if (x < 0)
426                                 return -EINVAL;
427
428                         r = dns_label_unescape(&p, label, sizeof(label));
429                         if (r <= 0)
430                                 return r;
431                         if (r != 1)
432                                 return -EINVAL;
433                         y = unhexchar(label[0]);
434                         if (y < 0)
435                                 return -EINVAL;
436
437                         a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
438                 }
439
440                 r = dns_name_equal(p, "ip6.arpa");
441                 if (r <= 0)
442                         return r;
443
444                 *family = AF_INET6;
445                 address->in6 = a;
446                 return 1;
447         }
448
449         return 0;
450 }
451
452 int dns_name_root(const char *name) {
453         char label[DNS_LABEL_MAX+1];
454         int r;
455
456         assert(name);
457
458         r = dns_label_unescape(&name, label, sizeof(label));
459         if (r < 0)
460                 return r;
461
462         return r == 0 && *name == 0;
463 }
464
465 int dns_name_single_label(const char *name) {
466         char label[DNS_LABEL_MAX+1];
467         int r;
468
469         assert(name);
470
471         r = dns_label_unescape(&name, label, sizeof(label));
472         if (r < 0)
473                 return r;
474         if (r == 0)
475                 return 0;
476
477         r = dns_label_unescape(&name, label, sizeof(label));
478         if (r < 0)
479                 return r;
480
481         return r == 0 && *name == 0;
482 }