Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #if HAVE_LIBIDN2
4 : : # include <idn2.h>
5 : : #elif HAVE_LIBIDN
6 : : # include <idna.h>
7 : : # include <stringprep.h>
8 : : #endif
9 : :
10 : : #include <endian.h>
11 : : #include <netinet/in.h>
12 : : #include <stdio.h>
13 : : #include <string.h>
14 : : #include <sys/socket.h>
15 : :
16 : : #include "alloc-util.h"
17 : : #include "dns-domain.h"
18 : : #include "hashmap.h"
19 : : #include "hexdecoct.h"
20 : : #include "hostname-util.h"
21 : : #include "in-addr-util.h"
22 : : #include "macro.h"
23 : : #include "parse-util.h"
24 : : #include "string-util.h"
25 : : #include "strv.h"
26 : : #include "utf8.h"
27 : :
28 : 36046 : int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
29 : : const char *n;
30 : 36046 : char *d, last_char = 0;
31 : 36046 : int r = 0;
32 : :
33 [ - + ]: 36046 : assert(name);
34 [ - + ]: 36046 : assert(*name);
35 : :
36 : 36046 : n = *name;
37 : 36046 : d = dest;
38 : :
39 : : for (;;) {
40 [ + + + + ]: 189386 : if (IN_SET(*n, 0, '.')) {
41 [ + + + + ]: 35874 : if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
42 : : /* Trailing dash */
43 : 32 : return -EINVAL;
44 : :
45 [ + + ]: 35842 : if (*n == '.')
46 : 18412 : n++;
47 : 35842 : break;
48 : : }
49 : :
50 [ + + ]: 153512 : if (r >= DNS_LABEL_MAX)
51 : 8 : return -EINVAL;
52 : :
53 [ + + ]: 153504 : if (sz <= 0)
54 : 20 : return -ENOBUFS;
55 : :
56 [ + + ]: 153484 : if (*n == '\\') {
57 : : /* Escaped character */
58 [ + + ]: 228 : if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
59 : 24 : return -EINVAL;
60 : :
61 : 204 : n++;
62 : :
63 [ + + ]: 204 : if (*n == 0)
64 : : /* Ending NUL */
65 : 16 : return -EINVAL;
66 : :
67 [ + + + + ]: 188 : else if (IN_SET(*n, '\\', '.')) {
68 : : /* Escaped backslash or dot */
69 : :
70 [ + + ]: 32 : if (FLAGS_SET(flags, DNS_LABEL_LDH))
71 : 4 : return -EINVAL;
72 : :
73 : 28 : last_char = *n;
74 [ + - ]: 28 : if (d)
75 : 28 : *(d++) = *n;
76 : 28 : sz--;
77 : 28 : r++;
78 : 28 : n++;
79 : :
80 [ + - + + ]: 156 : } else if (n[0] >= '0' && n[0] <= '9') {
81 : : unsigned k;
82 : :
83 : : /* Escaped literal ASCII character */
84 : :
85 [ + - + - ]: 152 : if (!(n[1] >= '0' && n[1] <= '9') ||
86 [ + - - + ]: 152 : !(n[2] >= '0' && n[2] <= '9'))
87 : 0 : return -EINVAL;
88 : :
89 : 456 : k = ((unsigned) (n[0] - '0') * 100) +
90 : 304 : ((unsigned) (n[1] - '0') * 10) +
91 : 152 : ((unsigned) (n[2] - '0'));
92 : :
93 : : /* Don't allow anything that doesn't
94 : : * fit in 8bit. Note that we do allow
95 : : * control characters, as some servers
96 : : * (e.g. cloudflare) are happy to
97 : : * generate labels with them
98 : : * inside. */
99 [ - + ]: 152 : if (k > 255)
100 : 0 : return -EINVAL;
101 : :
102 [ + + ]: 152 : if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
103 [ + + ]: 12 : !valid_ldh_char((char) k))
104 : 8 : return -EINVAL;
105 : :
106 : 144 : last_char = (char) k;
107 [ + - ]: 144 : if (d)
108 : 144 : *(d++) = (char) k;
109 : 144 : sz--;
110 : 144 : r++;
111 : :
112 : 144 : n += 3;
113 : : } else
114 : 4 : return -EINVAL;
115 : :
116 [ + + + - ]: 153256 : } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
117 : :
118 : : /* Normal character */
119 : :
120 [ + + ]: 153216 : if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
121 [ + + ]: 6588 : if (!valid_ldh_char(*n))
122 : 8 : return -EINVAL;
123 [ + + + + ]: 6580 : if (r == 0 && *n == '-')
124 : : /* Leading dash */
125 : 40 : return -EINVAL;
126 : : }
127 : :
128 : 153168 : last_char = *n;
129 [ + + ]: 153168 : if (d)
130 : 151776 : *(d++) = *n;
131 : 153168 : sz--;
132 : 153168 : r++;
133 : 153168 : n++;
134 : : } else
135 : 40 : return -EINVAL;
136 : : }
137 : :
138 : : /* Empty label that is not at the end? */
139 [ + + + + ]: 35842 : if (r == 0 && *n)
140 : 72 : return -EINVAL;
141 : :
142 : : /* More than one trailing dot? */
143 [ + + ]: 35770 : if (*n == '.')
144 : 72 : return -EINVAL;
145 : :
146 [ + + + + ]: 35698 : if (sz >= 1 && d)
147 : 35022 : *d = 0;
148 : :
149 : 35698 : *name = n;
150 : 35698 : return r;
151 : : }
152 : :
153 : : /* @label_terminal: terminal character of a label, updated to point to the terminal character of
154 : : * the previous label (always skipping one dot) or to NULL if there are no more
155 : : * labels. */
156 : 2358 : int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
157 : : const char *terminal;
158 : : int r;
159 : :
160 [ - + ]: 2358 : assert(name);
161 [ - + ]: 2358 : assert(label_terminal);
162 [ - + ]: 2358 : assert(dest);
163 : :
164 : : /* no more labels */
165 [ + + ]: 2358 : if (!*label_terminal) {
166 [ + - ]: 180 : if (sz >= 1)
167 : 180 : *dest = 0;
168 : :
169 : 180 : return 0;
170 : : }
171 : :
172 : 2178 : terminal = *label_terminal;
173 [ + - - + ]: 2178 : assert(IN_SET(*terminal, 0, '.'));
174 : :
175 : : /* Skip current terminal character (and accept domain names ending it ".") */
176 [ + + ]: 2178 : if (*terminal == 0)
177 : 1230 : terminal--;
178 [ + + + + ]: 2178 : if (terminal >= name && *terminal == '.')
179 : 992 : terminal--;
180 : :
181 : : /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
182 : : for (;;) {
183 [ + + ]: 12834 : if (terminal < name) {
184 : : /* Reached the first label, so indicate that there are no more */
185 : 907 : terminal = NULL;
186 : 907 : break;
187 : : }
188 : :
189 : : /* Find the start of the last label */
190 [ + + ]: 11927 : if (*terminal == '.') {
191 : : const char *y;
192 : 1283 : unsigned slashes = 0;
193 : :
194 [ + + + + ]: 1311 : for (y = terminal - 1; y >= name && *y == '\\'; y--)
195 : 28 : slashes++;
196 : :
197 [ + + ]: 1283 : if (slashes % 2 == 0) {
198 : : /* The '.' was not escaped */
199 : 1271 : name = terminal + 1;
200 : 1271 : break;
201 : : } else {
202 : 12 : terminal = y;
203 : 12 : continue;
204 : : }
205 : : }
206 : :
207 : 10644 : terminal--;
208 : : }
209 : :
210 : 2178 : r = dns_label_unescape(&name, dest, sz, 0);
211 [ + + ]: 2178 : if (r < 0)
212 : 40 : return r;
213 : :
214 : 2138 : *label_terminal = terminal;
215 : :
216 : 2138 : return r;
217 : : }
218 : :
219 : 9572 : int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
220 : : char *q;
221 : :
222 : : /* DNS labels must be between 1 and 63 characters long. A
223 : : * zero-length label does not exist. See RFC 2182, Section
224 : : * 11. */
225 : :
226 [ + - - + ]: 9572 : if (l <= 0 || l > DNS_LABEL_MAX)
227 : 0 : return -EINVAL;
228 [ - + ]: 9572 : if (sz < 1)
229 : 0 : return -ENOBUFS;
230 : :
231 [ - + ]: 9572 : assert(p);
232 [ - + ]: 9572 : assert(dest);
233 : :
234 : 9572 : q = dest;
235 [ + + ]: 67292 : while (l > 0) {
236 : :
237 [ + + + + ]: 57720 : if (IN_SET(*p, '.', '\\')) {
238 : :
239 : : /* Dot or backslash */
240 : :
241 [ - + ]: 4 : if (sz < 3)
242 : 0 : return -ENOBUFS;
243 : :
244 : 4 : *(q++) = '\\';
245 : 4 : *(q++) = *p;
246 : :
247 : 4 : sz -= 2;
248 : :
249 [ + + + + ]: 57716 : } else if (IN_SET(*p, '_', '-') ||
250 [ + + + + ]: 56952 : (*p >= '0' && *p <= '9') ||
251 [ + + - + ]: 46452 : (*p >= 'a' && *p <= 'z') ||
252 [ + + + - ]: 636 : (*p >= 'A' && *p <= 'Z')) {
253 : :
254 : : /* Proper character */
255 : :
256 [ - + ]: 57660 : if (sz < 2)
257 : 0 : return -ENOBUFS;
258 : :
259 : 57660 : *(q++) = *p;
260 : 57660 : sz -= 1;
261 : :
262 : : } else {
263 : :
264 : : /* Everything else */
265 : :
266 [ - + ]: 56 : if (sz < 5)
267 : 0 : return -ENOBUFS;
268 : :
269 : 56 : *(q++) = '\\';
270 : 56 : *(q++) = '0' + (char) ((uint8_t) *p / 100);
271 : 56 : *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
272 : 56 : *(q++) = '0' + (char) ((uint8_t) *p % 10);
273 : :
274 : 56 : sz -= 4;
275 : : }
276 : :
277 : 57720 : p++;
278 : 57720 : l--;
279 : : }
280 : :
281 : 9572 : *q = 0;
282 : 9572 : return (int) (q - dest);
283 : : }
284 : :
285 : 16 : int dns_label_escape_new(const char *p, size_t l, char **ret) {
286 : 16 : _cleanup_free_ char *s = NULL;
287 : : int r;
288 : :
289 [ - + ]: 16 : assert(p);
290 [ - + ]: 16 : assert(ret);
291 : :
292 [ + + - + ]: 16 : if (l <= 0 || l > DNS_LABEL_MAX)
293 : 4 : return -EINVAL;
294 : :
295 : 12 : s = new(char, DNS_LABEL_ESCAPED_MAX);
296 [ - + ]: 12 : if (!s)
297 : 0 : return -ENOMEM;
298 : :
299 : 12 : r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
300 [ - + ]: 12 : if (r < 0)
301 : 0 : return r;
302 : :
303 : 12 : *ret = TAKE_PTR(s);
304 : :
305 : 12 : return r;
306 : : }
307 : :
308 : : #if HAVE_LIBIDN
309 : : int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
310 : : _cleanup_free_ uint32_t *input = NULL;
311 : : size_t input_size, l;
312 : : const char *p;
313 : : bool contains_8bit = false;
314 : : char buffer[DNS_LABEL_MAX+1];
315 : :
316 : : assert(encoded);
317 : : assert(decoded);
318 : :
319 : : /* Converts an U-label into an A-label */
320 : :
321 : : if (encoded_size <= 0)
322 : : return -EINVAL;
323 : :
324 : : for (p = encoded; p < encoded + encoded_size; p++)
325 : : if ((uint8_t) *p > 127)
326 : : contains_8bit = true;
327 : :
328 : : if (!contains_8bit) {
329 : : if (encoded_size > DNS_LABEL_MAX)
330 : : return -EINVAL;
331 : :
332 : : return 0;
333 : : }
334 : :
335 : : input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
336 : : if (!input)
337 : : return -ENOMEM;
338 : :
339 : : if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0)
340 : : return -EINVAL;
341 : :
342 : : l = strlen(buffer);
343 : :
344 : : /* Verify that the result is not longer than one DNS label. */
345 : : if (l <= 0 || l > DNS_LABEL_MAX)
346 : : return -EINVAL;
347 : : if (l > decoded_max)
348 : : return -ENOBUFS;
349 : :
350 : : memcpy(decoded, buffer, l);
351 : :
352 : : /* If there's room, append a trailing NUL byte, but only then */
353 : : if (decoded_max > l)
354 : : decoded[l] = 0;
355 : :
356 : : return (int) l;
357 : : }
358 : :
359 : : int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
360 : : size_t input_size, output_size;
361 : : _cleanup_free_ uint32_t *input = NULL;
362 : : _cleanup_free_ char *result = NULL;
363 : : uint32_t *output = NULL;
364 : : size_t w;
365 : :
366 : : /* To be invoked after unescaping. Converts an A-label into an U-label. */
367 : :
368 : : assert(encoded);
369 : : assert(decoded);
370 : :
371 : : if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX)
372 : : return -EINVAL;
373 : :
374 : : if (!memory_startswith(encoded, encoded_size, IDNA_ACE_PREFIX))
375 : : return 0;
376 : :
377 : : input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
378 : : if (!input)
379 : : return -ENOMEM;
380 : :
381 : : output_size = input_size;
382 : : output = newa(uint32_t, output_size);
383 : :
384 : : idna_to_unicode_44i(input, input_size, output, &output_size, 0);
385 : :
386 : : result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
387 : : if (!result)
388 : : return -ENOMEM;
389 : : if (w <= 0)
390 : : return -EINVAL;
391 : : if (w > decoded_max)
392 : : return -ENOBUFS;
393 : :
394 : : memcpy(decoded, result, w);
395 : :
396 : : /* Append trailing NUL byte if there's space, but only then. */
397 : : if (decoded_max > w)
398 : : decoded[w] = 0;
399 : :
400 : : return w;
401 : : }
402 : : #endif
403 : :
404 : 772 : int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
405 : 772 : _cleanup_free_ char *ret = NULL;
406 : 772 : size_t n = 0, allocated = 0;
407 : : const char *p;
408 : 772 : bool first = true;
409 : : int r;
410 : :
411 [ + + ]: 772 : if (a)
412 : 760 : p = a;
413 [ + + ]: 12 : else if (b)
414 : 8 : p = TAKE_PTR(b);
415 : : else
416 : 4 : goto finish;
417 : :
418 : 2124 : for (;;) {
419 : : char label[DNS_LABEL_MAX];
420 : :
421 : 2892 : r = dns_label_unescape(&p, label, sizeof label, flags);
422 [ + + ]: 2892 : if (r < 0)
423 : 148 : return r;
424 [ + + ]: 2744 : if (r == 0) {
425 [ - + ]: 740 : if (*p != 0)
426 : 0 : return -EINVAL;
427 : :
428 [ + + ]: 740 : if (b) {
429 : : /* Now continue with the second string, if there is one */
430 : 120 : p = TAKE_PTR(b);
431 : 120 : continue;
432 : : }
433 : :
434 : 620 : break;
435 : : }
436 : :
437 [ + + ]: 2004 : if (_ret) {
438 [ - + ]: 472 : if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
439 : 0 : return -ENOMEM;
440 : :
441 : 472 : r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
442 [ - + ]: 472 : if (r < 0)
443 : 0 : return r;
444 : :
445 [ + + ]: 472 : if (!first)
446 : 288 : ret[n] = '.';
447 : : } else {
448 : : char escaped[DNS_LABEL_ESCAPED_MAX];
449 : :
450 : 1532 : r = dns_label_escape(label, r, escaped, sizeof(escaped));
451 [ - + ]: 1532 : if (r < 0)
452 : 0 : return r;
453 : : }
454 : :
455 [ + + ]: 2004 : if (!first)
456 : 1404 : n++;
457 : : else
458 : 600 : first = false;
459 : :
460 : 2004 : n += r;
461 : : }
462 : :
463 : 624 : finish:
464 [ + + ]: 624 : if (n > DNS_HOSTNAME_MAX)
465 : 24 : return -EINVAL;
466 : :
467 [ + + ]: 600 : if (_ret) {
468 [ + + ]: 240 : if (n == 0) {
469 : : /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
470 [ - + ]: 56 : if (!GREEDY_REALLOC(ret, allocated, 2))
471 : 0 : return -ENOMEM;
472 : :
473 : 56 : ret[n++] = '.';
474 : : } else {
475 [ - + ]: 184 : if (!GREEDY_REALLOC(ret, allocated, n + 1))
476 : 0 : return -ENOMEM;
477 : : }
478 : :
479 : 240 : ret[n] = 0;
480 : 240 : *_ret = TAKE_PTR(ret);
481 : : }
482 : :
483 : 600 : return 0;
484 : : }
485 : :
486 : 3460 : void dns_name_hash_func(const char *p, struct siphash *state) {
487 : : int r;
488 : :
489 [ - + ]: 3460 : assert(p);
490 : :
491 : 8120 : for (;;) {
492 : : char label[DNS_LABEL_MAX+1];
493 : :
494 : 11580 : r = dns_label_unescape(&p, label, sizeof label, 0);
495 [ - + ]: 11580 : if (r < 0)
496 : 0 : break;
497 [ + + ]: 11580 : if (r == 0)
498 : 3460 : break;
499 : :
500 : 8120 : ascii_strlower_n(label, r);
501 : 8120 : siphash24_compress(label, r, state);
502 : 8120 : siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */
503 : : }
504 : :
505 : : /* enforce that all names are terminated by the empty label */
506 : 3460 : string_hash_func("", state);
507 : 3460 : }
508 : :
509 : 571 : int dns_name_compare_func(const char *a, const char *b) {
510 : : const char *x, *y;
511 : : int r, q;
512 : :
513 [ - + ]: 571 : assert(a);
514 [ - + ]: 571 : assert(b);
515 : :
516 : 571 : x = a + strlen(a);
517 : 571 : y = b + strlen(b);
518 : :
519 : 676 : for (;;) {
520 : : char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
521 : :
522 [ + + + + ]: 1247 : if (x == NULL && y == NULL)
523 : 571 : return 0;
524 : :
525 : 1103 : r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
526 : 1103 : q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
527 [ + - - + ]: 1103 : if (r < 0 || q < 0)
528 [ # # ]: 0 : return CMP(r, q);
529 : :
530 : 1103 : r = ascii_strcasecmp_nn(la, r, lb, q);
531 [ + + ]: 1103 : if (r != 0)
532 : 427 : return r;
533 : : }
534 : : }
535 : :
536 : : DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
537 : :
538 : 1464 : int dns_name_equal(const char *x, const char *y) {
539 : : int r, q;
540 : :
541 [ - + ]: 1464 : assert(x);
542 [ - + ]: 1464 : assert(y);
543 : :
544 : 4068 : for (;;) {
545 : : char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
546 : :
547 : 5532 : r = dns_label_unescape(&x, la, sizeof la, 0);
548 [ + + ]: 5532 : if (r < 0)
549 : 1464 : return r;
550 : :
551 : 5524 : q = dns_label_unescape(&y, lb, sizeof lb, 0);
552 [ - + ]: 5524 : if (q < 0)
553 : 0 : return q;
554 : :
555 [ + + ]: 5524 : if (r != q)
556 : 36 : return false;
557 [ + + ]: 5488 : if (r == 0)
558 : 1400 : return true;
559 : :
560 [ + + ]: 4088 : if (ascii_strcasecmp_n(la, lb, r) != 0)
561 : 20 : return false;
562 : : }
563 : : }
564 : :
565 : 116 : int dns_name_endswith(const char *name, const char *suffix) {
566 : 116 : const char *n, *s, *saved_n = NULL;
567 : : int r, q;
568 : :
569 [ - + ]: 116 : assert(name);
570 [ - + ]: 116 : assert(suffix);
571 : :
572 : 116 : n = name;
573 : 116 : s = suffix;
574 : :
575 : 804 : for (;;) {
576 : : char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
577 : :
578 : 920 : r = dns_label_unescape(&n, ln, sizeof ln, 0);
579 [ + + ]: 920 : if (r < 0)
580 : 116 : return r;
581 : :
582 [ + + ]: 916 : if (!saved_n)
583 : 804 : saved_n = n;
584 : :
585 : 916 : q = dns_label_unescape(&s, ls, sizeof ls, 0);
586 [ - + ]: 916 : if (q < 0)
587 : 0 : return q;
588 : :
589 [ + + + + ]: 916 : if (r == 0 && q == 0)
590 : 72 : return true;
591 [ + + + - ]: 844 : if (r == 0 && saved_n == n)
592 : 40 : return false;
593 : :
594 [ + + + + ]: 804 : if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
595 : :
596 : : /* Not the same, let's jump back, and try with the next label again */
597 : 692 : s = suffix;
598 : 692 : n = TAKE_PTR(saved_n);
599 : : }
600 : : }
601 : : }
602 : :
603 : 48 : int dns_name_startswith(const char *name, const char *prefix) {
604 : : const char *n, *p;
605 : : int r, q;
606 : :
607 [ - + ]: 48 : assert(name);
608 [ - + ]: 48 : assert(prefix);
609 : :
610 : 48 : n = name;
611 : 48 : p = prefix;
612 : :
613 : 24 : for (;;) {
614 : : char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
615 : :
616 : 72 : r = dns_label_unescape(&p, lp, sizeof lp, 0);
617 [ - + ]: 72 : if (r < 0)
618 : 48 : return r;
619 [ + + ]: 72 : if (r == 0)
620 : 32 : return true;
621 : :
622 : 40 : q = dns_label_unescape(&n, ln, sizeof ln, 0);
623 [ - + ]: 40 : if (q < 0)
624 : 0 : return q;
625 : :
626 [ + + ]: 40 : if (r != q)
627 : 4 : return false;
628 [ + + ]: 36 : if (ascii_strcasecmp_n(ln, lp, r) != 0)
629 : 12 : return false;
630 : : }
631 : : }
632 : :
633 : 36 : int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
634 : 36 : const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
635 : : int r, q;
636 : :
637 [ - + ]: 36 : assert(name);
638 [ - + ]: 36 : assert(old_suffix);
639 [ - + ]: 36 : assert(new_suffix);
640 [ - + ]: 36 : assert(ret);
641 : :
642 : 36 : n = name;
643 : 36 : s = old_suffix;
644 : :
645 : 92 : for (;;) {
646 : : char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
647 : :
648 [ + + ]: 128 : if (!saved_before)
649 : 84 : saved_before = n;
650 : :
651 : 128 : r = dns_label_unescape(&n, ln, sizeof ln, 0);
652 [ - + ]: 128 : if (r < 0)
653 : 4 : return r;
654 : :
655 [ + + ]: 128 : if (!saved_after)
656 : 84 : saved_after = n;
657 : :
658 : 128 : q = dns_label_unescape(&s, ls, sizeof ls, 0);
659 [ - + ]: 128 : if (q < 0)
660 : 0 : return q;
661 : :
662 [ + + + + ]: 128 : if (r == 0 && q == 0)
663 : 32 : break;
664 [ + + + - ]: 96 : if (r == 0 && saved_after == n) {
665 : 4 : *ret = NULL; /* doesn't match */
666 : 4 : return 0;
667 : : }
668 : :
669 [ + + + + ]: 92 : if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
670 : :
671 : : /* Not the same, let's jump back, and try with the next label again */
672 : 48 : s = old_suffix;
673 : 48 : n = TAKE_PTR(saved_after);
674 : 48 : saved_before = NULL;
675 : : }
676 : : }
677 : :
678 : : /* Found it! Now generate the new name */
679 : 32 : prefix = strndupa(name, saved_before - name);
680 : :
681 : 32 : r = dns_name_concat(prefix, new_suffix, 0, ret);
682 [ - + ]: 32 : if (r < 0)
683 : 0 : return r;
684 : :
685 : 32 : return 1;
686 : : }
687 : :
688 : 112 : int dns_name_between(const char *a, const char *b, const char *c) {
689 : : /* Determine if b is strictly greater than a and strictly smaller than c.
690 : : We consider the order of names to be circular, so that if a is
691 : : strictly greater than c, we consider b to be between them if it is
692 : : either greater than a or smaller than c. This is how the canonical
693 : : DNS name order used in NSEC records work. */
694 : :
695 [ + + ]: 112 : if (dns_name_compare_func(a, c) < 0)
696 : : /*
697 : : a and c are properly ordered:
698 : : a<---b--->c
699 : : */
700 [ + + + + ]: 88 : return dns_name_compare_func(a, b) < 0 &&
701 : 40 : dns_name_compare_func(b, c) < 0;
702 : : else
703 : : /*
704 : : a and c are equal or 'reversed':
705 : : <--b--c a----->
706 : : or:
707 : : <-----c a--b-->
708 : : */
709 [ + + + + ]: 124 : return dns_name_compare_func(b, c) < 0 ||
710 : 60 : dns_name_compare_func(a, b) < 0;
711 : : }
712 : :
713 : 16 : int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
714 : : const uint8_t *p;
715 : : int r;
716 : :
717 [ - + ]: 16 : assert(a);
718 [ - + ]: 16 : assert(ret);
719 : :
720 : 16 : p = (const uint8_t*) a;
721 : :
722 [ + + ]: 16 : if (family == AF_INET)
723 : 8 : r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
724 [ + - ]: 8 : else if (family == AF_INET6)
725 : 256 : 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",
726 : 32 : hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
727 : 32 : hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
728 : 32 : hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
729 : 32 : hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
730 : 32 : hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
731 : 32 : hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
732 : 32 : hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
733 : 8 : hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
734 : : else
735 : 0 : return -EAFNOSUPPORT;
736 [ - + ]: 16 : if (r < 0)
737 : 0 : return -ENOMEM;
738 : :
739 : 16 : return 0;
740 : : }
741 : :
742 : 16 : int dns_name_address(const char *p, int *family, union in_addr_union *address) {
743 : : int r;
744 : :
745 [ - + ]: 16 : assert(p);
746 [ - + ]: 16 : assert(family);
747 [ - + ]: 16 : assert(address);
748 : :
749 : 16 : r = dns_name_endswith(p, "in-addr.arpa");
750 [ - + ]: 16 : if (r < 0)
751 : 0 : return r;
752 [ + + ]: 16 : if (r > 0) {
753 : : uint8_t a[4];
754 : : unsigned i;
755 : :
756 [ + + ]: 40 : for (i = 0; i < ELEMENTSOF(a); i++) {
757 : : char label[DNS_LABEL_MAX+1];
758 : :
759 : 32 : r = dns_label_unescape(&p, label, sizeof label, 0);
760 [ - + ]: 32 : if (r < 0)
761 : 0 : return r;
762 [ - + ]: 32 : if (r == 0)
763 : 0 : return -EINVAL;
764 [ - + ]: 32 : if (r > 3)
765 : 0 : return -EINVAL;
766 : :
767 : 32 : r = safe_atou8(label, &a[i]);
768 [ - + ]: 32 : if (r < 0)
769 : 0 : return r;
770 : : }
771 : :
772 : 8 : r = dns_name_equal(p, "in-addr.arpa");
773 [ - + ]: 8 : if (r <= 0)
774 : 0 : return r;
775 : :
776 : 8 : *family = AF_INET;
777 : 8 : address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
778 : : ((uint32_t) a[2] << 16) |
779 : : ((uint32_t) a[1] << 8) |
780 : : (uint32_t) a[0]);
781 : :
782 : 8 : return 1;
783 : : }
784 : :
785 : 8 : r = dns_name_endswith(p, "ip6.arpa");
786 [ - + ]: 8 : if (r < 0)
787 : 0 : return r;
788 [ + - ]: 8 : if (r > 0) {
789 : : struct in6_addr a;
790 : : unsigned i;
791 : :
792 [ + + ]: 136 : for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
793 : : char label[DNS_LABEL_MAX+1];
794 : : int x, y;
795 : :
796 : 128 : r = dns_label_unescape(&p, label, sizeof label, 0);
797 [ - + ]: 128 : if (r <= 0)
798 : 0 : return r;
799 [ - + ]: 128 : if (r != 1)
800 : 0 : return -EINVAL;
801 : 128 : x = unhexchar(label[0]);
802 [ - + ]: 128 : if (x < 0)
803 : 0 : return -EINVAL;
804 : :
805 : 128 : r = dns_label_unescape(&p, label, sizeof label, 0);
806 [ - + ]: 128 : if (r <= 0)
807 : 0 : return r;
808 [ - + ]: 128 : if (r != 1)
809 : 0 : return -EINVAL;
810 : 128 : y = unhexchar(label[0]);
811 [ - + ]: 128 : if (y < 0)
812 : 0 : return -EINVAL;
813 : :
814 : 128 : a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
815 : : }
816 : :
817 : 8 : r = dns_name_equal(p, "ip6.arpa");
818 [ - + ]: 8 : if (r <= 0)
819 : 0 : return r;
820 : :
821 : 8 : *family = AF_INET6;
822 : 8 : address->in6 = a;
823 : 8 : return 1;
824 : : }
825 : :
826 : 0 : return 0;
827 : : }
828 : :
829 : 11280 : bool dns_name_is_root(const char *name) {
830 : :
831 [ - + ]: 11280 : assert(name);
832 : :
833 : : /* There are exactly two ways to encode the root domain name:
834 : : * as empty string, or with a single dot. */
835 : :
836 : 11280 : return STR_IN_SET(name, "", ".");
837 : : }
838 : :
839 : 32 : bool dns_name_is_single_label(const char *name) {
840 : : int r;
841 : :
842 [ - + ]: 32 : assert(name);
843 : :
844 : 32 : r = dns_name_parent(&name);
845 [ + + ]: 32 : if (r <= 0)
846 : 12 : return false;
847 : :
848 : 20 : return dns_name_is_root(name);
849 : : }
850 : :
851 : : /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
852 : 100 : int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
853 : : uint8_t *label_length, *out;
854 : : int r;
855 : :
856 [ - + ]: 100 : assert(domain);
857 [ - + ]: 100 : assert(buffer);
858 : :
859 : 100 : out = buffer;
860 : :
861 : : do {
862 : : /* Reserve a byte for label length */
863 [ + + ]: 480 : if (len <= 0)
864 : 4 : return -ENOBUFS;
865 : 476 : len--;
866 : 476 : label_length = out;
867 : 476 : out++;
868 : :
869 : : /* Convert and copy a single label. Note that
870 : : * dns_label_unescape() returns 0 when it hits the end
871 : : * of the domain name, which we rely on here to encode
872 : : * the trailing NUL byte. */
873 : 476 : r = dns_label_unescape(&domain, (char *) out, len, 0);
874 [ + + ]: 476 : if (r < 0)
875 : 4 : return r;
876 : :
877 : : /* Optionally, output the name in DNSSEC canonical
878 : : * format, as described in RFC 4034, section 6.2. Or
879 : : * in other words: in lower-case. */
880 [ + + ]: 472 : if (canonical)
881 : 132 : ascii_strlower_n((char*) out, (size_t) r);
882 : :
883 : : /* Fill label length, move forward */
884 : 472 : *label_length = r;
885 : 472 : out += r;
886 : 472 : len -= r;
887 : :
888 [ + + ]: 472 : } while (r != 0);
889 : :
890 : : /* Verify the maximum size of the encoded name. The trailing
891 : : * dot + NUL byte account are included this time, hence
892 : : * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
893 : : * time. */
894 [ + + ]: 92 : if (out - buffer > DNS_HOSTNAME_MAX + 2)
895 : 4 : return -EINVAL;
896 : :
897 : 88 : return out - buffer;
898 : : }
899 : :
900 : 356 : static bool srv_type_label_is_valid(const char *label, size_t n) {
901 : : size_t k;
902 : :
903 [ - + ]: 356 : assert(label);
904 : :
905 [ + + ]: 356 : if (n < 2) /* Label needs to be at least 2 chars long */
906 : 16 : return false;
907 : :
908 [ + + ]: 340 : if (label[0] != '_') /* First label char needs to be underscore */
909 : 20 : return false;
910 : :
911 : : /* Second char must be a letter */
912 [ + + + - ]: 320 : if (!(label[1] >= 'A' && label[1] <= 'Z') &&
913 [ + + - + ]: 320 : !(label[1] >= 'a' && label[1] <= 'z'))
914 : 24 : return false;
915 : :
916 : : /* Third and further chars must be alphanumeric or a hyphen */
917 [ + + ]: 968 : for (k = 2; k < n; k++) {
918 [ + + + - ]: 680 : if (!(label[k] >= 'A' && label[k] <= 'Z') &&
919 [ + + - + ]: 680 : !(label[k] >= 'a' && label[k] <= 'z') &&
920 [ + + - + ]: 72 : !(label[k] >= '0' && label[k] <= '9') &&
921 [ + + ]: 24 : label[k] != '-')
922 : 8 : return false;
923 : : }
924 : :
925 : 288 : return true;
926 : : }
927 : :
928 : 204 : bool dns_srv_type_is_valid(const char *name) {
929 : 204 : unsigned c = 0;
930 : : int r;
931 : :
932 [ + + ]: 204 : if (!name)
933 : 8 : return false;
934 : :
935 : 216 : for (;;) {
936 : : char label[DNS_LABEL_MAX];
937 : :
938 : : /* This more or less implements RFC 6335, Section 5.1 */
939 : :
940 : 412 : r = dns_label_unescape(&name, label, sizeof label, 0);
941 [ + + ]: 412 : if (r < 0)
942 : 68 : return false;
943 [ + + ]: 404 : if (r == 0)
944 : 128 : break;
945 : :
946 [ + + ]: 276 : if (c >= 2)
947 : 8 : return false;
948 : :
949 [ + + ]: 268 : if (!srv_type_label_is_valid(label, r))
950 : 52 : return false;
951 : :
952 : 216 : c++;
953 : : }
954 : :
955 : 128 : return c == 2; /* exactly two labels */
956 : : }
957 : :
958 : 76 : bool dnssd_srv_type_is_valid(const char *name) {
959 [ + + + + ]: 108 : return dns_srv_type_is_valid(name) &&
960 [ + + ]: 32 : ((dns_name_endswith(name, "_tcp") > 0) ||
961 : 8 : (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */
962 : : }
963 : :
964 : 76 : bool dns_service_name_is_valid(const char *name) {
965 : : size_t l;
966 : :
967 : : /* This more or less implements RFC 6763, Section 4.1.1 */
968 : :
969 [ + + ]: 76 : if (!name)
970 : 4 : return false;
971 : :
972 [ + + ]: 72 : if (!utf8_is_valid(name))
973 : 4 : return false;
974 : :
975 [ + + ]: 68 : if (string_has_cc(name, NULL))
976 : 4 : return false;
977 : :
978 : 64 : l = strlen(name);
979 [ + + ]: 64 : if (l <= 0)
980 : 12 : return false;
981 [ + + ]: 52 : if (l > 63)
982 : 4 : return false;
983 : :
984 : 48 : return true;
985 : : }
986 : :
987 : 56 : int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
988 : : char escaped[DNS_LABEL_ESCAPED_MAX];
989 : 56 : _cleanup_free_ char *n = NULL;
990 : : int r;
991 : :
992 [ - + ]: 56 : assert(type);
993 [ - + ]: 56 : assert(domain);
994 [ - + ]: 56 : assert(ret);
995 : :
996 [ + + ]: 56 : if (!dns_srv_type_is_valid(type))
997 : 12 : return -EINVAL;
998 : :
999 [ + + ]: 44 : if (!name)
1000 : 16 : return dns_name_concat(type, domain, 0, ret);
1001 : :
1002 [ + + ]: 28 : if (!dns_service_name_is_valid(name))
1003 : 8 : return -EINVAL;
1004 : :
1005 : 20 : r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
1006 [ - + ]: 20 : if (r < 0)
1007 : 0 : return r;
1008 : :
1009 : 20 : r = dns_name_concat(type, domain, 0, &n);
1010 [ - + ]: 20 : if (r < 0)
1011 : 0 : return r;
1012 : :
1013 : 20 : return dns_name_concat(escaped, n, 0, ret);
1014 : : }
1015 : :
1016 : 20 : static bool dns_service_name_label_is_valid(const char *label, size_t n) {
1017 : : char *s;
1018 : :
1019 [ - + ]: 20 : assert(label);
1020 : :
1021 [ - + ]: 20 : if (memchr(label, 0, n))
1022 : 0 : return false;
1023 : :
1024 : 20 : s = strndupa(label, n);
1025 : 20 : return dns_service_name_is_valid(s);
1026 : : }
1027 : :
1028 : 52 : int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
1029 : 52 : _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
1030 : 52 : const char *p = joined, *q = NULL, *d = NULL;
1031 : : char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
1032 : : int an, bn, cn, r;
1033 : 52 : unsigned x = 0;
1034 : :
1035 [ - + ]: 52 : assert(joined);
1036 : :
1037 : : /* Get first label from the full name */
1038 : 52 : an = dns_label_unescape(&p, a, sizeof(a), 0);
1039 [ - + ]: 52 : if (an < 0)
1040 : 0 : return an;
1041 : :
1042 [ + + ]: 52 : if (an > 0) {
1043 : 48 : x++;
1044 : :
1045 : : /* If there was a first label, try to get the second one */
1046 : 48 : bn = dns_label_unescape(&p, b, sizeof(b), 0);
1047 [ - + ]: 48 : if (bn < 0)
1048 : 0 : return bn;
1049 : :
1050 [ + + ]: 48 : if (bn > 0) {
1051 : 44 : x++;
1052 : :
1053 : : /* If there was a second label, try to get the third one */
1054 : 44 : q = p;
1055 : 44 : cn = dns_label_unescape(&p, c, sizeof(c), 0);
1056 [ - + ]: 44 : if (cn < 0)
1057 : 0 : return cn;
1058 : :
1059 [ + + ]: 44 : if (cn > 0)
1060 : 28 : x++;
1061 : : } else
1062 : 4 : cn = 0;
1063 : : } else
1064 : 4 : an = 0;
1065 : :
1066 [ + + + + ]: 52 : if (x >= 2 && srv_type_label_is_valid(b, bn)) {
1067 : :
1068 [ + + + + ]: 36 : if (x >= 3 && srv_type_label_is_valid(c, cn)) {
1069 : :
1070 [ + - ]: 20 : if (dns_service_name_label_is_valid(a, an)) {
1071 : : /* OK, got <name> . <type> . <type2> . <domain> */
1072 : :
1073 : 20 : name = strndup(a, an);
1074 [ - + ]: 20 : if (!name)
1075 : 0 : return -ENOMEM;
1076 : :
1077 : 20 : type = strjoin(b, ".", c);
1078 [ - + ]: 20 : if (!type)
1079 : 0 : return -ENOMEM;
1080 : :
1081 : 20 : d = p;
1082 : 20 : goto finish;
1083 : : }
1084 : :
1085 [ + - ]: 16 : } else if (srv_type_label_is_valid(a, an)) {
1086 : :
1087 : : /* OK, got <type> . <type2> . <domain> */
1088 : :
1089 : 16 : name = NULL;
1090 : :
1091 : 16 : type = strjoin(a, ".", b);
1092 [ - + ]: 16 : if (!type)
1093 : 0 : return -ENOMEM;
1094 : :
1095 : 16 : d = q;
1096 : 16 : goto finish;
1097 : : }
1098 : : }
1099 : :
1100 : 16 : name = NULL;
1101 : 16 : type = NULL;
1102 : 16 : d = joined;
1103 : :
1104 : 52 : finish:
1105 : 52 : r = dns_name_normalize(d, 0, &domain);
1106 [ - + ]: 52 : if (r < 0)
1107 : 0 : return r;
1108 : :
1109 [ + - ]: 52 : if (_domain)
1110 : 52 : *_domain = TAKE_PTR(domain);
1111 : :
1112 [ + - ]: 52 : if (_type)
1113 : 52 : *_type = TAKE_PTR(type);
1114 : :
1115 [ + - ]: 52 : if (_name)
1116 : 52 : *_name = TAKE_PTR(name);
1117 : :
1118 : 52 : return 0;
1119 : : }
1120 : :
1121 : 144 : static int dns_name_build_suffix_table(const char *name, const char *table[]) {
1122 : : const char *p;
1123 : 144 : unsigned n = 0;
1124 : : int r;
1125 : :
1126 [ - + ]: 144 : assert(name);
1127 [ - + ]: 144 : assert(table);
1128 : :
1129 : 144 : p = name;
1130 : : for (;;) {
1131 [ - + ]: 344 : if (n > DNS_N_LABELS_MAX)
1132 : 0 : return -EINVAL;
1133 : :
1134 : 344 : table[n] = p;
1135 : 344 : r = dns_name_parent(&p);
1136 [ - + ]: 344 : if (r < 0)
1137 : 0 : return r;
1138 [ + + ]: 344 : if (r == 0)
1139 : 144 : break;
1140 : :
1141 : 200 : n++;
1142 : : }
1143 : :
1144 : 144 : return (int) n;
1145 : : }
1146 : :
1147 : 64 : int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
1148 : : const char* labels[DNS_N_LABELS_MAX+1];
1149 : : int n;
1150 : :
1151 [ - + ]: 64 : assert(name);
1152 [ - + ]: 64 : assert(ret);
1153 : :
1154 : 64 : n = dns_name_build_suffix_table(name, labels);
1155 [ - + ]: 64 : if (n < 0)
1156 : 0 : return n;
1157 : :
1158 [ + + ]: 64 : if ((unsigned) n < n_labels)
1159 : 24 : return -EINVAL;
1160 : :
1161 : 40 : *ret = labels[n - n_labels];
1162 : 40 : return (int) (n - n_labels);
1163 : : }
1164 : :
1165 : 96 : int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
1166 : : int r;
1167 : :
1168 [ - + ]: 96 : assert(a);
1169 [ - + ]: 96 : assert(ret);
1170 : :
1171 [ + + ]: 164 : for (; n_labels > 0; n_labels--) {
1172 : 96 : r = dns_name_parent(&a);
1173 [ - + ]: 96 : if (r < 0)
1174 : 0 : return r;
1175 [ + + ]: 96 : if (r == 0) {
1176 : 28 : *ret = "";
1177 : 28 : return 0;
1178 : : }
1179 : : }
1180 : :
1181 : 68 : *ret = a;
1182 : 68 : return 1;
1183 : : }
1184 : :
1185 : 68 : int dns_name_count_labels(const char *name) {
1186 : 68 : unsigned n = 0;
1187 : : const char *p;
1188 : : int r;
1189 : :
1190 [ - + ]: 68 : assert(name);
1191 : :
1192 : 68 : p = name;
1193 : : for (;;) {
1194 : 180 : r = dns_name_parent(&p);
1195 [ + + ]: 180 : if (r < 0)
1196 : 4 : return r;
1197 [ + + ]: 176 : if (r == 0)
1198 : 64 : break;
1199 : :
1200 [ - + ]: 112 : if (n >= DNS_N_LABELS_MAX)
1201 : 0 : return -EINVAL;
1202 : :
1203 : 112 : n++;
1204 : : }
1205 : :
1206 : 64 : return (int) n;
1207 : : }
1208 : :
1209 : 80 : int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
1210 : : int r;
1211 : :
1212 [ - + ]: 80 : assert(a);
1213 [ - + ]: 80 : assert(b);
1214 : :
1215 : 80 : r = dns_name_skip(a, n_labels, &a);
1216 [ + + ]: 80 : if (r <= 0)
1217 : 28 : return r;
1218 : :
1219 : 52 : return dns_name_equal(a, b);
1220 : : }
1221 : :
1222 : 40 : int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
1223 : : const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1];
1224 : 40 : int n = 0, m = 0, k = 0, r, q;
1225 : :
1226 [ - + ]: 40 : assert(a);
1227 [ - + ]: 40 : assert(b);
1228 [ - + ]: 40 : assert(ret);
1229 : :
1230 : : /* Determines the common suffix of domain names a and b */
1231 : :
1232 : 40 : n = dns_name_build_suffix_table(a, a_labels);
1233 [ - + ]: 40 : if (n < 0)
1234 : 0 : return n;
1235 : :
1236 : 40 : m = dns_name_build_suffix_table(b, b_labels);
1237 [ - + ]: 40 : if (m < 0)
1238 : 0 : return m;
1239 : :
1240 : 24 : for (;;) {
1241 : : char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
1242 : : const char *x, *y;
1243 : :
1244 [ + + + + ]: 64 : if (k >= n || k >= m) {
1245 : 24 : *ret = a_labels[n - k];
1246 : 40 : return 0;
1247 : : }
1248 : :
1249 : 40 : x = a_labels[n - 1 - k];
1250 : 40 : r = dns_label_unescape(&x, la, sizeof la, 0);
1251 [ - + ]: 40 : if (r < 0)
1252 : 0 : return r;
1253 : :
1254 : 40 : y = b_labels[m - 1 - k];
1255 : 40 : q = dns_label_unescape(&y, lb, sizeof lb, 0);
1256 [ - + ]: 40 : if (q < 0)
1257 : 0 : return q;
1258 : :
1259 [ + + + + ]: 40 : if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) {
1260 : 16 : *ret = a_labels[n - k];
1261 : 16 : return 0;
1262 : : }
1263 : :
1264 : 24 : k++;
1265 : : }
1266 : : }
1267 : :
1268 : 60 : int dns_name_apply_idna(const char *name, char **ret) {
1269 : : /* Return negative on error, 0 if not implemented, positive on success. */
1270 : :
1271 : : #if HAVE_LIBIDN2
1272 : : int r;
1273 : 60 : _cleanup_free_ char *t = NULL;
1274 : :
1275 [ - + ]: 60 : assert(name);
1276 [ - + ]: 60 : assert(ret);
1277 : :
1278 : 60 : r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
1279 : : IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
1280 [ + - ]: 60 : log_debug("idn2_lookup_u8: %s → %s", name, t);
1281 [ + + ]: 60 : if (r == IDN2_OK) {
1282 [ + + ]: 56 : if (!startswith(name, "xn--")) {
1283 [ + - ]: 52 : _cleanup_free_ char *s = NULL;
1284 : :
1285 : 52 : r = idn2_to_unicode_8z8z(t, &s, 0);
1286 [ - + ]: 52 : if (r != IDN2_OK) {
1287 [ # # ]: 0 : log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s",
1288 : : t, r, idn2_strerror(r));
1289 : 0 : return 0;
1290 : : }
1291 : :
1292 [ - + ]: 52 : if (!streq_ptr(name, s)) {
1293 [ # # ]: 0 : log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.",
1294 : : name, t, s);
1295 : 0 : return 0;
1296 : : }
1297 : : }
1298 : :
1299 : 56 : *ret = TAKE_PTR(t);
1300 : :
1301 : 56 : return 1; /* *ret has been written */
1302 : : }
1303 : :
1304 [ + - ]: 4 : log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
1305 [ + - ]: 4 : if (r == IDN2_2HYPHEN)
1306 : : /* The name has two hyphens — forbidden by IDNA2008 in some cases */
1307 : 4 : return 0;
1308 [ # # # # ]: 0 : if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
1309 : 0 : return -ENOSPC;
1310 : 0 : return -EINVAL;
1311 : : #elif HAVE_LIBIDN
1312 : : _cleanup_free_ char *buf = NULL;
1313 : : size_t n = 0, allocated = 0;
1314 : : bool first = true;
1315 : : int r, q;
1316 : :
1317 : : assert(name);
1318 : : assert(ret);
1319 : :
1320 : : for (;;) {
1321 : : char label[DNS_LABEL_MAX];
1322 : :
1323 : : r = dns_label_unescape(&name, label, sizeof label, 0);
1324 : : if (r < 0)
1325 : : return r;
1326 : : if (r == 0)
1327 : : break;
1328 : :
1329 : : q = dns_label_apply_idna(label, r, label, sizeof label);
1330 : : if (q < 0)
1331 : : return q;
1332 : : if (q > 0)
1333 : : r = q;
1334 : :
1335 : : if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
1336 : : return -ENOMEM;
1337 : :
1338 : : r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
1339 : : if (r < 0)
1340 : : return r;
1341 : :
1342 : : if (first)
1343 : : first = false;
1344 : : else
1345 : : buf[n++] = '.';
1346 : :
1347 : : n += r;
1348 : : }
1349 : :
1350 : : if (n > DNS_HOSTNAME_MAX)
1351 : : return -EINVAL;
1352 : :
1353 : : if (!GREEDY_REALLOC(buf, allocated, n + 1))
1354 : : return -ENOMEM;
1355 : :
1356 : : buf[n] = 0;
1357 : : *ret = TAKE_PTR(buf);
1358 : :
1359 : : return 1;
1360 : : #else
1361 : : return 0;
1362 : : #endif
1363 : : }
1364 : :
1365 : 100 : int dns_name_is_valid_or_address(const char *name) {
1366 : : /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */
1367 : :
1368 [ + + ]: 100 : if (isempty(name))
1369 : 8 : return 0;
1370 : :
1371 [ + + ]: 92 : if (in_addr_from_string_auto(name, NULL, NULL) >= 0)
1372 : 12 : return 1;
1373 : :
1374 : 80 : return dns_name_is_valid(name);
1375 : : }
|