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