Coverage Report

Created: 2025-06-13 06:27

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