chiark / gitweb /
sd-network: remove redundant array size parameter from functions that return arrays
[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 (n > DNS_NAME_MAX)
207                 return -EINVAL;
208
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
219 unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) {
220         const char *p = s;
221         unsigned long ul = 0;
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
236                 ul = hash_key[0] * ul + ul + string_hash_func(label, hash_key);
237         }
238
239         return ul;
240 }
241
242 int 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
267 int 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
293 int 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
334 int 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)
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));
355         else
356                 return -EAFNOSUPPORT;
357         if (r < 0)
358                 return -ENOMEM;
359
360         return 0;
361 }
362
363 int dns_name_root(const char *name) {
364         char label[DNS_LABEL_MAX+1];
365         int r;
366
367         assert(name);
368
369         r = dns_label_unescape(&name, label, sizeof(label));
370         if (r < 0)
371                 return r;
372
373         return r == 0 && *name == 0;
374 }
375
376 int dns_name_single_label(const char *name) {
377         char label[DNS_LABEL_MAX+1];
378         int r;
379
380         assert(name);
381
382         r = dns_label_unescape(&name, label, sizeof(label));
383         if (r < 0)
384                 return r;
385
386         if (r == 0)
387                 return 0;
388
389         r = dns_label_unescape(&name, label, sizeof(label));
390         if (r < 0)
391                 return r;
392
393         return r == 0 && *name == 0;
394 }