]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-question.c
core: gracefully ignore PrivateBPF=yes if the kernel does not support it (#38238)
[thirdparty/systemd.git] / src / resolve / resolved-dns-question.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
faa133f3 2
284d7641
DDM
3#include <stdio.h>
4
b5efdb8a 5#include "alloc-util.h"
4ad7f276 6#include "dns-domain.h"
0f7091e6 7#include "dns-type.h"
b5efdb8a 8#include "resolved-dns-question.h"
68527d30 9#include "resolved-dns-rr.h"
0438aa57 10#include "socket-util.h"
284d7641 11#include "string-util.h"
faa133f3 12
da6053d0 13DnsQuestion *dns_question_new(size_t n) {
faa133f3
LP
14 DnsQuestion *q;
15
398c6118
LP
16 if (n > UINT16_MAX) /* We can only place 64K key in an question section at max */
17 n = UINT16_MAX;
faa133f3 18
ab715ddb 19 q = malloc0(offsetof(DnsQuestion, items) + sizeof(DnsQuestionItem) * n);
faa133f3
LP
20 if (!q)
21 return NULL;
22
23 q->n_ref = 1;
24 q->n_allocated = n;
25
26 return q;
27}
28
8301aa0b 29static DnsQuestion *dns_question_free(DnsQuestion *q) {
ab715ddb 30 DnsResourceKey *key;
faa133f3 31
8301aa0b 32 assert(q);
faa133f3 33
ab715ddb
SB
34 DNS_QUESTION_FOREACH(key, q)
35 dns_resource_key_unref(key);
36
8301aa0b 37 return mfree(q);
faa133f3
LP
38}
39
8301aa0b
YW
40DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsQuestion, dns_question, dns_question_free);
41
ab715ddb 42int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) {
2d34cf0c
ZJS
43 /* Insert without checking for duplicates. */
44
45 assert(key);
46 assert(q);
47
48 if (q->n_keys >= q->n_allocated)
49 return -ENOSPC;
50
ab715ddb
SB
51 q->items[q->n_keys++] = (DnsQuestionItem) {
52 .key = dns_resource_key_ref(key),
53 .flags = flags,
54 };
2d34cf0c
ZJS
55 return 0;
56}
57
4d593fb1
LP
58static int dns_question_add_raw_all(DnsQuestion *a, DnsQuestion *b) {
59 DnsQuestionItem *item;
60 int r;
61
62 DNS_QUESTION_FOREACH_ITEM(item, b) {
63 r = dns_question_add_raw(a, item->key, item->flags);
64 if (r < 0)
65 return r;
66 }
67
68 return 0;
69}
70
ab715ddb
SB
71int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags) {
72 DnsQuestionItem *item;
7e8e0422
LP
73 int r;
74
faa133f3
LP
75 assert(key);
76
8013e860
LP
77 if (!q)
78 return -ENOSPC;
79
ab715ddb
SB
80 DNS_QUESTION_FOREACH_ITEM(item, q) {
81 r = dns_resource_key_equal(item->key, key);
7e8e0422
LP
82 if (r < 0)
83 return r;
ab715ddb 84 if (r > 0 && item->flags == flags)
7e8e0422
LP
85 return 0;
86 }
87
ab715ddb 88 return dns_question_add_raw(q, key, flags);
faa133f3
LP
89}
90
4d593fb1
LP
91static int dns_question_add_all(DnsQuestion *a, DnsQuestion *b) {
92 DnsQuestionItem *item;
93 int r;
94
95 DNS_QUESTION_FOREACH_ITEM(item, b) {
96 r = dns_question_add(a, item->key, item->flags);
97 if (r < 0)
98 return r;
99 }
100
101 return 0;
102}
103
801ad6a6 104int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
ab715ddb 105 DnsResourceKey *key;
faa133f3
LP
106 int r;
107
faa133f3
LP
108 assert(rr);
109
8013e860
LP
110 if (!q)
111 return 0;
112
ab715ddb
SB
113 DNS_QUESTION_FOREACH(key, q) {
114 r = dns_resource_key_match_rr(key, rr, search_domain);
faa133f3
LP
115 if (r != 0)
116 return r;
117 }
118
119 return 0;
120}
121
542e0c84 122int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
ab715ddb 123 DnsResourceKey *key;
faa133f3
LP
124 int r;
125
faa133f3
LP
126 assert(rr);
127
8013e860
LP
128 if (!q)
129 return 0;
130
542e0c84
LP
131 if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME))
132 return 0;
133
ab715ddb 134 DNS_QUESTION_FOREACH(key, q) {
542e0c84 135 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
ab715ddb 136 if (!dns_type_may_redirect(key->type))
542e0c84
LP
137 return 0;
138
ab715ddb 139 r = dns_resource_key_match_cname_or_dname(key, rr->key, search_domain);
faa133f3
LP
140 if (r != 0)
141 return r;
142 }
143
34b9656f 144 return 0;
faa133f3
LP
145}
146
703e4f5e 147int dns_question_is_valid_for_query(DnsQuestion *q) {
faa133f3 148 const char *name;
da6053d0 149 size_t i;
faa133f3
LP
150 int r;
151
8013e860
LP
152 if (!q)
153 return 0;
faa133f3
LP
154
155 if (q->n_keys <= 0)
156 return 0;
157
158 if (q->n_keys > 65535)
159 return 0;
160
ab715ddb 161 name = dns_resource_key_name(q->items[0].key);
faa133f3
LP
162 if (!name)
163 return 0;
164
165 /* Check that all keys in this question bear the same name */
0f7091e6 166 for (i = 0; i < q->n_keys; i++) {
ab715ddb 167 assert(q->items[i].key);
34b9656f 168
0f7091e6 169 if (i > 0) {
ab715ddb 170 r = dns_name_equal(dns_resource_key_name(q->items[i].key), name);
0f7091e6
LP
171 if (r <= 0)
172 return r;
173 }
174
ab715ddb 175 if (!dns_type_is_valid_query(q->items[i].key->type))
0f7091e6 176 return 0;
faa133f3
LP
177 }
178
179 return 1;
180}
181
ab715ddb 182int dns_question_contains_key(DnsQuestion *q, const DnsResourceKey *k) {
da6053d0 183 size_t j;
1086182d
LP
184 int r;
185
1086182d
LP
186 assert(k);
187
ab715ddb 188 if (!q)
8013e860
LP
189 return 0;
190
ab715ddb
SB
191 for (j = 0; j < q->n_keys; j++) {
192 r = dns_resource_key_equal(q->items[j].key, k);
1086182d
LP
193 if (r != 0)
194 return r;
195 }
196
197 return 0;
198}
199
ab715ddb
SB
200static int dns_question_contains_item(DnsQuestion *q, const DnsQuestionItem *i) {
201 DnsQuestionItem *item;
202 int r;
203
204 assert(i);
205
206 DNS_QUESTION_FOREACH_ITEM(item, q) {
207 if (item->flags != i->flags)
208 continue;
209 r = dns_resource_key_equal(item->key, i->key);
210 if (r != 0)
211 return r;
212 }
213
214 return false;
215}
216
1086182d 217int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
ab715ddb 218 DnsQuestionItem *item;
1086182d
LP
219 int r;
220
b6800689
LP
221 if (a == b)
222 return 1;
223
8013e860
LP
224 if (!a)
225 return !b || b->n_keys == 0;
226 if (!b)
227 return a->n_keys == 0;
1086182d 228
ab715ddb 229 /* Checks if all items in a are also contained b, and vice versa */
1086182d 230
ab715ddb
SB
231 DNS_QUESTION_FOREACH_ITEM(item, a) {
232 r = dns_question_contains_item(b, item);
1086182d
LP
233 if (r <= 0)
234 return r;
235 }
ab715ddb
SB
236 DNS_QUESTION_FOREACH_ITEM(item, b) {
237 r = dns_question_contains_item(a, item);
1086182d
LP
238 if (r <= 0)
239 return r;
240 }
241
242 return 1;
243}
244
36d9205d 245int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
faa133f3 246 _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
23b298bc 247 DnsResourceKey *key;
faa133f3 248 bool same = true;
faa133f3
LP
249 int r;
250
36d9205d 251 assert(cname);
faa133f3 252 assert(ret);
58db254a 253 assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
faa133f3 254
23b298bc
LP
255 if (dns_question_size(q) <= 0) {
256 *ret = NULL;
8013e860
LP
257 return 0;
258 }
259
23b298bc 260 DNS_QUESTION_FOREACH(key, q) {
58db254a
LP
261 _cleanup_free_ char *destination = NULL;
262 const char *d;
263
264 if (cname->key->type == DNS_TYPE_CNAME)
265 d = cname->cname.name;
266 else {
1c02e7ba 267 r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination);
58db254a
LP
268 if (r < 0)
269 return r;
270 if (r == 0)
271 continue;
272
273 d = destination;
274 }
275
1c02e7ba 276 r = dns_name_equal(dns_resource_key_name(key), d);
faa133f3
LP
277 if (r < 0)
278 return r;
279
280 if (r == 0) {
281 same = false;
282 break;
283 }
284 }
285
23b298bc 286 /* Fully the same, indicate we didn't do a thing */
faa133f3 287 if (same) {
23b298bc 288 *ret = NULL;
faa133f3
LP
289 return 0;
290 }
291
292 n = dns_question_new(q->n_keys);
293 if (!n)
294 return -ENOMEM;
295
296 /* Create a new question, and patch in the new name */
23b298bc 297 DNS_QUESTION_FOREACH(key, q) {
faa133f3
LP
298 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
299
23b298bc 300 k = dns_resource_key_new_redirect(key, cname);
faa133f3
LP
301 if (!k)
302 return -ENOMEM;
303
ab715ddb 304 r = dns_question_add(n, k, 0);
faa133f3
LP
305 if (r < 0)
306 return r;
307 }
308
1cc6c93a 309 *ret = TAKE_PTR(n);
faa133f3
LP
310
311 return 1;
312}
45ec7efb 313
bfd5a068 314const char* dns_question_first_name(DnsQuestion *q) {
703e4f5e
LP
315
316 if (!q)
317 return NULL;
45ec7efb
LP
318
319 if (q->n_keys < 1)
320 return NULL;
321
ab715ddb 322 return dns_resource_key_name(q->items[0].key);
45ec7efb
LP
323}
324
23b298bc 325int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) {
45ec7efb 326 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
23b298bc 327 _cleanup_free_ char *buf = NULL;
45ec7efb
LP
328 int r;
329
330 assert(ret);
331 assert(name);
332
333 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
334 return -EAFNOSUPPORT;
335
0438aa57
LP
336 /* If IPv6 is off and the request has an unspecified lookup family, restrict it automatically to
337 * IPv4. */
338 if (family == AF_UNSPEC && !socket_ipv6_is_enabled())
339 family = AF_INET;
340
23b298bc
LP
341 if (convert_idna) {
342 r = dns_name_apply_idna(name, &buf);
343 if (r < 0)
344 return r;
ad1f3fe6 345 if (r > 0 && !streq(name, buf))
87057e24 346 name = buf;
ad1f3fe6
ZJS
347 else
348 /* We did not manage to create convert the idna name, or it's
349 * the same as the original name. We assume the caller already
5238e957 350 * created an unconverted question, so let's not repeat work
ad1f3fe6
ZJS
351 * unnecessarily. */
352 return -EALREADY;
23b298bc
LP
353 }
354
45ec7efb
LP
355 q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
356 if (!q)
357 return -ENOMEM;
358
359 if (family != AF_INET6) {
360 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
361
362 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name);
363 if (!key)
364 return -ENOMEM;
365
ab715ddb 366 r = dns_question_add(q, key, 0);
45ec7efb
LP
367 if (r < 0)
368 return r;
369 }
370
371 if (family != AF_INET) {
372 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
373
374 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
375 if (!key)
376 return -ENOMEM;
377
ab715ddb 378 r = dns_question_add(q, key, 0);
45ec7efb
LP
379 if (r < 0)
380 return r;
381 }
382
1cc6c93a 383 *ret = TAKE_PTR(q);
45ec7efb
LP
384
385 return 0;
386}
387
388int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
389 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
390 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
391 _cleanup_free_ char *reverse = NULL;
392 int r;
393
394 assert(ret);
395 assert(a);
396
397 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
398 return -EAFNOSUPPORT;
399
400 r = dns_name_reverse(family, a, &reverse);
401 if (r < 0)
402 return r;
403
404 q = dns_question_new(1);
405 if (!q)
406 return -ENOMEM;
407
408 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
409 if (!key)
410 return -ENOMEM;
411
412 reverse = NULL;
8458b7fb
VCS
413
414 r = dns_question_add(q, key, 0);
415 if (r < 0)
416 return r;
417
418 *ret = TAKE_PTR(q);
419
420 return 0;
421}
422
423int dns_question_new_service_pointer(DnsQuestion **ret, const char *type, const char *domain, bool convert_idna) {
424 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
425 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
426 _cleanup_free_ char *buf = NULL, *joined = NULL;
427 const char *name;
428 int r;
429
430 assert(ret);
431
432 if (!domain)
433 return -EINVAL;
434
435 if (type) {
436 if (convert_idna) {
437 r = dns_name_apply_idna(domain, &buf);
438 if (r < 0)
439 return r;
440 if (r > 0)
441 domain = buf;
442 }
443
444 r = dns_service_join(NULL, type, domain, &joined);
445 if (r < 0)
446 return r;
447
448 name = joined;
449 } else
450 name = domain;
451
452
453 q = dns_question_new(1);
454 if (!q)
455 return -ENOMEM;
456
457 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_PTR, name);
458 if (!key)
459 return -ENOMEM;
45ec7efb 460
ab715ddb 461 r = dns_question_add(q, key, 0);
45ec7efb
LP
462 if (r < 0)
463 return r;
464
1cc6c93a 465 *ret = TAKE_PTR(q);
45ec7efb
LP
466
467 return 0;
468}
469
23b298bc
LP
470int dns_question_new_service(
471 DnsQuestion **ret,
472 const char *service,
473 const char *type,
474 const char *domain,
475 bool with_txt,
476 bool convert_idna) {
477
45ec7efb
LP
478 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
479 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
23b298bc
LP
480 _cleanup_free_ char *buf = NULL, *joined = NULL;
481 const char *name;
45ec7efb
LP
482 int r;
483
484 assert(ret);
23b298bc
LP
485
486 /* We support three modes of invocation:
487 *
488 * 1. Only a domain is specified, in which case we assume a properly encoded SRV RR name, including service
489 * type and possibly a service name. If specified in this way we assume it's already IDNA converted if
490 * that's necessary.
491 *
492 * 2. Both service type and a domain specified, in which case a normal SRV RR is assumed, without a DNS-SD
493 * style prefix. In this case we'll IDNA convert the domain, if that's requested.
494 *
495 * 3. All three of service name, type and domain are specified, in which case a DNS-SD service is put
496 * together. The service name is never IDNA converted, and the domain is if requested.
497 *
498 * It's not supported to specify a service name without a type, or no domain name.
499 */
500
501 if (!domain)
502 return -EINVAL;
503
504 if (type) {
505 if (convert_idna) {
506 r = dns_name_apply_idna(domain, &buf);
507 if (r < 0)
508 return r;
87057e24
ZJS
509 if (r > 0)
510 domain = buf;
23b298bc
LP
511 }
512
513 r = dns_service_join(service, type, domain, &joined);
514 if (r < 0)
515 return r;
516
517 name = joined;
518 } else {
519 if (service)
520 return -EINVAL;
521
522 name = domain;
523 }
45ec7efb
LP
524
525 q = dns_question_new(1 + with_txt);
526 if (!q)
527 return -ENOMEM;
528
529 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
530 if (!key)
531 return -ENOMEM;
532
ab715ddb 533 r = dns_question_add(q, key, 0);
45ec7efb
LP
534 if (r < 0)
535 return r;
536
537 if (with_txt) {
538 dns_resource_key_unref(key);
539 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
540 if (!key)
541 return -ENOMEM;
542
ab715ddb 543 r = dns_question_add(q, key, 0);
45ec7efb
LP
544 if (r < 0)
545 return r;
546 }
547
1cc6c93a 548 *ret = TAKE_PTR(q);
45ec7efb
LP
549
550 return 0;
551}
1414b67e
LP
552
553/*
554 * This function is not used in the code base, but is useful when debugging. Do not delete.
555 */
556void dns_question_dump(DnsQuestion *question, FILE *f) {
557 DnsResourceKey *k;
558
559 if (!f)
560 f = stdout;
561
562 DNS_QUESTION_FOREACH(k, question) {
563 char buf[DNS_RESOURCE_KEY_STRING_MAX];
564
565 fputc('\t', f);
566 fputs(dns_resource_key_to_string(k, buf, sizeof(buf)), f);
567 fputc('\n', f);
568 }
569}
4d593fb1
LP
570
571int dns_question_merge(DnsQuestion *a, DnsQuestion *b, DnsQuestion **ret) {
572 _cleanup_(dns_question_unrefp) DnsQuestion *k = NULL;
573 int r;
574
575 assert(ret);
576
577 if (a == b || dns_question_size(b) <= 0) {
578 *ret = dns_question_ref(a);
579 return 0;
580 }
581
582 if (dns_question_size(a) <= 0) {
583 *ret = dns_question_ref(b);
584 return 0;
585 }
586
587 k = dns_question_new(dns_question_size(a) + dns_question_size(b));
588 if (!k)
589 return -ENOMEM;
590
591 r = dns_question_add_raw_all(k, a);
592 if (r < 0)
593 return r;
594
595 r = dns_question_add_all(k, b);
596 if (r < 0)
597 return r;
598
599 *ret = TAKE_PTR(k);
600 return 0;
601}