]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-domain.c
dns-domain: introduce macros for accessing all DNS header fields
[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
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
216unsigned 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
239int 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
264int 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
290int 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
331int 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)
3fe1169f
LP
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));
74b2466e
LP
352 else
353 return -EAFNOSUPPORT;
354 if (r < 0)
355 return -ENOMEM;
356
357 return 0;
358}
359
360int 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
373int 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}