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