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