]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
Merge pull request #2409 from snakeroot/dropin-doc-2
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.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 "resolved-dns-answer.h"
25 #include "resolved-dns-dnssec.h"
26 #include "string-util.h"
27
28 DnsAnswer *dns_answer_new(unsigned n) {
29 DnsAnswer *a;
30
31 a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
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
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
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) {
69 dns_answer_flush(a);
70 free(a);
71 } else
72 a->n_ref--;
73
74 return NULL;
75 }
76
77 static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
78 assert(rr);
79
80 if (!a)
81 return -ENOSPC;
82
83 if (a->n_rrs >= a->n_allocated)
84 return -ENOSPC;
85
86 a->items[a->n_rrs++] = (DnsAnswerItem) {
87 .rr = dns_resource_record_ref(rr),
88 .ifindex = ifindex,
89 .flags = flags,
90 };
91
92 return 1;
93 }
94
95 static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
96 DnsResourceRecord *rr;
97 DnsAnswerFlags flags;
98 int ifindex, r;
99
100 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
101 r = dns_answer_add_raw(a, rr, ifindex, flags);
102 if (r < 0)
103 return r;
104 }
105
106 return 0;
107 }
108
109 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
110 unsigned i;
111 int r;
112
113 assert(rr);
114
115 if (!a)
116 return -ENOSPC;
117 if (a->n_ref > 1)
118 return -EBUSY;
119
120 for (i = 0; i < a->n_rrs; i++) {
121 if (a->items[i].ifindex != ifindex)
122 continue;
123
124 r = dns_resource_record_equal(a->items[i].rr, rr);
125 if (r < 0)
126 return r;
127 if (r > 0) {
128 /* Don't mix contradicting TTLs (see below) */
129 if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
130 return -EINVAL;
131
132 /* Entry already exists, keep the entry with
133 * the higher RR. */
134 if (rr->ttl > a->items[i].rr->ttl) {
135 dns_resource_record_ref(rr);
136 dns_resource_record_unref(a->items[i].rr);
137 a->items[i].rr = rr;
138 }
139
140 a->items[i].flags |= flags;
141 return 0;
142 }
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 }
158 }
159
160 return dns_answer_add_raw(a, rr, ifindex, flags);
161 }
162
163 static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
164 DnsResourceRecord *rr;
165 DnsAnswerFlags flags;
166 int ifindex, r;
167
168 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
169 r = dns_answer_add(a, rr, ifindex, flags);
170 if (r < 0)
171 return r;
172 }
173
174 return 0;
175 }
176
177 int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
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
187 return dns_answer_add(*a, rr, ifindex, flags);
188 }
189
190 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl) {
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
197 soa->ttl = ttl;
198
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;
211 soa->soa.minimum = ttl;
212
213 return dns_answer_add(a, soa, 0, DNS_ANSWER_AUTHENTICATED);
214 }
215
216 int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
217 DnsAnswerFlags flags = 0, i_flags;
218 DnsResourceRecord *i;
219 bool found = false;
220 int r;
221
222 assert(key);
223
224 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
225 r = dns_resource_key_match_rr(key, i, NULL);
226 if (r < 0)
227 return r;
228 if (r == 0)
229 continue;
230
231 if (!ret_flags)
232 return 1;
233
234 if (found)
235 flags &= i_flags;
236 else {
237 flags = i_flags;
238 found = true;
239 }
240 }
241
242 if (ret_flags)
243 *ret_flags = flags;
244
245 return found;
246 }
247
248 int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
249 DnsAnswerFlags flags = 0, i_flags;
250 DnsResourceRecord *i;
251 bool found = false;
252 int r;
253
254 assert(rr);
255
256 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
257 r = dns_resource_record_equal(i, rr);
258 if (r < 0)
259 return r;
260 if (r == 0)
261 continue;
262
263 if (!ret_flags)
264 return 1;
265
266 if (found)
267 flags &= i_flags;
268 else {
269 flags = i_flags;
270 found = true;
271 }
272 }
273
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;
321 }
322
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
350 int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
351 DnsResourceRecord *rr, *soa = NULL;
352 DnsAnswerFlags rr_flags, soa_flags = 0;
353 int r;
354
355 assert(key);
356
357 /* For a SOA record we can never find a matching SOA record */
358 if (key->type == DNS_TYPE_SOA)
359 return 0;
360
361 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
362 r = dns_resource_key_match_soa(key, rr->key);
363 if (r < 0)
364 return r;
365 if (r > 0) {
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;
377 }
378 }
379
380 if (!soa)
381 return 0;
382
383 if (ret)
384 *ret = soa;
385 if (flags)
386 *flags = soa_flags;
387
388 return 1;
389 }
390
391 int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
392 DnsResourceRecord *rr;
393 DnsAnswerFlags rr_flags;
394 int r;
395
396 assert(key);
397
398 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
399 if (!dns_type_may_redirect(key->type))
400 return 0;
401
402 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
403 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
404 if (r < 0)
405 return r;
406 if (r > 0) {
407 if (ret)
408 *ret = rr;
409 if (flags)
410 *flags = rr_flags;
411 return 1;
412 }
413 }
414
415 return 0;
416 }
417
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;
471 unsigned i;
472 int r;
473
474 assert(a);
475 assert(key);
476
477 /* Remove all entries matching the specified key from *a */
478
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;
498 }
499
500 if ((*a)->n_ref > 1) {
501 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
502 DnsAnswerFlags flags;
503 int ifindex;
504
505 copy = dns_answer_new((*a)->n_rrs);
506 if (!copy)
507 return -ENOMEM;
508
509 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
510 r = dns_resource_key_equal(rr->key, key);
511 if (r < 0)
512 return r;
513 if (r > 0)
514 continue;
515
516 r = dns_answer_add_raw(copy, rr, ifindex, flags);
517 if (r < 0)
518 return r;
519 }
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++;
549 }
550
551 return 1;
552 }
553
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
640 int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
641 DnsResourceRecord *rr_source;
642 int ifindex_source, r;
643 DnsAnswerFlags flags_source;
644
645 assert(a);
646 assert(key);
647
648 /* Copy all RRs matching the specified key from source into *a */
649
650 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
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;
662
663 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
664 if (r < 0)
665 return r;
666 }
667
668 return 0;
669 }
670
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
685 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
686 DnsAnswerItem *items;
687 unsigned i, start, end;
688
689 if (!a)
690 return;
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
701 items = newa(DnsAnswerItem, a->n_rrs);
702 for (i = 0; i < a->n_rrs; i++) {
703
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)))
707 /* Order address records that are are not preferred to the end of the array */
708 items[end--] = a->items[i];
709 else
710 /* Order all other records to the beginning of the array */
711 items[start++] = a->items[i];
712 }
713
714 assert(start == end+1);
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
721 assert(a);
722
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
737 /* Allocate more than we need */
738 ns *= 2;
739
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;
753 }
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 }
787
788 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
789 DnsResourceRecord *rr;
790 DnsAnswerFlags flags;
791 int ifindex;
792
793 if (!f)
794 f = stdout;
795
796 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
797 const char *t;
798
799 fputc('\t', f);
800
801 t = dns_resource_record_to_string(rr);
802 if (!t) {
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 }
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 }