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