Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <stdio.h>
4 : :
5 : : #include "alloc-util.h"
6 : : #include "dns-domain.h"
7 : : #include "resolved-dns-answer.h"
8 : : #include "resolved-dns-dnssec.h"
9 : : #include "string-util.h"
10 : :
11 : 16 : DnsAnswer *dns_answer_new(size_t n) {
12 : : DnsAnswer *a;
13 : :
14 : 16 : a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
15 [ - + ]: 16 : if (!a)
16 : 0 : return NULL;
17 : :
18 : 16 : a->n_ref = 1;
19 : 16 : a->n_allocated = n;
20 : :
21 : 16 : return a;
22 : : }
23 : :
24 : 16 : static void dns_answer_flush(DnsAnswer *a) {
25 : : DnsResourceRecord *rr;
26 : :
27 [ - + ]: 16 : if (!a)
28 : 0 : return;
29 : :
30 [ + - + - : 32 : DNS_ANSWER_FOREACH(rr, a)
- + + - +
+ ]
31 : 16 : dns_resource_record_unref(rr);
32 : :
33 : 16 : a->n_rrs = 0;
34 : : }
35 : :
36 : 16 : static DnsAnswer *dns_answer_free(DnsAnswer *a) {
37 [ - + ]: 16 : assert(a);
38 : :
39 : 16 : dns_answer_flush(a);
40 : 16 : return mfree(a);
41 : : }
42 : :
43 [ + + - + : 5048 : DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
- + ]
44 : :
45 : 16 : static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
46 [ - + ]: 16 : assert(rr);
47 : :
48 [ - + ]: 16 : if (!a)
49 : 0 : return -ENOSPC;
50 : :
51 [ - + ]: 16 : if (a->n_rrs >= a->n_allocated)
52 : 0 : return -ENOSPC;
53 : :
54 : 32 : a->items[a->n_rrs++] = (DnsAnswerItem) {
55 : 16 : .rr = dns_resource_record_ref(rr),
56 : : .ifindex = ifindex,
57 : : .flags = flags,
58 : : };
59 : :
60 : 16 : return 1;
61 : : }
62 : :
63 : 0 : static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
64 : : DnsResourceRecord *rr;
65 : : DnsAnswerFlags flags;
66 : : int ifindex, r;
67 : :
68 [ # # # # : 0 : DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
# # # # #
# # # # #
# # # # #
# # # ]
69 : 0 : r = dns_answer_add_raw(a, rr, ifindex, flags);
70 [ # # ]: 0 : if (r < 0)
71 : 0 : return r;
72 : : }
73 : :
74 : 0 : return 0;
75 : : }
76 : :
77 : 16 : int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
78 : : size_t i;
79 : : int r;
80 : :
81 [ - + ]: 16 : assert(rr);
82 : :
83 [ - + ]: 16 : if (!a)
84 : 0 : return -ENOSPC;
85 [ - + ]: 16 : if (a->n_ref > 1)
86 : 0 : return -EBUSY;
87 : :
88 [ - + ]: 16 : for (i = 0; i < a->n_rrs; i++) {
89 [ # # ]: 0 : if (a->items[i].ifindex != ifindex)
90 : 0 : continue;
91 : :
92 : 0 : r = dns_resource_key_equal(a->items[i].rr->key, rr->key);
93 [ # # ]: 0 : if (r < 0)
94 : 0 : return r;
95 [ # # ]: 0 : if (r == 0)
96 : 0 : continue;
97 : :
98 : : /* There's already an RR of the same RRset in place! Let's see if the TTLs more or less
99 : : * match. We don't really care if they match precisely, but we do care whether one is 0 and
100 : : * the other is not. See RFC 2181, Section 5.2. */
101 [ # # ]: 0 : if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
102 : 0 : return -EINVAL;
103 : :
104 : 0 : r = dns_resource_record_payload_equal(a->items[i].rr, rr);
105 [ # # ]: 0 : if (r < 0)
106 : 0 : return r;
107 [ # # ]: 0 : if (r == 0)
108 : 0 : continue;
109 : :
110 : : /* Entry already exists, keep the entry with the higher RR. */
111 [ # # ]: 0 : if (rr->ttl > a->items[i].rr->ttl) {
112 : 0 : dns_resource_record_ref(rr);
113 : 0 : dns_resource_record_unref(a->items[i].rr);
114 : 0 : a->items[i].rr = rr;
115 : : }
116 : :
117 : 0 : a->items[i].flags |= flags;
118 : 0 : return 0;
119 : : }
120 : :
121 : 16 : return dns_answer_add_raw(a, rr, ifindex, flags);
122 : : }
123 : :
124 : 0 : static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
125 : : DnsResourceRecord *rr;
126 : : DnsAnswerFlags flags;
127 : : int ifindex, r;
128 : :
129 [ # # # # : 0 : DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
# # # # #
# # # # #
# # # # #
# # # ]
130 : 0 : r = dns_answer_add(a, rr, ifindex, flags);
131 [ # # ]: 0 : if (r < 0)
132 : 0 : return r;
133 : : }
134 : :
135 : 0 : return 0;
136 : : }
137 : :
138 : 0 : int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
139 : : int r;
140 : :
141 [ # # ]: 0 : assert(a);
142 [ # # ]: 0 : assert(rr);
143 : :
144 : 0 : r = dns_answer_reserve_or_clone(a, 1);
145 [ # # ]: 0 : if (r < 0)
146 : 0 : return r;
147 : :
148 : 0 : return dns_answer_add(*a, rr, ifindex, flags);
149 : : }
150 : :
151 : 0 : int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
152 : 0 : _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
153 : :
154 : 0 : soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
155 [ # # ]: 0 : if (!soa)
156 : 0 : return -ENOMEM;
157 : :
158 : 0 : soa->ttl = ttl;
159 : :
160 : 0 : soa->soa.mname = strdup(name);
161 [ # # ]: 0 : if (!soa->soa.mname)
162 : 0 : return -ENOMEM;
163 : :
164 : 0 : soa->soa.rname = strjoin("root.", name);
165 [ # # ]: 0 : if (!soa->soa.rname)
166 : 0 : return -ENOMEM;
167 : :
168 : 0 : soa->soa.serial = 1;
169 : 0 : soa->soa.refresh = 1;
170 : 0 : soa->soa.retry = 1;
171 : 0 : soa->soa.expire = 1;
172 : 0 : soa->soa.minimum = ttl;
173 : :
174 : 0 : return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED);
175 : : }
176 : :
177 : 0 : int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
178 : 0 : DnsAnswerFlags flags = 0, i_flags;
179 : : DnsResourceRecord *i;
180 : 0 : bool found = false;
181 : : int r;
182 : :
183 [ # # ]: 0 : assert(key);
184 : :
185 [ # # # # : 0 : DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
# # # # #
# # # # #
# # ]
186 : 0 : r = dns_resource_key_match_rr(key, i, NULL);
187 [ # # ]: 0 : if (r < 0)
188 : 0 : return r;
189 [ # # ]: 0 : if (r == 0)
190 : 0 : continue;
191 : :
192 [ # # ]: 0 : if (!ret_flags)
193 : 0 : return 1;
194 : :
195 [ # # ]: 0 : if (found)
196 : 0 : flags &= i_flags;
197 : : else {
198 : 0 : flags = i_flags;
199 : 0 : found = true;
200 : : }
201 : : }
202 : :
203 [ # # ]: 0 : if (ret_flags)
204 : 0 : *ret_flags = flags;
205 : :
206 : 0 : return found;
207 : : }
208 : :
209 : 0 : int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
210 : : DnsResourceRecord *i;
211 : :
212 [ # # # # : 0 : DNS_ANSWER_FOREACH(i, a) {
# # # # #
# ]
213 [ # # # # ]: 0 : if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
214 : 0 : return true;
215 : : }
216 : :
217 : 0 : return false;
218 : : }
219 : :
220 : 0 : int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
221 : : DnsResourceRecord *rr;
222 : : int r;
223 : :
224 : : /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
225 : :
226 [ # # # # : 0 : DNS_ANSWER_FOREACH(rr, answer) {
# # # # #
# ]
227 : : const char *p;
228 : :
229 [ # # ]: 0 : if (rr->key->type != DNS_TYPE_NSEC3)
230 : 0 : continue;
231 : :
232 : 0 : p = dns_resource_key_name(rr->key);
233 : 0 : r = dns_name_parent(&p);
234 [ # # ]: 0 : if (r < 0)
235 : 0 : return r;
236 [ # # ]: 0 : if (r == 0)
237 : 0 : continue;
238 : :
239 : 0 : r = dns_name_equal(p, zone);
240 [ # # ]: 0 : if (r != 0)
241 : 0 : return r;
242 : : }
243 : :
244 : 0 : return false;
245 : : }
246 : :
247 : 0 : int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
248 : 0 : DnsResourceRecord *rr, *soa = NULL;
249 : 0 : DnsAnswerFlags rr_flags, soa_flags = 0;
250 : : int r;
251 : :
252 [ # # ]: 0 : assert(key);
253 : :
254 : : /* For a SOA record we can never find a matching SOA record */
255 [ # # ]: 0 : if (key->type == DNS_TYPE_SOA)
256 : 0 : return 0;
257 : :
258 [ # # # # : 0 : DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
# # # # #
# # # # #
# # ]
259 : 0 : r = dns_resource_key_match_soa(key, rr->key);
260 [ # # ]: 0 : if (r < 0)
261 : 0 : return r;
262 [ # # ]: 0 : if (r > 0) {
263 : :
264 [ # # ]: 0 : if (soa) {
265 : 0 : r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
266 [ # # ]: 0 : if (r < 0)
267 : 0 : return r;
268 [ # # ]: 0 : if (r > 0)
269 : 0 : continue;
270 : : }
271 : :
272 : 0 : soa = rr;
273 : 0 : soa_flags = rr_flags;
274 : : }
275 : : }
276 : :
277 [ # # ]: 0 : if (!soa)
278 : 0 : return 0;
279 : :
280 [ # # ]: 0 : if (ret)
281 : 0 : *ret = soa;
282 [ # # ]: 0 : if (flags)
283 : 0 : *flags = soa_flags;
284 : :
285 : 0 : return 1;
286 : : }
287 : :
288 : 0 : int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
289 : : DnsResourceRecord *rr;
290 : : DnsAnswerFlags rr_flags;
291 : : int r;
292 : :
293 [ # # ]: 0 : assert(key);
294 : :
295 : : /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
296 [ # # ]: 0 : if (!dns_type_may_redirect(key->type))
297 : 0 : return 0;
298 : :
299 [ # # # # : 0 : DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
# # # # #
# # # # #
# # ]
300 : 0 : r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
301 [ # # ]: 0 : if (r < 0)
302 : 0 : return r;
303 [ # # ]: 0 : if (r > 0) {
304 [ # # ]: 0 : if (ret)
305 : 0 : *ret = rr;
306 [ # # ]: 0 : if (flags)
307 : 0 : *flags = rr_flags;
308 : 0 : return 1;
309 : : }
310 : : }
311 : :
312 : 0 : return 0;
313 : : }
314 : :
315 : 0 : int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
316 : 0 : _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
317 : : int r;
318 : :
319 [ # # ]: 0 : assert(ret);
320 : :
321 [ # # ]: 0 : if (dns_answer_size(a) <= 0) {
322 : 0 : *ret = dns_answer_ref(b);
323 : 0 : return 0;
324 : : }
325 : :
326 [ # # ]: 0 : if (dns_answer_size(b) <= 0) {
327 : 0 : *ret = dns_answer_ref(a);
328 : 0 : return 0;
329 : : }
330 : :
331 : 0 : k = dns_answer_new(a->n_rrs + b->n_rrs);
332 [ # # ]: 0 : if (!k)
333 : 0 : return -ENOMEM;
334 : :
335 : 0 : r = dns_answer_add_raw_all(k, a);
336 [ # # ]: 0 : if (r < 0)
337 : 0 : return r;
338 : :
339 : 0 : r = dns_answer_add_all(k, b);
340 [ # # ]: 0 : if (r < 0)
341 : 0 : return r;
342 : :
343 : 0 : *ret = TAKE_PTR(k);
344 : :
345 : 0 : return 0;
346 : : }
347 : :
348 : 0 : int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
349 : : DnsAnswer *merged;
350 : : int r;
351 : :
352 [ # # ]: 0 : assert(a);
353 : :
354 : 0 : r = dns_answer_merge(*a, b, &merged);
355 [ # # ]: 0 : if (r < 0)
356 : 0 : return r;
357 : :
358 : 0 : dns_answer_unref(*a);
359 : 0 : *a = merged;
360 : :
361 : 0 : return 0;
362 : : }
363 : :
364 : 0 : int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
365 : 0 : bool found = false, other = false;
366 : : DnsResourceRecord *rr;
367 : : size_t i;
368 : : int r;
369 : :
370 [ # # ]: 0 : assert(a);
371 [ # # ]: 0 : assert(key);
372 : :
373 : : /* Remove all entries matching the specified key from *a */
374 : :
375 [ # # # # : 0 : DNS_ANSWER_FOREACH(rr, *a) {
# # # # #
# ]
376 : 0 : r = dns_resource_key_equal(rr->key, key);
377 [ # # ]: 0 : if (r < 0)
378 : 0 : return r;
379 [ # # ]: 0 : if (r > 0)
380 : 0 : found = true;
381 : : else
382 : 0 : other = true;
383 : :
384 [ # # # # ]: 0 : if (found && other)
385 : 0 : break;
386 : : }
387 : :
388 [ # # ]: 0 : if (!found)
389 : 0 : return 0;
390 : :
391 [ # # ]: 0 : if (!other) {
392 : 0 : *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
393 : 0 : return 1;
394 : : }
395 : :
396 [ # # ]: 0 : if ((*a)->n_ref > 1) {
397 : 0 : _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
398 : : DnsAnswerFlags flags;
399 : : int ifindex;
400 : :
401 : 0 : copy = dns_answer_new((*a)->n_rrs);
402 [ # # ]: 0 : if (!copy)
403 : 0 : return -ENOMEM;
404 : :
405 [ # # # # : 0 : DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
# # # # #
# # # # #
# # # # #
# # # ]
406 : 0 : r = dns_resource_key_equal(rr->key, key);
407 [ # # ]: 0 : if (r < 0)
408 : 0 : return r;
409 [ # # ]: 0 : if (r > 0)
410 : 0 : continue;
411 : :
412 : 0 : r = dns_answer_add_raw(copy, rr, ifindex, flags);
413 [ # # ]: 0 : if (r < 0)
414 : 0 : return r;
415 : : }
416 : :
417 : 0 : dns_answer_unref(*a);
418 : 0 : *a = TAKE_PTR(copy);
419 : :
420 : 0 : return 1;
421 : : }
422 : :
423 : : /* Only a single reference, edit in-place */
424 : :
425 : 0 : i = 0;
426 : : for (;;) {
427 [ # # ]: 0 : if (i >= (*a)->n_rrs)
428 : 0 : break;
429 : :
430 : 0 : r = dns_resource_key_equal((*a)->items[i].rr->key, key);
431 [ # # ]: 0 : if (r < 0)
432 : 0 : return r;
433 [ # # ]: 0 : if (r > 0) {
434 : : /* Kill this entry */
435 : :
436 : 0 : dns_resource_record_unref((*a)->items[i].rr);
437 : 0 : memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
438 : 0 : (*a)->n_rrs--;
439 : 0 : continue;
440 : :
441 : : } else
442 : : /* Keep this entry */
443 : 0 : i++;
444 : : }
445 : :
446 : 0 : return 1;
447 : : }
448 : :
449 : 0 : int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
450 : 0 : bool found = false, other = false;
451 : : DnsResourceRecord *rr;
452 : : size_t i;
453 : : int r;
454 : :
455 [ # # ]: 0 : assert(a);
456 [ # # ]: 0 : assert(rm);
457 : :
458 : : /* Remove all entries matching the specified RR from *a */
459 : :
460 [ # # # # : 0 : DNS_ANSWER_FOREACH(rr, *a) {
# # # # #
# ]
461 : 0 : r = dns_resource_record_equal(rr, rm);
462 [ # # ]: 0 : if (r < 0)
463 : 0 : return r;
464 [ # # ]: 0 : if (r > 0)
465 : 0 : found = true;
466 : : else
467 : 0 : other = true;
468 : :
469 [ # # # # ]: 0 : if (found && other)
470 : 0 : break;
471 : : }
472 : :
473 [ # # ]: 0 : if (!found)
474 : 0 : return 0;
475 : :
476 [ # # ]: 0 : if (!other) {
477 : 0 : *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
478 : 0 : return 1;
479 : : }
480 : :
481 [ # # ]: 0 : if ((*a)->n_ref > 1) {
482 : 0 : _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
483 : : DnsAnswerFlags flags;
484 : : int ifindex;
485 : :
486 : 0 : copy = dns_answer_new((*a)->n_rrs);
487 [ # # ]: 0 : if (!copy)
488 : 0 : return -ENOMEM;
489 : :
490 [ # # # # : 0 : DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
# # # # #
# # # # #
# # # # #
# # # ]
491 : 0 : r = dns_resource_record_equal(rr, rm);
492 [ # # ]: 0 : if (r < 0)
493 : 0 : return r;
494 [ # # ]: 0 : if (r > 0)
495 : 0 : continue;
496 : :
497 : 0 : r = dns_answer_add_raw(copy, rr, ifindex, flags);
498 [ # # ]: 0 : if (r < 0)
499 : 0 : return r;
500 : : }
501 : :
502 : 0 : dns_answer_unref(*a);
503 : 0 : *a = TAKE_PTR(copy);
504 : :
505 : 0 : return 1;
506 : : }
507 : :
508 : : /* Only a single reference, edit in-place */
509 : :
510 : 0 : i = 0;
511 : : for (;;) {
512 [ # # ]: 0 : if (i >= (*a)->n_rrs)
513 : 0 : break;
514 : :
515 : 0 : r = dns_resource_record_equal((*a)->items[i].rr, rm);
516 [ # # ]: 0 : if (r < 0)
517 : 0 : return r;
518 [ # # ]: 0 : if (r > 0) {
519 : : /* Kill this entry */
520 : :
521 : 0 : dns_resource_record_unref((*a)->items[i].rr);
522 : 0 : memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
523 : 0 : (*a)->n_rrs--;
524 : 0 : continue;
525 : :
526 : : } else
527 : : /* Keep this entry */
528 : 0 : i++;
529 : : }
530 : :
531 : 0 : return 1;
532 : : }
533 : :
534 : 0 : int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
535 : : DnsResourceRecord *rr_source;
536 : : int ifindex_source, r;
537 : : DnsAnswerFlags flags_source;
538 : :
539 [ # # ]: 0 : assert(a);
540 [ # # ]: 0 : assert(key);
541 : :
542 : : /* Copy all RRs matching the specified key from source into *a */
543 : :
544 [ # # # # : 0 : DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
# # # # #
# # # # #
# # # # #
# # # ]
545 : :
546 : 0 : r = dns_resource_key_equal(rr_source->key, key);
547 [ # # ]: 0 : if (r < 0)
548 : 0 : return r;
549 [ # # ]: 0 : if (r == 0)
550 : 0 : continue;
551 : :
552 : : /* Make space for at least one entry */
553 : 0 : r = dns_answer_reserve_or_clone(a, 1);
554 [ # # ]: 0 : if (r < 0)
555 : 0 : return r;
556 : :
557 : 0 : r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
558 [ # # ]: 0 : if (r < 0)
559 : 0 : return r;
560 : : }
561 : :
562 : 0 : return 0;
563 : : }
564 : :
565 : 0 : int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
566 : : int r;
567 : :
568 [ # # ]: 0 : assert(to);
569 [ # # ]: 0 : assert(from);
570 [ # # ]: 0 : assert(key);
571 : :
572 : 0 : r = dns_answer_copy_by_key(to, *from, key, or_flags);
573 [ # # ]: 0 : if (r < 0)
574 : 0 : return r;
575 : :
576 : 0 : return dns_answer_remove_by_key(from, key);
577 : : }
578 : :
579 : 0 : void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
580 : : DnsAnswerItem *items;
581 : : size_t i, start, end;
582 : :
583 [ # # ]: 0 : if (!a)
584 : 0 : return;
585 : :
586 [ # # ]: 0 : if (a->n_rrs <= 1)
587 : 0 : return;
588 : :
589 : 0 : start = 0;
590 : 0 : end = a->n_rrs-1;
591 : :
592 : : /* RFC 4795, Section 2.6 suggests we should order entries
593 : : * depending on whether the sender is a link-local address. */
594 : :
595 [ # # # # ]: 0 : items = newa(DnsAnswerItem, a->n_rrs);
596 [ # # ]: 0 : for (i = 0; i < a->n_rrs; i++) {
597 : :
598 [ # # ]: 0 : if (a->items[i].rr->key->class == DNS_CLASS_IN &&
599 [ # # # # ]: 0 : ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
600 [ # # # # ]: 0 : (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
601 : : /* Order address records that are not preferred to the end of the array */
602 : 0 : items[end--] = a->items[i];
603 : : else
604 : : /* Order all other records to the beginning of the array */
605 : 0 : items[start++] = a->items[i];
606 : : }
607 : :
608 [ # # ]: 0 : assert(start == end+1);
609 : 0 : memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
610 : : }
611 : :
612 : 0 : int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
613 : : DnsAnswer *n;
614 : :
615 [ # # ]: 0 : assert(a);
616 : :
617 [ # # ]: 0 : if (n_free <= 0)
618 : 0 : return 0;
619 : :
620 [ # # ]: 0 : if (*a) {
621 : : size_t ns;
622 : :
623 [ # # ]: 0 : if ((*a)->n_ref > 1)
624 : 0 : return -EBUSY;
625 : :
626 : 0 : ns = (*a)->n_rrs + n_free;
627 : :
628 [ # # ]: 0 : if ((*a)->n_allocated >= ns)
629 : 0 : return 0;
630 : :
631 : : /* Allocate more than we need */
632 : 0 : ns *= 2;
633 : :
634 : 0 : n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
635 [ # # ]: 0 : if (!n)
636 : 0 : return -ENOMEM;
637 : :
638 : 0 : n->n_allocated = ns;
639 : : } else {
640 : 0 : n = dns_answer_new(n_free);
641 [ # # ]: 0 : if (!n)
642 : 0 : return -ENOMEM;
643 : : }
644 : :
645 : 0 : *a = n;
646 : 0 : return 0;
647 : : }
648 : :
649 : 0 : int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
650 : 0 : _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
651 : : int r;
652 : :
653 [ # # ]: 0 : assert(a);
654 : :
655 : : /* Tries to extend the DnsAnswer object. And if that's not
656 : : * possible, since we are not the sole owner, then allocate a
657 : : * new, appropriately sized one. Either way, after this call
658 : : * the object will only have a single reference, and has room
659 : : * for at least the specified number of RRs. */
660 : :
661 : 0 : r = dns_answer_reserve(a, n_free);
662 [ # # ]: 0 : if (r != -EBUSY)
663 : 0 : return r;
664 : :
665 [ # # ]: 0 : assert(*a);
666 : :
667 : 0 : n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
668 [ # # ]: 0 : if (!n)
669 : 0 : return -ENOMEM;
670 : :
671 : 0 : r = dns_answer_add_raw_all(n, *a);
672 [ # # ]: 0 : if (r < 0)
673 : 0 : return r;
674 : :
675 : 0 : dns_answer_unref(*a);
676 : 0 : *a = TAKE_PTR(n);
677 : :
678 : 0 : return 0;
679 : : }
680 : :
681 : 0 : void dns_answer_dump(DnsAnswer *answer, FILE *f) {
682 : : DnsResourceRecord *rr;
683 : : DnsAnswerFlags flags;
684 : : int ifindex;
685 : :
686 [ # # ]: 0 : if (!f)
687 : 0 : f = stdout;
688 : :
689 [ # # # # : 0 : DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
# # # # #
# # # # #
# # # # #
# # # ]
690 : : const char *t;
691 : :
692 : 0 : fputc('\t', f);
693 : :
694 : 0 : t = dns_resource_record_to_string(rr);
695 [ # # ]: 0 : if (!t) {
696 : 0 : log_oom();
697 : 0 : continue;
698 : : }
699 : :
700 : 0 : fputs(t, f);
701 : :
702 [ # # # # ]: 0 : if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
703 : 0 : fputs("\t;", f);
704 : :
705 [ # # ]: 0 : if (ifindex != 0)
706 : 0 : printf(" ifindex=%i", ifindex);
707 [ # # ]: 0 : if (flags & DNS_ANSWER_AUTHENTICATED)
708 : 0 : fputs(" authenticated", f);
709 [ # # ]: 0 : if (flags & DNS_ANSWER_CACHEABLE)
710 : 0 : fputs(" cachable", f);
711 [ # # ]: 0 : if (flags & DNS_ANSWER_SHARED_OWNER)
712 : 0 : fputs(" shared-owner", f);
713 : :
714 : 0 : fputc('\n', f);
715 : : }
716 : 0 : }
717 : :
718 : 0 : int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
719 : : DnsResourceRecord *rr;
720 : : int r;
721 : :
722 [ # # ]: 0 : assert(cname);
723 : :
724 : : /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
725 : : * synthesized from it */
726 : :
727 [ # # ]: 0 : if (cname->key->type != DNS_TYPE_CNAME)
728 : 0 : return 0;
729 : :
730 [ # # # # : 0 : DNS_ANSWER_FOREACH(rr, a) {
# # # # #
# ]
731 [ # # # ]: 0 : _cleanup_free_ char *n = NULL;
732 : :
733 [ # # ]: 0 : if (rr->key->type != DNS_TYPE_DNAME)
734 : 0 : continue;
735 [ # # ]: 0 : if (rr->key->class != cname->key->class)
736 : 0 : continue;
737 : :
738 : 0 : r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n);
739 [ # # ]: 0 : if (r < 0)
740 : 0 : return r;
741 [ # # ]: 0 : if (r == 0)
742 : 0 : continue;
743 : :
744 : 0 : r = dns_name_equal(n, dns_resource_key_name(cname->key));
745 [ # # ]: 0 : if (r < 0)
746 : 0 : return r;
747 [ # # ]: 0 : if (r > 0)
748 : 0 : return 1;
749 : : }
750 : :
751 : 0 : return 0;
752 : : }
|