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