Bug Summary

File:build-scan/../src/shared/dns-domain.c
Warning:line 1062, column 49
Potential leak of memory pointed to by 'name'

Annotated Source Code

Press '?' to see keyboard shortcuts

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