chiark / gitweb /
33ae2610e117460c2a59ac4b313234448f942229
[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 (*n == '\\') {
49                         /* Escaped character */
50
51                         n++;
52
53                         if (*n == 0)
54                                 /* Ending NUL */
55                                 return -EINVAL;
56
57                         else if (*n == '\\' || *n == '.') {
58                                 /* Escaped backslash or dot */
59                                 *(d++) = *(n++);
60                                 sz--;
61                                 r++;
62
63                         } else if (n[0] >= '0' && n[0] <= '9') {
64                                 unsigned k;
65
66                                 /* Escaped literal ASCII character */
67
68                                 if (!(n[1] >= '0' && n[1] <= '9') ||
69                                     !(n[2] >= '0' && n[2] <= '9'))
70                                         return -EINVAL;
71
72                                 k = ((unsigned) (n[0] - '0') * 100) +
73                                         ((unsigned) (n[1] - '0') * 10) +
74                                         ((unsigned) (n[2] - '0'));
75
76                                 /* Don't allow CC characters or anything that doesn't fit in 8bit */
77                                 if (k < ' ' || k > 255 || k == 127)
78                                         return -EINVAL;
79
80                                 *(d++) = (char) k;
81                                 sz--;
82                                 r++;
83
84                                 n += 3;
85                         } else
86                                 return -EINVAL;
87
88                 } else if (*n >= ' ' && *n != 127) {
89
90                         /* Normal character */
91                         *(d++) = *(n++);
92                         sz--;
93                         r++;
94                 } else
95                         return -EINVAL;
96         }
97
98         /* Empty label that is not at the end? */
99         if (r == 0 && *n)
100                 return -EINVAL;
101
102         if (sz >= 1)
103                 *d = 0;
104
105         *name = n;
106         return r;
107 }
108
109 int dns_label_escape(const char *p, size_t l, char **ret) {
110         _cleanup_free_ char *s = NULL;
111         char *q;
112         int r;
113
114         assert(p);
115         assert(ret);
116
117         s = malloc(l * 4 + 1);
118         if (!s)
119                 return -ENOMEM;
120
121         q = s;
122         while (l > 0) {
123
124                 if (*p == '.' || *p == '\\') {
125
126                         /* Dot or backslash */
127                         *(q++) = '\\';
128                         *(q++) = *p;
129
130                 } else if (*p == '_' ||
131                            *p == '-' ||
132                            (*p >= '0' && *p <= '9') ||
133                            (*p >= 'a' && *p <= 'z') ||
134                            (*p >= 'A' && *p <= 'Z')) {
135
136                         /* Proper character */
137                         *(q++) = *p;
138                 } else if (*p >= ' ' && *p != 127) {
139
140                         /* Everything else */
141                         *(q++) = '\\';
142                         *(q++) = '0' + (char) ((unsigned) *p / 100);
143                         *(q++) = '0' + (char) (((unsigned) *p / 10) % 10);
144                         *(q++) = '0' + (char) ((unsigned) *p % 10);
145
146                 } else
147                         return -EINVAL;
148
149                 p++;
150                 l--;
151         }
152
153         *q = 0;
154         *ret = s;
155         r = q - s;
156         s = NULL;
157
158         return r;
159 }
160
161 int dns_name_normalize(const char *s, char **_ret) {
162         _cleanup_free_ char *ret = NULL;
163         size_t n = 0, allocated = 0;
164         const char *p = s;
165         bool first = true;
166         int r;
167
168         assert(s);
169         assert(_ret);
170
171         for (;;) {
172                 _cleanup_free_ char *t = NULL;
173                 char label[DNS_LABEL_MAX];
174
175                 r = dns_label_unescape(&p, label, sizeof(label));
176                 if (r < 0)
177                         return r;
178                 if (r == 0) {
179                         if (*p != 0)
180                                 return -EINVAL;
181                         break;
182                 }
183
184                 r = dns_label_escape(label, r, &t);
185                 if (r < 0)
186                         return r;
187
188                 if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1))
189                         return -ENOMEM;
190
191                 if (!first)
192                         ret[n++] = '.';
193                 else
194                         first = false;
195
196                 memcpy(ret + n, t, r);
197                 n += r;
198         }
199
200         if (!GREEDY_REALLOC(ret, allocated, n + 1))
201                 return -ENOMEM;
202
203         ret[n] = 0;
204         *_ret = ret;
205         ret = NULL;
206
207         return 0;
208 }
209
210 unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
211         const char *p = s;
212         unsigned long ul = 0;
213         int r;
214
215         assert(p);
216
217         while (*p) {
218                 char label[DNS_LABEL_MAX+1];
219
220                 r = dns_label_unescape(&p, label, sizeof(label));
221                 if (r < 0)
222                         break;
223
224                 label[r] = 0;
225                 ascii_strlower(label);
226
227                 ul = hash_key[0] * ul + ul + string_hash_func(label, hash_key);
228         }
229
230         return ul;
231 }
232
233 int dns_name_compare_func(const void *a, const void *b) {
234         const char *x = a, *y = b;
235         int r, q;
236
237         assert(a);
238         assert(b);
239
240         for (;;) {
241                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
242
243                 if (*x == 0 && *y == 0)
244                         return 0;
245
246                 r = dns_label_unescape(&x, la, sizeof(la));
247                 q = dns_label_unescape(&y, lb, sizeof(lb));
248                 if (r < 0 || q < 0)
249                         return r - q;
250
251                 la[r] = lb[q] = 0;
252                 r = strcasecmp(la, lb);
253                 if (r != 0)
254                         return r;
255         }
256 }
257
258 int dns_name_equal(const char *x, const char *y) {
259         int r, q;
260
261         assert(x);
262         assert(y);
263
264         for (;;) {
265                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
266
267                 if (*x == 0 && *y == 0)
268                         return true;
269
270                 r = dns_label_unescape(&x, la, sizeof(la));
271                 if (r < 0)
272                         return r;
273
274                 q = dns_label_unescape(&y, lb, sizeof(lb));
275                 if (q < 0)
276                         return q;
277
278                 la[r] = lb[q] = 0;
279                 if (strcasecmp(la, lb))
280                         return false;
281         }
282 }
283
284 int dns_name_endswith(const char *name, const char *suffix) {
285         const char *n, *s, *saved_n = NULL;
286         int r, q;
287
288         assert(name);
289         assert(suffix);
290
291         n = name;
292         s = suffix;
293
294         for (;;) {
295                 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
296
297                 r = dns_label_unescape(&n, ln, sizeof(ln));
298                 if (r < 0)
299                         return r;
300
301                 if (!saved_n)
302                         saved_n = n;
303
304                 q = dns_label_unescape(&s, ls, sizeof(ls));
305                 if (r < 0)
306                         return r;
307
308                 if (r == 0 && q == 0)
309                         return true;
310                 if (r == 0 && saved_n == n)
311                         return false;
312
313                 ln[r] = ls[q] = 0;
314
315                 if (r != q || strcasecmp(ln, ls)) {
316
317                         /* Not the same, let's jump back, and try with the next label again */
318                         s = suffix;
319                         n = saved_n;
320                         saved_n = NULL;
321                 }
322         }
323 }
324
325 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
326         const uint8_t *p;
327         int r;
328
329         assert(a);
330         assert(ret);
331
332         p = (const uint8_t*) a;
333
334         if (family == AF_INET)
335                 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
336         else if (family == AF_INET6)
337                 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",
338                              hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
339                              hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
340                              hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
341                              hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
342                              hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
343                              hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
344                              hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
345                              hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
346         else
347                 return -EAFNOSUPPORT;
348         if (r < 0)
349                 return -ENOMEM;
350
351         return 0;
352 }
353
354 int dns_name_root(const char *name) {
355         char label[DNS_LABEL_MAX+1];
356         int r;
357
358         assert(name);
359
360         r = dns_label_unescape(&name, label, sizeof(label));
361         if (r < 0)
362                 return r;
363
364         return r == 0 && *name == 0;
365 }
366
367 int dns_name_single_label(const char *name) {
368         char label[DNS_LABEL_MAX+1];
369         int r;
370
371         assert(name);
372
373         r = dns_label_unescape(&name, label, sizeof(label));
374         if (r < 0)
375                 return r;
376
377         if (r == 0)
378                 return 0;
379
380         r = dns_label_unescape(&name, label, sizeof(label));
381         if (r < 0)
382                 return r;
383
384         return r == 0 && *name == 0;
385 }