]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-domain.c
resolved: set LLMNR TCP and UDP TTLs to the values suggested by the RFC
[thirdparty/systemd.git] / src / resolve / resolved-dns-domain.c
CommitLineData
74b2466e
LP
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
24int 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
1fa65c59
LP
48 if (r >= DNS_LABEL_MAX)
49 return -EINVAL;
50
74b2466e
LP
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 (*n >= ' ' && *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
112int 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
1fa65c59
LP
120 if (l > DNS_LABEL_MAX)
121 return -EINVAL;
122
74b2466e
LP
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 (*p >= ' ' && *p != 127) {
145
146 /* Everything else */
147 *(q++) = '\\';
148 *(q++) = '0' + (char) ((unsigned) *p / 100);
149 *(q++) = '0' + (char) (((unsigned) *p / 10) % 10);
150 *(q++) = '0' + (char) ((unsigned) *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
167int 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 assert(_ret);
176
177 for (;;) {
178 _cleanup_free_ char *t = NULL;
179 char label[DNS_LABEL_MAX];
180
181 r = dns_label_unescape(&p, label, sizeof(label));
182 if (r < 0)
183 return r;
184 if (r == 0) {
185 if (*p != 0)
186 return -EINVAL;
187 break;
188 }
189
190 r = dns_label_escape(label, r, &t);
191 if (r < 0)
192 return r;
193
194 if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
195 return -ENOMEM;
196
197 if (!first)
198 ret[n++] = '.';
199 else
200 first = false;
201
202 memcpy(ret + n, t, r);
203 n += r;
204 }
205
76f468c8
LP
206 if (n > DNS_NAME_MAX)
207 return -EINVAL;
208
74b2466e
LP
209 if (!GREEDY_REALLOC(ret, allocated, n + 1))
210 return -ENOMEM;
211
212 ret[n] = 0;
213 *_ret = ret;
214 ret = NULL;
215
216 return 0;
217}
218
219unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
220 const char *p = s;
322345fd 221 unsigned long ul = hash_key[0];
74b2466e
LP
222 int r;
223
224 assert(p);
225
226 while (*p) {
227 char label[DNS_LABEL_MAX+1];
228
229 r = dns_label_unescape(&p, label, sizeof(label));
230 if (r < 0)
231 break;
232
233 label[r] = 0;
234 ascii_strlower(label);
235
322345fd 236 ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key);
74b2466e
LP
237 }
238
239 return ul;
240}
241
242int dns_name_compare_func(const void *a, const void *b) {
243 const char *x = a, *y = b;
244 int r, q;
245
246 assert(a);
247 assert(b);
248
249 for (;;) {
250 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
251
252 if (*x == 0 && *y == 0)
253 return 0;
254
255 r = dns_label_unescape(&x, la, sizeof(la));
256 q = dns_label_unescape(&y, lb, sizeof(lb));
257 if (r < 0 || q < 0)
258 return r - q;
259
260 la[r] = lb[q] = 0;
261 r = strcasecmp(la, lb);
262 if (r != 0)
263 return r;
264 }
265}
266
267int dns_name_equal(const char *x, const char *y) {
268 int r, q;
269
270 assert(x);
271 assert(y);
272
273 for (;;) {
274 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
275
276 if (*x == 0 && *y == 0)
277 return true;
278
279 r = dns_label_unescape(&x, la, sizeof(la));
280 if (r < 0)
281 return r;
282
283 q = dns_label_unescape(&y, lb, sizeof(lb));
284 if (q < 0)
285 return q;
286
287 la[r] = lb[q] = 0;
288 if (strcasecmp(la, lb))
289 return false;
290 }
291}
292
293int dns_name_endswith(const char *name, const char *suffix) {
294 const char *n, *s, *saved_n = NULL;
295 int r, q;
296
297 assert(name);
298 assert(suffix);
299
300 n = name;
301 s = suffix;
302
303 for (;;) {
304 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
305
306 r = dns_label_unescape(&n, ln, sizeof(ln));
307 if (r < 0)
308 return r;
309
310 if (!saved_n)
311 saved_n = n;
312
313 q = dns_label_unescape(&s, ls, sizeof(ls));
314 if (r < 0)
315 return r;
316
317 if (r == 0 && q == 0)
318 return true;
319 if (r == 0 && saved_n == n)
320 return false;
321
322 ln[r] = ls[q] = 0;
323
324 if (r != q || strcasecmp(ln, ls)) {
325
326 /* Not the same, let's jump back, and try with the next label again */
327 s = suffix;
328 n = saved_n;
329 saved_n = NULL;
330 }
331 }
332}
333
334int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
335 const uint8_t *p;
336 int r;
337
338 assert(a);
339 assert(ret);
340
341 p = (const uint8_t*) a;
342
343 if (family == AF_INET)
344 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
345 else if (family == AF_INET6)
3fe1169f
LP
346 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",
347 hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
348 hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
349 hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
350 hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
351 hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
352 hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
353 hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
354 hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
74b2466e
LP
355 else
356 return -EAFNOSUPPORT;
357 if (r < 0)
358 return -ENOMEM;
359
360 return 0;
361}
362
b914e211
LP
363int dns_name_address(const char *p, int *family, union in_addr_union *address) {
364 int r;
365
366 assert(p);
367 assert(family);
368 assert(address);
369
370 r = dns_name_endswith(p, "in-addr.arpa");
371 if (r < 0)
372 return r;
373 if (r > 0) {
374 uint8_t a[4];
375 unsigned i;
376
377 for (i = 0; i < ELEMENTSOF(a); i++) {
378 char label[DNS_LABEL_MAX+1];
379
380 r = dns_label_unescape(&p, label, sizeof(label));
381 if (r < 0)
382 return r;
383 if (r == 0)
384 return -EINVAL;
385 if (r > 3)
386 return -EINVAL;
387
388 r = safe_atou8(label, &a[i]);
389 if (r < 0)
390 return r;
391 }
392
393 r = dns_name_equal(p, "in-addr.arpa");
394 if (r <= 0)
395 return r;
396
397 *family = AF_INET;
398 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
399 ((uint32_t) a[2] << 16) |
400 ((uint32_t) a[1] << 8) |
401 (uint32_t) a[0]);
402
403 return 1;
404 }
405
406 r = dns_name_endswith(p, "ip6.arpa");
407 if (r < 0)
408 return r;
409 if (r > 0) {
410 struct in6_addr a;
411 unsigned i;
412
413 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
414 char label[DNS_LABEL_MAX+1];
415 int x, y;
416
417 r = dns_label_unescape(&p, label, sizeof(label));
418 if (r <= 0)
419 return r;
420 if (r != 1)
421 return -EINVAL;
422 x = unhexchar(label[0]);
423 if (x < 0)
424 return -EINVAL;
425
426 r = dns_label_unescape(&p, label, sizeof(label));
427 if (r <= 0)
428 return r;
429 if (r != 1)
430 return -EINVAL;
431 y = unhexchar(label[0]);
432 if (y < 0)
433 return -EINVAL;
434
435 a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
436 }
437
438 r = dns_name_equal(p, "ip6.arpa");
439 if (r <= 0)
440 return r;
441
442 *family = AF_INET6;
443 address->in6 = a;
444 return 1;
445 }
446
447 return 0;
448}
449
74b2466e
LP
450int dns_name_root(const char *name) {
451 char label[DNS_LABEL_MAX+1];
452 int r;
453
454 assert(name);
455
456 r = dns_label_unescape(&name, label, sizeof(label));
457 if (r < 0)
458 return r;
459
460 return r == 0 && *name == 0;
461}
462
463int dns_name_single_label(const char *name) {
464 char label[DNS_LABEL_MAX+1];
465 int r;
466
467 assert(name);
468
469 r = dns_label_unescape(&name, label, sizeof(label));
470 if (r < 0)
471 return r;
74b2466e
LP
472 if (r == 0)
473 return 0;
474
475 r = dns_label_unescape(&name, label, sizeof(label));
476 if (r < 0)
477 return r;
478
479 return r == 0 && *name == 0;
480}