]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-question.c
Merge pull request #8025 from sourcejedi/pid1_journal_or2
[thirdparty/systemd.git] / src / resolve / resolved-dns-question.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
faa133f3
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
b5efdb8a 21#include "alloc-util.h"
4ad7f276 22#include "dns-domain.h"
0f7091e6 23#include "dns-type.h"
b5efdb8a 24#include "resolved-dns-question.h"
faa133f3
LP
25
26DnsQuestion *dns_question_new(unsigned n) {
27 DnsQuestion *q;
28
29 assert(n > 0);
30
31 q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n);
32 if (!q)
33 return NULL;
34
35 q->n_ref = 1;
36 q->n_allocated = n;
37
38 return q;
39}
40
41DnsQuestion *dns_question_ref(DnsQuestion *q) {
42 if (!q)
43 return NULL;
44
45 assert(q->n_ref > 0);
46 q->n_ref++;
47 return q;
48}
49
50DnsQuestion *dns_question_unref(DnsQuestion *q) {
51 if (!q)
52 return NULL;
53
54 assert(q->n_ref > 0);
55
56 if (q->n_ref == 1) {
57 unsigned i;
58
59 for (i = 0; i < q->n_keys; i++)
60 dns_resource_key_unref(q->keys[i]);
61 free(q);
62 } else
63 q->n_ref--;
64
65 return NULL;
66}
67
68int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
7e8e0422
LP
69 unsigned i;
70 int r;
71
faa133f3
LP
72 assert(key);
73
8013e860
LP
74 if (!q)
75 return -ENOSPC;
76
7e8e0422
LP
77 for (i = 0; i < q->n_keys; i++) {
78 r = dns_resource_key_equal(q->keys[i], key);
79 if (r < 0)
80 return r;
81 if (r > 0)
82 return 0;
83 }
84
faa133f3
LP
85 if (q->n_keys >= q->n_allocated)
86 return -ENOSPC;
87
88 q->keys[q->n_keys++] = dns_resource_key_ref(key);
89 return 0;
90}
91
801ad6a6 92int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
faa133f3
LP
93 unsigned i;
94 int r;
95
faa133f3
LP
96 assert(rr);
97
8013e860
LP
98 if (!q)
99 return 0;
100
faa133f3 101 for (i = 0; i < q->n_keys; i++) {
801ad6a6 102 r = dns_resource_key_match_rr(q->keys[i], rr, search_domain);
faa133f3
LP
103 if (r != 0)
104 return r;
105 }
106
107 return 0;
108}
109
542e0c84 110int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
faa133f3
LP
111 unsigned i;
112 int r;
113
faa133f3
LP
114 assert(rr);
115
8013e860
LP
116 if (!q)
117 return 0;
118
542e0c84
LP
119 if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME))
120 return 0;
121
faa133f3 122 for (i = 0; i < q->n_keys; i++) {
542e0c84
LP
123 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
124 if (!dns_type_may_redirect(q->keys[i]->type))
125 return 0;
126
5d27351f 127 r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
faa133f3
LP
128 if (r != 0)
129 return r;
130 }
131
34b9656f 132 return 0;
faa133f3
LP
133}
134
703e4f5e 135int dns_question_is_valid_for_query(DnsQuestion *q) {
faa133f3
LP
136 const char *name;
137 unsigned i;
138 int r;
139
8013e860
LP
140 if (!q)
141 return 0;
faa133f3
LP
142
143 if (q->n_keys <= 0)
144 return 0;
145
146 if (q->n_keys > 65535)
147 return 0;
148
1c02e7ba 149 name = dns_resource_key_name(q->keys[0]);
faa133f3
LP
150 if (!name)
151 return 0;
152
153 /* Check that all keys in this question bear the same name */
0f7091e6 154 for (i = 0; i < q->n_keys; i++) {
34b9656f
LP
155 assert(q->keys[i]);
156
0f7091e6 157 if (i > 0) {
1c02e7ba 158 r = dns_name_equal(dns_resource_key_name(q->keys[i]), name);
0f7091e6
LP
159 if (r <= 0)
160 return r;
161 }
162
163 if (!dns_type_is_valid_query(q->keys[i]->type))
164 return 0;
faa133f3
LP
165 }
166
167 return 1;
168}
169
6a21960c 170int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
1086182d
LP
171 unsigned j;
172 int r;
173
1086182d
LP
174 assert(k);
175
8013e860
LP
176 if (!a)
177 return 0;
178
1086182d
LP
179 for (j = 0; j < a->n_keys; j++) {
180 r = dns_resource_key_equal(a->keys[j], k);
181 if (r != 0)
182 return r;
183 }
184
185 return 0;
186}
187
188int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
189 unsigned j;
190 int r;
191
b6800689
LP
192 if (a == b)
193 return 1;
194
8013e860
LP
195 if (!a)
196 return !b || b->n_keys == 0;
197 if (!b)
198 return a->n_keys == 0;
1086182d
LP
199
200 /* Checks if all keys in a are also contained b, and vice versa */
201
202 for (j = 0; j < a->n_keys; j++) {
203 r = dns_question_contains(b, a->keys[j]);
204 if (r <= 0)
205 return r;
206 }
207
208 for (j = 0; j < b->n_keys; j++) {
209 r = dns_question_contains(a, b->keys[j]);
210 if (r <= 0)
211 return r;
212 }
213
214 return 1;
215}
216
36d9205d 217int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
faa133f3 218 _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
23b298bc 219 DnsResourceKey *key;
faa133f3 220 bool same = true;
faa133f3
LP
221 int r;
222
36d9205d 223 assert(cname);
faa133f3 224 assert(ret);
58db254a 225 assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
faa133f3 226
23b298bc
LP
227 if (dns_question_size(q) <= 0) {
228 *ret = NULL;
8013e860
LP
229 return 0;
230 }
231
23b298bc 232 DNS_QUESTION_FOREACH(key, q) {
58db254a
LP
233 _cleanup_free_ char *destination = NULL;
234 const char *d;
235
236 if (cname->key->type == DNS_TYPE_CNAME)
237 d = cname->cname.name;
238 else {
1c02e7ba 239 r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination);
58db254a
LP
240 if (r < 0)
241 return r;
242 if (r == 0)
243 continue;
244
245 d = destination;
246 }
247
1c02e7ba 248 r = dns_name_equal(dns_resource_key_name(key), d);
faa133f3
LP
249 if (r < 0)
250 return r;
251
252 if (r == 0) {
253 same = false;
254 break;
255 }
256 }
257
23b298bc 258 /* Fully the same, indicate we didn't do a thing */
faa133f3 259 if (same) {
23b298bc 260 *ret = NULL;
faa133f3
LP
261 return 0;
262 }
263
264 n = dns_question_new(q->n_keys);
265 if (!n)
266 return -ENOMEM;
267
268 /* Create a new question, and patch in the new name */
23b298bc 269 DNS_QUESTION_FOREACH(key, q) {
faa133f3
LP
270 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
271
23b298bc 272 k = dns_resource_key_new_redirect(key, cname);
faa133f3
LP
273 if (!k)
274 return -ENOMEM;
275
276 r = dns_question_add(n, k);
277 if (r < 0)
278 return r;
279 }
280
281 *ret = n;
282 n = NULL;
283
284 return 1;
285}
45ec7efb 286
703e4f5e
LP
287const char *dns_question_first_name(DnsQuestion *q) {
288
289 if (!q)
290 return NULL;
45ec7efb
LP
291
292 if (q->n_keys < 1)
293 return NULL;
294
1c02e7ba 295 return dns_resource_key_name(q->keys[0]);
45ec7efb
LP
296}
297
23b298bc 298int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) {
45ec7efb 299 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
23b298bc 300 _cleanup_free_ char *buf = NULL;
45ec7efb
LP
301 int r;
302
303 assert(ret);
304 assert(name);
305
306 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
307 return -EAFNOSUPPORT;
308
23b298bc
LP
309 if (convert_idna) {
310 r = dns_name_apply_idna(name, &buf);
311 if (r < 0)
312 return r;
ad1f3fe6 313 if (r > 0 && !streq(name, buf))
87057e24 314 name = buf;
ad1f3fe6
ZJS
315 else
316 /* We did not manage to create convert the idna name, or it's
317 * the same as the original name. We assume the caller already
318 * created an uncoverted question, so let's not repeat work
319 * unnecessarily. */
320 return -EALREADY;
23b298bc
LP
321 }
322
45ec7efb
LP
323 q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
324 if (!q)
325 return -ENOMEM;
326
327 if (family != AF_INET6) {
328 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
329
330 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name);
331 if (!key)
332 return -ENOMEM;
333
334 r = dns_question_add(q, key);
335 if (r < 0)
336 return r;
337 }
338
339 if (family != AF_INET) {
340 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
341
342 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
343 if (!key)
344 return -ENOMEM;
345
346 r = dns_question_add(q, key);
347 if (r < 0)
348 return r;
349 }
350
351 *ret = q;
352 q = NULL;
353
354 return 0;
355}
356
357int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
358 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
359 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
360 _cleanup_free_ char *reverse = NULL;
361 int r;
362
363 assert(ret);
364 assert(a);
365
366 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
367 return -EAFNOSUPPORT;
368
369 r = dns_name_reverse(family, a, &reverse);
370 if (r < 0)
371 return r;
372
373 q = dns_question_new(1);
374 if (!q)
375 return -ENOMEM;
376
377 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
378 if (!key)
379 return -ENOMEM;
380
381 reverse = NULL;
382
383 r = dns_question_add(q, key);
384 if (r < 0)
385 return r;
386
387 *ret = q;
388 q = NULL;
389
390 return 0;
391}
392
23b298bc
LP
393int dns_question_new_service(
394 DnsQuestion **ret,
395 const char *service,
396 const char *type,
397 const char *domain,
398 bool with_txt,
399 bool convert_idna) {
400
45ec7efb
LP
401 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
402 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
23b298bc
LP
403 _cleanup_free_ char *buf = NULL, *joined = NULL;
404 const char *name;
45ec7efb
LP
405 int r;
406
407 assert(ret);
23b298bc
LP
408
409 /* We support three modes of invocation:
410 *
411 * 1. Only a domain is specified, in which case we assume a properly encoded SRV RR name, including service
412 * type and possibly a service name. If specified in this way we assume it's already IDNA converted if
413 * that's necessary.
414 *
415 * 2. Both service type and a domain specified, in which case a normal SRV RR is assumed, without a DNS-SD
416 * style prefix. In this case we'll IDNA convert the domain, if that's requested.
417 *
418 * 3. All three of service name, type and domain are specified, in which case a DNS-SD service is put
419 * together. The service name is never IDNA converted, and the domain is if requested.
420 *
421 * It's not supported to specify a service name without a type, or no domain name.
422 */
423
424 if (!domain)
425 return -EINVAL;
426
427 if (type) {
428 if (convert_idna) {
429 r = dns_name_apply_idna(domain, &buf);
430 if (r < 0)
431 return r;
87057e24
ZJS
432 if (r > 0)
433 domain = buf;
23b298bc
LP
434 }
435
436 r = dns_service_join(service, type, domain, &joined);
437 if (r < 0)
438 return r;
439
440 name = joined;
441 } else {
442 if (service)
443 return -EINVAL;
444
445 name = domain;
446 }
45ec7efb
LP
447
448 q = dns_question_new(1 + with_txt);
449 if (!q)
450 return -ENOMEM;
451
452 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
453 if (!key)
454 return -ENOMEM;
455
456 r = dns_question_add(q, key);
457 if (r < 0)
458 return r;
459
460 if (with_txt) {
461 dns_resource_key_unref(key);
462 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
463 if (!key)
464 return -ENOMEM;
465
466 r = dns_question_add(q, key);
467 if (r < 0)
468 return r;
469 }
470
471 *ret = q;
472 q = NULL;
473
474 return 0;
475}