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