]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-question.c
resolved: when following a CNAME, turn off search domains
[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
801ad6a6 111int dns_question_matches_cname(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
faa133f3 120 for (i = 0; i < q->n_keys; i++) {
5d27351f 121 r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
faa133f3
LP
122 if (r != 0)
123 return r;
124 }
125
34b9656f 126 return 0;
faa133f3
LP
127}
128
703e4f5e 129int dns_question_is_valid_for_query(DnsQuestion *q) {
faa133f3
LP
130 const char *name;
131 unsigned i;
132 int r;
133
8013e860
LP
134 if (!q)
135 return 0;
faa133f3
LP
136
137 if (q->n_keys <= 0)
138 return 0;
139
140 if (q->n_keys > 65535)
141 return 0;
142
143 name = DNS_RESOURCE_KEY_NAME(q->keys[0]);
144 if (!name)
145 return 0;
146
147 /* Check that all keys in this question bear the same name */
0f7091e6 148 for (i = 0; i < q->n_keys; i++) {
34b9656f
LP
149 assert(q->keys[i]);
150
0f7091e6
LP
151 if (i > 0) {
152 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name);
153 if (r <= 0)
154 return r;
155 }
156
157 if (!dns_type_is_valid_query(q->keys[i]->type))
158 return 0;
faa133f3
LP
159 }
160
161 return 1;
162}
163
6a21960c 164int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
1086182d
LP
165 unsigned j;
166 int r;
167
1086182d
LP
168 assert(k);
169
8013e860
LP
170 if (!a)
171 return 0;
172
1086182d
LP
173 for (j = 0; j < a->n_keys; j++) {
174 r = dns_resource_key_equal(a->keys[j], k);
175 if (r != 0)
176 return r;
177 }
178
179 return 0;
180}
181
182int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
183 unsigned j;
184 int r;
185
b6800689
LP
186 if (a == b)
187 return 1;
188
8013e860
LP
189 if (!a)
190 return !b || b->n_keys == 0;
191 if (!b)
192 return a->n_keys == 0;
1086182d
LP
193
194 /* Checks if all keys in a are also contained b, and vice versa */
195
196 for (j = 0; j < a->n_keys; j++) {
197 r = dns_question_contains(b, a->keys[j]);
198 if (r <= 0)
199 return r;
200 }
201
202 for (j = 0; j < b->n_keys; j++) {
203 r = dns_question_contains(a, b->keys[j]);
204 if (r <= 0)
205 return r;
206 }
207
208 return 1;
209}
210
36d9205d 211int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
faa133f3 212 _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
23b298bc 213 DnsResourceKey *key;
faa133f3 214 bool same = true;
faa133f3
LP
215 int r;
216
36d9205d 217 assert(cname);
faa133f3 218 assert(ret);
58db254a 219 assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
faa133f3 220
23b298bc
LP
221 if (dns_question_size(q) <= 0) {
222 *ret = NULL;
8013e860
LP
223 return 0;
224 }
225
23b298bc 226 DNS_QUESTION_FOREACH(key, q) {
58db254a
LP
227 _cleanup_free_ char *destination = NULL;
228 const char *d;
229
230 if (cname->key->type == DNS_TYPE_CNAME)
231 d = cname->cname.name;
232 else {
23b298bc 233 r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
58db254a
LP
234 if (r < 0)
235 return r;
236 if (r == 0)
237 continue;
238
239 d = destination;
240 }
241
23b298bc 242 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), d);
faa133f3
LP
243 if (r < 0)
244 return r;
245
246 if (r == 0) {
247 same = false;
248 break;
249 }
250 }
251
23b298bc 252 /* Fully the same, indicate we didn't do a thing */
faa133f3 253 if (same) {
23b298bc 254 *ret = NULL;
faa133f3
LP
255 return 0;
256 }
257
258 n = dns_question_new(q->n_keys);
259 if (!n)
260 return -ENOMEM;
261
262 /* Create a new question, and patch in the new name */
23b298bc 263 DNS_QUESTION_FOREACH(key, q) {
faa133f3
LP
264 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
265
23b298bc 266 k = dns_resource_key_new_redirect(key, cname);
faa133f3
LP
267 if (!k)
268 return -ENOMEM;
269
270 r = dns_question_add(n, k);
271 if (r < 0)
272 return r;
273 }
274
275 *ret = n;
276 n = NULL;
277
278 return 1;
279}
45ec7efb 280
703e4f5e
LP
281const char *dns_question_first_name(DnsQuestion *q) {
282
283 if (!q)
284 return NULL;
45ec7efb
LP
285
286 if (q->n_keys < 1)
287 return NULL;
288
289 return DNS_RESOURCE_KEY_NAME(q->keys[0]);
290}
291
23b298bc 292int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) {
45ec7efb 293 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
23b298bc 294 _cleanup_free_ char *buf = NULL;
45ec7efb
LP
295 int r;
296
297 assert(ret);
298 assert(name);
299
300 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
301 return -EAFNOSUPPORT;
302
23b298bc
LP
303 if (convert_idna) {
304 r = dns_name_apply_idna(name, &buf);
305 if (r < 0)
306 return r;
307
308 name = buf;
309 }
310
45ec7efb
LP
311 q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
312 if (!q)
313 return -ENOMEM;
314
315 if (family != AF_INET6) {
316 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
317
318 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name);
319 if (!key)
320 return -ENOMEM;
321
322 r = dns_question_add(q, key);
323 if (r < 0)
324 return r;
325 }
326
327 if (family != AF_INET) {
328 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
329
330 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, 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 *ret = q;
340 q = NULL;
341
342 return 0;
343}
344
345int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
346 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
347 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
348 _cleanup_free_ char *reverse = NULL;
349 int r;
350
351 assert(ret);
352 assert(a);
353
354 if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
355 return -EAFNOSUPPORT;
356
357 r = dns_name_reverse(family, a, &reverse);
358 if (r < 0)
359 return r;
360
361 q = dns_question_new(1);
362 if (!q)
363 return -ENOMEM;
364
365 key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
366 if (!key)
367 return -ENOMEM;
368
369 reverse = NULL;
370
371 r = dns_question_add(q, key);
372 if (r < 0)
373 return r;
374
375 *ret = q;
376 q = NULL;
377
378 return 0;
379}
380
23b298bc
LP
381int dns_question_new_service(
382 DnsQuestion **ret,
383 const char *service,
384 const char *type,
385 const char *domain,
386 bool with_txt,
387 bool convert_idna) {
388
45ec7efb
LP
389 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
390 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
23b298bc
LP
391 _cleanup_free_ char *buf = NULL, *joined = NULL;
392 const char *name;
45ec7efb
LP
393 int r;
394
395 assert(ret);
23b298bc
LP
396
397 /* We support three modes of invocation:
398 *
399 * 1. Only a domain is specified, in which case we assume a properly encoded SRV RR name, including service
400 * type and possibly a service name. If specified in this way we assume it's already IDNA converted if
401 * that's necessary.
402 *
403 * 2. Both service type and a domain specified, in which case a normal SRV RR is assumed, without a DNS-SD
404 * style prefix. In this case we'll IDNA convert the domain, if that's requested.
405 *
406 * 3. All three of service name, type and domain are specified, in which case a DNS-SD service is put
407 * together. The service name is never IDNA converted, and the domain is if requested.
408 *
409 * It's not supported to specify a service name without a type, or no domain name.
410 */
411
412 if (!domain)
413 return -EINVAL;
414
415 if (type) {
416 if (convert_idna) {
417 r = dns_name_apply_idna(domain, &buf);
418 if (r < 0)
419 return r;
420
421 domain = buf;
422 }
423
424 r = dns_service_join(service, type, domain, &joined);
425 if (r < 0)
426 return r;
427
428 name = joined;
429 } else {
430 if (service)
431 return -EINVAL;
432
433 name = domain;
434 }
45ec7efb
LP
435
436 q = dns_question_new(1 + with_txt);
437 if (!q)
438 return -ENOMEM;
439
440 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
441 if (!key)
442 return -ENOMEM;
443
444 r = dns_question_add(q, key);
445 if (r < 0)
446 return r;
447
448 if (with_txt) {
449 dns_resource_key_unref(key);
450 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
451 if (!key)
452 return -ENOMEM;
453
454 r = dns_question_add(q, key);
455 if (r < 0)
456 return r;
457 }
458
459 *ret = q;
460 q = NULL;
461
462 return 0;
463}