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