]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-question.c
Merge pull request #2306 from walyong/exec_v01
[thirdparty/systemd.git] / src / resolve / resolved-dns-question.c
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
22 #include "alloc-util.h"
23 #include "dns-domain.h"
24 #include "dns-type.h"
25 #include "resolved-dns-question.h"
26
27 DnsQuestion *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
42 DnsQuestion *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
51 DnsQuestion *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
69 int dns_question_add(DnsQuestion *q, DnsResourceKey *key) {
70 unsigned i;
71 int r;
72
73 assert(key);
74
75 if (!q)
76 return -ENOSPC;
77
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
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
93 int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
94 unsigned i;
95 int r;
96
97 assert(rr);
98
99 if (!q)
100 return 0;
101
102 for (i = 0; i < q->n_keys; i++) {
103 r = dns_resource_key_match_rr(q->keys[i], rr, search_domain);
104 if (r != 0)
105 return r;
106 }
107
108 return 0;
109 }
110
111 int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) {
112 unsigned i;
113 int r;
114
115 assert(rr);
116
117 if (!q)
118 return 0;
119
120 if (!IN_SET(rr->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME))
121 return 0;
122
123 for (i = 0; i < q->n_keys; i++) {
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
128 r = dns_resource_key_match_cname_or_dname(q->keys[i], rr->key, search_domain);
129 if (r != 0)
130 return r;
131 }
132
133 return 0;
134 }
135
136 int dns_question_is_valid_for_query(DnsQuestion *q) {
137 const char *name;
138 unsigned i;
139 int r;
140
141 if (!q)
142 return 0;
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 */
155 for (i = 0; i < q->n_keys; i++) {
156 assert(q->keys[i]);
157
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;
166 }
167
168 return 1;
169 }
170
171 int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) {
172 unsigned j;
173 int r;
174
175 assert(k);
176
177 if (!a)
178 return 0;
179
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
189 int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) {
190 unsigned j;
191 int r;
192
193 if (a == b)
194 return 1;
195
196 if (!a)
197 return !b || b->n_keys == 0;
198 if (!b)
199 return a->n_keys == 0;
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
218 int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) {
219 _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL;
220 DnsResourceKey *key;
221 bool same = true;
222 int r;
223
224 assert(cname);
225 assert(ret);
226 assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
227
228 if (dns_question_size(q) <= 0) {
229 *ret = NULL;
230 return 0;
231 }
232
233 DNS_QUESTION_FOREACH(key, q) {
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 {
240 r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
241 if (r < 0)
242 return r;
243 if (r == 0)
244 continue;
245
246 d = destination;
247 }
248
249 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), d);
250 if (r < 0)
251 return r;
252
253 if (r == 0) {
254 same = false;
255 break;
256 }
257 }
258
259 /* Fully the same, indicate we didn't do a thing */
260 if (same) {
261 *ret = NULL;
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 */
270 DNS_QUESTION_FOREACH(key, q) {
271 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
272
273 k = dns_resource_key_new_redirect(key, cname);
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 }
287
288 const char *dns_question_first_name(DnsQuestion *q) {
289
290 if (!q)
291 return NULL;
292
293 if (q->n_keys < 1)
294 return NULL;
295
296 return DNS_RESOURCE_KEY_NAME(q->keys[0]);
297 }
298
299 int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) {
300 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
301 _cleanup_free_ char *buf = NULL;
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
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
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
352 int 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
388 int 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
396 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
397 _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
398 _cleanup_free_ char *buf = NULL, *joined = NULL;
399 const char *name;
400 int r;
401
402 assert(ret);
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 }
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 }