chiark / gitweb /
resolved: support for TCP DNS queries
[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 (*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
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 (*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
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         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
206         if (!GREEDY_REALLOC(ret, allocated, n + 1))
207                 return -ENOMEM;
208
209         ret[n] = 0;
210         *_ret = ret;
211         ret = NULL;
212
213         return 0;
214 }
215
216 unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
217         const char *p = s;
218         unsigned long ul = 0;
219         int r;
220
221         assert(p);
222
223         while (*p) {
224                 char label[DNS_LABEL_MAX+1];
225
226                 r = dns_label_unescape(&p, label, sizeof(label));
227                 if (r < 0)
228                         break;
229
230                 label[r] = 0;
231                 ascii_strlower(label);
232
233                 ul = hash_key[0] * ul + ul + string_hash_func(label, hash_key);
234         }
235
236         return ul;
237 }
238
239 int dns_name_compare_func(const void *a, const void *b) {
240         const char *x = a, *y = b;
241         int r, q;
242
243         assert(a);
244         assert(b);
245
246         for (;;) {
247                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
248
249                 if (*x == 0 && *y == 0)
250                         return 0;
251
252                 r = dns_label_unescape(&x, la, sizeof(la));
253                 q = dns_label_unescape(&y, lb, sizeof(lb));
254                 if (r < 0 || q < 0)
255                         return r - q;
256
257                 la[r] = lb[q] = 0;
258                 r = strcasecmp(la, lb);
259                 if (r != 0)
260                         return r;
261         }
262 }
263
264 int dns_name_equal(const char *x, const char *y) {
265         int r, q;
266
267         assert(x);
268         assert(y);
269
270         for (;;) {
271                 char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
272
273                 if (*x == 0 && *y == 0)
274                         return true;
275
276                 r = dns_label_unescape(&x, la, sizeof(la));
277                 if (r < 0)
278                         return r;
279
280                 q = dns_label_unescape(&y, lb, sizeof(lb));
281                 if (q < 0)
282                         return q;
283
284                 la[r] = lb[q] = 0;
285                 if (strcasecmp(la, lb))
286                         return false;
287         }
288 }
289
290 int dns_name_endswith(const char *name, const char *suffix) {
291         const char *n, *s, *saved_n = NULL;
292         int r, q;
293
294         assert(name);
295         assert(suffix);
296
297         n = name;
298         s = suffix;
299
300         for (;;) {
301                 char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
302
303                 r = dns_label_unescape(&n, ln, sizeof(ln));
304                 if (r < 0)
305                         return r;
306
307                 if (!saved_n)
308                         saved_n = n;
309
310                 q = dns_label_unescape(&s, ls, sizeof(ls));
311                 if (r < 0)
312                         return r;
313
314                 if (r == 0 && q == 0)
315                         return true;
316                 if (r == 0 && saved_n == n)
317                         return false;
318
319                 ln[r] = ls[q] = 0;
320
321                 if (r != q || strcasecmp(ln, ls)) {
322
323                         /* Not the same, let's jump back, and try with the next label again */
324                         s = suffix;
325                         n = saved_n;
326                         saved_n = NULL;
327                 }
328         }
329 }
330
331 int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
332         const uint8_t *p;
333         int r;
334
335         assert(a);
336         assert(ret);
337
338         p = (const uint8_t*) a;
339
340         if (family == AF_INET)
341                 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
342         else if (family == AF_INET6)
343                 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",
344                              hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
345                              hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
346                              hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
347                              hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
348                              hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
349                              hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
350                              hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
351                              hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
352         else
353                 return -EAFNOSUPPORT;
354         if (r < 0)
355                 return -ENOMEM;
356
357         return 0;
358 }
359
360 int dns_name_root(const char *name) {
361         char label[DNS_LABEL_MAX+1];
362         int r;
363
364         assert(name);
365
366         r = dns_label_unescape(&name, label, sizeof(label));
367         if (r < 0)
368                 return r;
369
370         return r == 0 && *name == 0;
371 }
372
373 int dns_name_single_label(const char *name) {
374         char label[DNS_LABEL_MAX+1];
375         int r;
376
377         assert(name);
378
379         r = dns_label_unescape(&name, label, sizeof(label));
380         if (r < 0)
381                 return r;
382
383         if (r == 0)
384                 return 0;
385
386         r = dns_label_unescape(&name, label, sizeof(label));
387         if (r < 0)
388                 return r;
389
390         return r == 0 && *name == 0;
391 }