1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "alloc-util.h"
4 #include "dns-domain.h"
5 #include "list.h"
6 #include "resolved-dns-packet.h"
7 #include "resolved-dns-zone.h"
8 #include "resolved-dnssd.h"
9 #include "resolved-manager.h"
10 #include "string-util.h"
11 
12 /* Never allow more than 1K entries */
13 #define ZONE_MAX 1024
14 
dns_zone_item_probe_stop(DnsZoneItem * i)15 void dns_zone_item_probe_stop(DnsZoneItem *i) {
16         DnsTransaction *t;
17         assert(i);
18 
19         if (!i->probe_transaction)
20                 return;
21 
22         t = TAKE_PTR(i->probe_transaction);
23 
24         set_remove(t->notify_zone_items, i);
25         set_remove(t->notify_zone_items_done, i);
26         dns_transaction_gc(t);
27 }
28 
dns_zone_item_free(DnsZoneItem * i)29 static DnsZoneItem* dns_zone_item_free(DnsZoneItem *i) {
30         if (!i)
31                 return NULL;
32 
33         dns_zone_item_probe_stop(i);
34         dns_resource_record_unref(i->rr);
35 
36         return mfree(i);
37 }
38 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free);
39 
dns_zone_item_remove_and_free(DnsZone * z,DnsZoneItem * i)40 static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) {
41         DnsZoneItem *first;
42 
43         assert(z);
44 
45         if (!i)
46                 return;
47 
48         first = hashmap_get(z->by_key, i->rr->key);
49         LIST_REMOVE(by_key, first, i);
50         if (first)
51                 assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0);
52         else
53                 hashmap_remove(z->by_key, i->rr->key);
54 
55         first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key));
56         LIST_REMOVE(by_name, first, i);
57         if (first)
58                 assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0);
59         else
60                 hashmap_remove(z->by_name, dns_resource_key_name(i->rr->key));
61 
62         dns_zone_item_free(i);
63 }
64 
dns_zone_flush(DnsZone * z)65 void dns_zone_flush(DnsZone *z) {
66         DnsZoneItem *i;
67 
68         assert(z);
69 
70         while ((i = hashmap_first(z->by_key)))
71                 dns_zone_item_remove_and_free(z, i);
72 
73         assert(hashmap_size(z->by_key) == 0);
74         assert(hashmap_size(z->by_name) == 0);
75 
76         z->by_key = hashmap_free(z->by_key);
77         z->by_name = hashmap_free(z->by_name);
78 }
79 
dns_zone_get(DnsZone * z,DnsResourceRecord * rr)80 DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
81         assert(z);
82         assert(rr);
83 
84         LIST_FOREACH(by_key, i, (DnsZoneItem*) hashmap_get(z->by_key, rr->key))
85                 if (dns_resource_record_equal(i->rr, rr) > 0)
86                         return i;
87 
88         return NULL;
89 }
90 
dns_zone_remove_rr(DnsZone * z,DnsResourceRecord * rr)91 void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) {
92         DnsZoneItem *i;
93 
94         assert(z);
95 
96         if (!rr)
97                 return;
98 
99         i = dns_zone_get(z, rr);
100         if (i)
101                 dns_zone_item_remove_and_free(z, i);
102 }
103 
dns_zone_remove_rrs_by_key(DnsZone * z,DnsResourceKey * key)104 int dns_zone_remove_rrs_by_key(DnsZone *z, DnsResourceKey *key) {
105         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
106         DnsResourceRecord *rr;
107         bool tentative;
108         int r;
109 
110         r = dns_zone_lookup(z, key, 0, &answer, &soa, &tentative);
111         if (r < 0)
112                 return r;
113 
114         DNS_ANSWER_FOREACH(rr, answer)
115                 dns_zone_remove_rr(z, rr);
116 
117         return 0;
118 }
119 
dns_zone_init(DnsZone * z)120 static int dns_zone_init(DnsZone *z) {
121         int r;
122 
123         assert(z);
124 
125         r = hashmap_ensure_allocated(&z->by_key, &dns_resource_key_hash_ops);
126         if (r < 0)
127                 return r;
128 
129         r = hashmap_ensure_allocated(&z->by_name, &dns_name_hash_ops);
130         if (r < 0)
131                 return r;
132 
133         return 0;
134 }
135 
dns_zone_link_item(DnsZone * z,DnsZoneItem * i)136 static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) {
137         DnsZoneItem *first;
138         int r;
139 
140         first = hashmap_get(z->by_key, i->rr->key);
141         if (first) {
142                 LIST_PREPEND(by_key, first, i);
143                 assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0);
144         } else {
145                 r = hashmap_put(z->by_key, i->rr->key, i);
146                 if (r < 0)
147                         return r;
148         }
149 
150         first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key));
151         if (first) {
152                 LIST_PREPEND(by_name, first, i);
153                 assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0);
154         } else {
155                 r = hashmap_put(z->by_name, dns_resource_key_name(i->rr->key), i);
156                 if (r < 0)
157                         return r;
158         }
159 
160         return 0;
161 }
162 
dns_zone_item_probe_start(DnsZoneItem * i)163 static int dns_zone_item_probe_start(DnsZoneItem *i)  {
164         _cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL;
165         int r;
166 
167         assert(i);
168 
169         if (i->probe_transaction)
170                 return 0;
171 
172         t = dns_scope_find_transaction(
173                         i->scope,
174                         &DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key)),
175                         SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
176         if (!t) {
177                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
178 
179                 key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key));
180                 if (!key)
181                         return -ENOMEM;
182 
183                 r = dns_transaction_new(&t, i->scope, key, NULL, SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
184                 if (r < 0)
185                         return r;
186         }
187 
188         r = set_ensure_allocated(&t->notify_zone_items_done, NULL);
189         if (r < 0)
190                 return r;
191 
192         r = set_ensure_put(&t->notify_zone_items, NULL, i);
193         if (r < 0)
194                 return r;
195 
196         t->probing = true;
197         i->probe_transaction = TAKE_PTR(t);
198 
199         if (i->probe_transaction->state == DNS_TRANSACTION_NULL) {
200                 i->block_ready++;
201                 r = dns_transaction_go(i->probe_transaction);
202                 i->block_ready--;
203 
204                 if (r < 0) {
205                         dns_zone_item_probe_stop(i);
206                         return r;
207                 }
208         }
209 
210         dns_zone_item_notify(i);
211         return 0;
212 }
213 
dns_zone_put(DnsZone * z,DnsScope * s,DnsResourceRecord * rr,bool probe)214 int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
215         _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL;
216         DnsZoneItem *existing;
217         int r;
218 
219         assert(z);
220         assert(s);
221         assert(rr);
222 
223         if (dns_class_is_pseudo(rr->key->class))
224                 return -EINVAL;
225         if (dns_type_is_pseudo(rr->key->type))
226                 return -EINVAL;
227 
228         existing = dns_zone_get(z, rr);
229         if (existing)
230                 return 0;
231 
232         r = dns_zone_init(z);
233         if (r < 0)
234                 return r;
235 
236         i = new(DnsZoneItem, 1);
237         if (!i)
238                 return -ENOMEM;
239 
240         *i = (DnsZoneItem) {
241                 .scope = s,
242                 .rr = dns_resource_record_ref(rr),
243                 .probing_enabled = probe,
244         };
245 
246         r = dns_zone_link_item(z, i);
247         if (r < 0)
248                 return r;
249 
250         if (probe) {
251                 bool established = false;
252 
253                 /* Check if there's already an RR with the same name
254                  * established. If so, it has been probed already, and
255                  * we don't need to probe again. */
256 
257                 LIST_FOREACH_OTHERS(by_name, j, i)
258                         if (j->state == DNS_ZONE_ITEM_ESTABLISHED)
259                                 established = true;
260 
261                 if (established)
262                         i->state = DNS_ZONE_ITEM_ESTABLISHED;
263                 else {
264                         i->state = DNS_ZONE_ITEM_PROBING;
265 
266                         r = dns_zone_item_probe_start(i);
267                         if (r < 0) {
268                                 dns_zone_item_remove_and_free(z, i);
269                                 i = NULL;
270                                 return r;
271                         }
272                 }
273         } else
274                 i->state = DNS_ZONE_ITEM_ESTABLISHED;
275 
276         i = NULL;
277         return 0;
278 }
279 
dns_zone_add_authenticated_answer(DnsAnswer * a,DnsZoneItem * i,int ifindex)280 static int dns_zone_add_authenticated_answer(DnsAnswer *a, DnsZoneItem *i, int ifindex) {
281         DnsAnswerFlags flags;
282 
283         /* From RFC 6762, Section 10.2
284          * "They (the rules about when to set the cache-flush bit) apply to
285          * startup announcements as described in Section 8.3, "Announcing",
286          * and to responses generated as a result of receiving query messages."
287          * So, set the cache-flush bit for mDNS answers except for DNS-SD
288          * service enumeration PTRs described in RFC 6763, Section 4.1. */
289         if (i->scope->protocol == DNS_PROTOCOL_MDNS &&
290             !dns_resource_key_is_dnssd_ptr(i->rr->key))
291                 flags = DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHE_FLUSH;
292         else
293                 flags = DNS_ANSWER_AUTHENTICATED;
294 
295         return dns_answer_add(a, i->rr, ifindex, flags, NULL);
296 }
297 
dns_zone_lookup(DnsZone * z,DnsResourceKey * key,int ifindex,DnsAnswer ** ret_answer,DnsAnswer ** ret_soa,bool * ret_tentative)298 int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
299         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
300         unsigned n_answer = 0;
301         DnsZoneItem *first;
302         bool tentative = true, need_soa = false;
303         int r;
304 
305         /* Note that we don't actually need the ifindex for anything. However when it is passed we'll initialize the
306          * ifindex field in the answer with it */
307 
308         assert(z);
309         assert(key);
310         assert(ret_answer);
311 
312         /* First iteration, count what we have */
313 
314         if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
315                 bool found = false, added = false;
316                 int k;
317 
318                 /* If this is a generic match, then we have to
319                  * go through the list by the name and look
320                  * for everything manually */
321 
322                 first = hashmap_get(z->by_name, dns_resource_key_name(key));
323                 LIST_FOREACH(by_name, j, first) {
324                         if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
325                                 continue;
326 
327                         found = true;
328 
329                         k = dns_resource_key_match_rr(key, j->rr, NULL);
330                         if (k < 0)
331                                 return k;
332                         if (k > 0) {
333                                 n_answer++;
334                                 added = true;
335                         }
336 
337                 }
338 
339                 if (found && !added)
340                         need_soa = true;
341 
342         } else {
343                 bool found = false;
344 
345                 /* If this is a specific match, then look for
346                  * the right key immediately */
347 
348                 first = hashmap_get(z->by_key, key);
349                 LIST_FOREACH(by_key, j, first) {
350                         if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
351                                 continue;
352 
353                         found = true;
354                         n_answer++;
355                 }
356 
357                 if (!found) {
358                         first = hashmap_get(z->by_name, dns_resource_key_name(key));
359                         LIST_FOREACH(by_name, j, first) {
360                                 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
361                                         continue;
362 
363                                 need_soa = true;
364                                 break;
365                         }
366                 }
367         }
368 
369         if (n_answer <= 0 && !need_soa)
370                 goto return_empty;
371 
372         if (n_answer > 0) {
373                 answer = dns_answer_new(n_answer);
374                 if (!answer)
375                         return -ENOMEM;
376         }
377 
378         if (need_soa) {
379                 soa = dns_answer_new(1);
380                 if (!soa)
381                         return -ENOMEM;
382         }
383 
384         /* Second iteration, actually add the RRs to the answers */
385         if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
386                 bool found = false, added = false;
387                 int k;
388 
389                 first = hashmap_get(z->by_name, dns_resource_key_name(key));
390                 LIST_FOREACH(by_name, j, first) {
391                         if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
392                                 continue;
393 
394                         found = true;
395 
396                         if (j->state != DNS_ZONE_ITEM_PROBING)
397                                 tentative = false;
398 
399                         k = dns_resource_key_match_rr(key, j->rr, NULL);
400                         if (k < 0)
401                                 return k;
402                         if (k > 0) {
403                                 r = dns_zone_add_authenticated_answer(answer, j, ifindex);
404                                 if (r < 0)
405                                         return r;
406 
407                                 added = true;
408                         }
409                 }
410 
411                 if (found && !added) {
412                         r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
413                         if (r < 0)
414                                 return r;
415                 }
416         } else {
417                 bool found = false;
418 
419                 first = hashmap_get(z->by_key, key);
420                 LIST_FOREACH(by_key, j, first) {
421                         if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
422                                 continue;
423 
424                         found = true;
425 
426                         if (j->state != DNS_ZONE_ITEM_PROBING)
427                                 tentative = false;
428 
429                         r = dns_zone_add_authenticated_answer(answer, j, ifindex);
430                         if (r < 0)
431                                 return r;
432                 }
433 
434                 if (!found) {
435                         bool add_soa = false;
436 
437                         first = hashmap_get(z->by_name, dns_resource_key_name(key));
438                         LIST_FOREACH(by_name, j, first) {
439                                 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
440                                         continue;
441 
442                                 if (j->state != DNS_ZONE_ITEM_PROBING)
443                                         tentative = false;
444 
445                                 add_soa = true;
446                         }
447 
448                         if (add_soa) {
449                                 r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
450                                 if (r < 0)
451                                         return r;
452                         }
453                 }
454         }
455 
456         /* If the caller sets ret_tentative to NULL, then use this as
457          * indication to not return tentative entries */
458 
459         if (!ret_tentative && tentative)
460                 goto return_empty;
461 
462         *ret_answer = TAKE_PTR(answer);
463 
464         if (ret_soa)
465                 *ret_soa = TAKE_PTR(soa);
466 
467         if (ret_tentative)
468                 *ret_tentative = tentative;
469 
470         return 1;
471 
472 return_empty:
473         *ret_answer = NULL;
474 
475         if (ret_soa)
476                 *ret_soa = NULL;
477 
478         if (ret_tentative)
479                 *ret_tentative = false;
480 
481         return 0;
482 }
483 
dns_zone_item_conflict(DnsZoneItem * i)484 void dns_zone_item_conflict(DnsZoneItem *i) {
485         assert(i);
486 
487         if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
488                 return;
489 
490         log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
491 
492         dns_zone_item_probe_stop(i);
493 
494         /* Withdraw the conflict item */
495         i->state = DNS_ZONE_ITEM_WITHDRAWN;
496 
497         (void) dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
498 
499         /* Maybe change the hostname */
500         if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
501                 manager_next_hostname(i->scope->manager);
502 }
503 
dns_zone_item_notify(DnsZoneItem * i)504 void dns_zone_item_notify(DnsZoneItem *i) {
505         assert(i);
506         assert(i->probe_transaction);
507 
508         if (i->block_ready > 0)
509                 return;
510 
511         if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
512                 return;
513 
514         if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
515                 bool we_lost = false;
516 
517                 /* The probe got a successful reply. If we so far
518                  * weren't established we just give up.
519                  *
520                  * In LLMNR case if we already
521                  * were established, and the peer has the
522                  * lexicographically larger IP address we continue
523                  * and defend it. */
524 
525                 if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) {
526                         log_debug("Got a successful probe for not yet established RR, we lost.");
527                         we_lost = true;
528                 } else if (i->probe_transaction->scope->protocol == DNS_PROTOCOL_LLMNR) {
529                         assert(i->probe_transaction->received);
530                         we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0;
531                         if (we_lost)
532                                 log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost.");
533                 }
534 
535                 if (we_lost) {
536                         dns_zone_item_conflict(i);
537                         return;
538                 }
539 
540                 log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
541         }
542 
543         log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
544 
545         dns_zone_item_probe_stop(i);
546         i->state = DNS_ZONE_ITEM_ESTABLISHED;
547 }
548 
dns_zone_item_verify(DnsZoneItem * i)549 static int dns_zone_item_verify(DnsZoneItem *i) {
550         int r;
551 
552         assert(i);
553 
554         if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
555                 return 0;
556 
557         log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
558 
559         i->state = DNS_ZONE_ITEM_VERIFYING;
560         r = dns_zone_item_probe_start(i);
561         if (r < 0) {
562                 log_error_errno(r, "Failed to start probing for verifying RR: %m");
563                 i->state = DNS_ZONE_ITEM_ESTABLISHED;
564                 return r;
565         }
566 
567         return 0;
568 }
569 
dns_zone_check_conflicts(DnsZone * zone,DnsResourceRecord * rr)570 int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
571         DnsZoneItem *first;
572         int c = 0;
573 
574         assert(zone);
575         assert(rr);
576 
577         /* This checks whether a response RR we received from somebody
578          * else is one that we actually thought was uniquely ours. If
579          * so, we'll verify our RRs. */
580 
581         /* No conflict if we don't have the name at all. */
582         first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key));
583         if (!first)
584                 return 0;
585 
586         /* No conflict if we have the exact same RR */
587         if (dns_zone_get(zone, rr))
588                 return 0;
589 
590         /* No conflict if it is DNS-SD RR used for service enumeration. */
591         if (dns_resource_key_is_dnssd_ptr(rr->key))
592                 return 0;
593 
594         /* OK, somebody else has RRs for the same name. Yuck! Let's
595          * start probing again */
596 
597         LIST_FOREACH(by_name, i, first) {
598                 if (dns_resource_record_equal(i->rr, rr))
599                         continue;
600 
601                 dns_zone_item_verify(i);
602                 c++;
603         }
604 
605         return c;
606 }
607 
dns_zone_verify_conflicts(DnsZone * zone,DnsResourceKey * key)608 int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
609         DnsZoneItem *first;
610         int c = 0;
611 
612         assert(zone);
613 
614         /* Somebody else notified us about a possible conflict. Let's
615          * verify if that's true. */
616 
617         first = hashmap_get(zone->by_name, dns_resource_key_name(key));
618         if (!first)
619                 return 0;
620 
621         LIST_FOREACH(by_name, i, first) {
622                 dns_zone_item_verify(i);
623                 c++;
624         }
625 
626         return c;
627 }
628 
dns_zone_verify_all(DnsZone * zone)629 void dns_zone_verify_all(DnsZone *zone) {
630         DnsZoneItem *i;
631 
632         assert(zone);
633 
634         HASHMAP_FOREACH(i, zone->by_key)
635                 LIST_FOREACH(by_key, j, i)
636                         dns_zone_item_verify(j);
637 }
638 
dns_zone_dump(DnsZone * zone,FILE * f)639 void dns_zone_dump(DnsZone *zone, FILE *f) {
640         DnsZoneItem *i;
641 
642         if (!zone)
643                 return;
644 
645         if (!f)
646                 f = stdout;
647 
648         HASHMAP_FOREACH(i, zone->by_key)
649                 LIST_FOREACH(by_key, j, i) {
650                         const char *t;
651 
652                         t = dns_resource_record_to_string(j->rr);
653                         if (!t) {
654                                 log_oom();
655                                 continue;
656                         }
657 
658                         fputc('\t', f);
659                         fputs(t, f);
660                         fputc('\n', f);
661                 }
662 }
663 
dns_zone_is_empty(DnsZone * zone)664 bool dns_zone_is_empty(DnsZone *zone) {
665         if (!zone)
666                 return true;
667 
668         return hashmap_isempty(zone->by_key);
669 }
670 
dns_zone_contains_name(DnsZone * z,const char * name)671 bool dns_zone_contains_name(DnsZone *z, const char *name) {
672         DnsZoneItem *first;
673 
674         first = hashmap_get(z->by_name, name);
675         if (!first)
676                 return false;
677 
678         LIST_FOREACH(by_name, i, first) {
679                 if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
680                         continue;
681 
682                 return true;
683         }
684 
685         return false;
686 }
687