]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-zone.c
resolved: rework parsing of /etc/hosts
[thirdparty/systemd.git] / src / resolve / resolved-dns-zone.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
623a4c97 2
b5efdb8a 3#include "alloc-util.h"
4ad7f276 4#include "dns-domain.h"
07630cea 5#include "list.h"
623a4c97 6#include "resolved-dns-packet.h"
07630cea 7#include "resolved-dns-zone.h"
c3036641 8#include "resolved-dnssd.h"
07630cea 9#include "string-util.h"
623a4c97
LP
10
11/* Never allow more than 1K entries */
12#define ZONE_MAX 1024
13
3ef64445 14void dns_zone_item_probe_stop(DnsZoneItem *i) {
ec2c5e43
LP
15 DnsTransaction *t;
16 assert(i);
623a4c97 17
ec2c5e43
LP
18 if (!i->probe_transaction)
19 return;
20
ae2a15bc 21 t = TAKE_PTR(i->probe_transaction);
ec2c5e43 22
547973de 23 set_remove(t->notify_zone_items, i);
35aa04e9 24 set_remove(t->notify_zone_items_done, i);
ec2c5e43
LP
25 dns_transaction_gc(t);
26}
623a4c97
LP
27
28static void dns_zone_item_free(DnsZoneItem *i) {
29 if (!i)
30 return;
31
ec2c5e43 32 dns_zone_item_probe_stop(i);
623a4c97 33 dns_resource_record_unref(i->rr);
ec2c5e43 34
623a4c97
LP
35 free(i);
36}
37
38DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free);
39
40static 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
1c02e7ba 55 first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key));
623a4c97
LP
56 LIST_REMOVE(by_name, first, i);
57 if (first)
1c02e7ba 58 assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0);
623a4c97 59 else
1c02e7ba 60 hashmap_remove(z->by_name, dns_resource_key_name(i->rr->key));
623a4c97
LP
61
62 dns_zone_item_free(i);
63}
64
65void 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
525d3cc7
LP
76 z->by_key = hashmap_free(z->by_key);
77 z->by_name = hashmap_free(z->by_name);
623a4c97
LP
78}
79
8d67e72c 80DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
623a4c97
LP
81 DnsZoneItem *i;
82
83 assert(z);
84 assert(rr);
85
86 LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key))
3ef77d04 87 if (dns_resource_record_equal(i->rr, rr) > 0)
623a4c97
LP
88 return i;
89
90 return NULL;
91}
92
93void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) {
94 DnsZoneItem *i;
95
96 assert(z);
97 assert(rr);
98
99 i = dns_zone_get(z, rr);
100 if (i)
101 dns_zone_item_remove_and_free(z, i);
102}
103
6db6a464
DR
104int 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
623a4c97
LP
120static int dns_zone_init(DnsZone *z) {
121 int r;
122
123 assert(z);
124
d5099efc 125 r = hashmap_ensure_allocated(&z->by_key, &dns_resource_key_hash_ops);
623a4c97
LP
126 if (r < 0)
127 return r;
128
d5099efc 129 r = hashmap_ensure_allocated(&z->by_name, &dns_name_hash_ops);
623a4c97
LP
130 if (r < 0)
131 return r;
132
133 return 0;
134}
135
136static 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
1c02e7ba 150 first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key));
623a4c97
LP
151 if (first) {
152 LIST_PREPEND(by_name, first, i);
1c02e7ba 153 assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0);
623a4c97 154 } else {
1c02e7ba 155 r = hashmap_put(z->by_name, dns_resource_key_name(i->rr->key), i);
623a4c97
LP
156 if (r < 0)
157 return r;
158 }
159
160 return 0;
161}
162
ec2c5e43 163static int dns_zone_item_probe_start(DnsZoneItem *i) {
ec2c5e43
LP
164 DnsTransaction *t;
165 int r;
166
167 assert(i);
168
169 if (i->probe_transaction)
170 return 0;
171
1c02e7ba 172 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 173 if (!t) {
1b4f6e79
LP
174 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
175
1c02e7ba 176 key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key));
1b4f6e79
LP
177 if (!key)
178 return -ENOMEM;
179
f52e61da 180 r = dns_transaction_new(&t, i->scope, key);
ec2c5e43
LP
181 if (r < 0)
182 return r;
183 }
184
547973de 185 r = set_ensure_allocated(&t->notify_zone_items, NULL);
ec2c5e43
LP
186 if (r < 0)
187 goto gc;
188
35aa04e9
LP
189 r = set_ensure_allocated(&t->notify_zone_items_done, NULL);
190 if (r < 0)
191 goto gc;
192
547973de 193 r = set_put(t->notify_zone_items, i);
ec2c5e43
LP
194 if (r < 0)
195 goto gc;
196
197 i->probe_transaction = t;
53fda2bb 198 t->probing = true;
ec2c5e43
LP
199
200 if (t->state == DNS_TRANSACTION_NULL) {
201
202 i->block_ready++;
203 r = dns_transaction_go(t);
204 i->block_ready--;
205
206 if (r < 0) {
207 dns_zone_item_probe_stop(i);
208 return r;
209 }
210 }
211
547973de 212 dns_zone_item_notify(i);
ec2c5e43
LP
213 return 0;
214
215gc:
216 dns_transaction_gc(t);
217 return r;
218}
219
220int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
623a4c97
LP
221 _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL;
222 DnsZoneItem *existing;
223 int r;
224
225 assert(z);
ec2c5e43 226 assert(s);
623a4c97
LP
227 assert(rr);
228
222148b6 229 if (dns_class_is_pseudo(rr->key->class))
1d3b690f 230 return -EINVAL;
222148b6 231 if (dns_type_is_pseudo(rr->key->type))
1d3b690f
LP
232 return -EINVAL;
233
623a4c97
LP
234 existing = dns_zone_get(z, rr);
235 if (existing)
236 return 0;
237
238 r = dns_zone_init(z);
239 if (r < 0)
240 return r;
241
242 i = new0(DnsZoneItem, 1);
243 if (!i)
244 return -ENOMEM;
245
ec2c5e43 246 i->scope = s;
623a4c97 247 i->rr = dns_resource_record_ref(rr);
ec2c5e43 248 i->probing_enabled = probe;
623a4c97
LP
249
250 r = dns_zone_link_item(z, i);
251 if (r < 0)
252 return r;
253
ec2c5e43 254 if (probe) {
cd1b20f9
LP
255 DnsZoneItem *first, *j;
256 bool established = false;
257
258 /* Check if there's already an RR with the same name
259 * established. If so, it has been probed already, and
260 * we don't ned to probe again. */
261
262 LIST_FIND_HEAD(by_name, i, first);
263 LIST_FOREACH(by_name, j, first) {
264 if (i == j)
265 continue;
266
267 if (j->state == DNS_ZONE_ITEM_ESTABLISHED)
268 established = true;
ec2c5e43
LP
269 }
270
cd1b20f9
LP
271 if (established)
272 i->state = DNS_ZONE_ITEM_ESTABLISHED;
273 else {
60eb3f7c
LP
274 i->state = DNS_ZONE_ITEM_PROBING;
275
cd1b20f9
LP
276 r = dns_zone_item_probe_start(i);
277 if (r < 0) {
278 dns_zone_item_remove_and_free(z, i);
279 i = NULL;
280 return r;
281 }
cd1b20f9 282 }
ec2c5e43
LP
283 } else
284 i->state = DNS_ZONE_ITEM_ESTABLISHED;
285
623a4c97
LP
286 i = NULL;
287 return 0;
288}
289
748a548e
DR
290static int dns_zone_add_authenticated_answer(DnsAnswer *a, DnsZoneItem *i, int ifindex) {
291 DnsAnswerFlags flags;
292
293 /* From RFC 6762, Section 10.2
294 * "They (the rules about when to set the cache-flush bit) apply to
295 * startup announcements as described in Section 8.3, "Announcing",
296 * and to responses generated as a result of receiving query messages."
297 * So, set the cache-flush bit for mDNS answers except for DNS-SD
298 * service enumeration PTRs described in RFC 6763, Section 4.1. */
299 if (i->scope->protocol == DNS_PROTOCOL_MDNS &&
300 !dns_resource_key_is_dnssd_ptr(i->rr->key))
301 flags = DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHE_FLUSH;
302 else
303 flags = DNS_ANSWER_AUTHENTICATED;
304
305 return dns_answer_add(a, i->rr, ifindex, flags);
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) {
748a548e 413 r = dns_zone_add_authenticated_answer(answer, j, ifindex);
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
748a548e 439 r = dns_zone_add_authenticated_answer(answer, j, ifindex);
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
1cc6c93a 472 *ret_answer = TAKE_PTR(answer);
623a4c97 473
1cc6c93a
YW
474 if (ret_soa)
475 *ret_soa = TAKE_PTR(soa);
8bf52d3d 476
ec2c5e43
LP
477 if (ret_tentative)
478 *ret_tentative = tentative;
479
623a4c97 480 return 1;
5032b16d
LP
481
482return_empty:
483 *ret_answer = NULL;
484
485 if (ret_soa)
486 *ret_soa = NULL;
487
488 if (ret_tentative)
489 *ret_tentative = false;
490
491 return 0;
623a4c97 492}
ec2c5e43
LP
493
494void dns_zone_item_conflict(DnsZoneItem *i) {
ec2c5e43
LP
495 assert(i);
496
a4076574
LP
497 if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
498 return;
499
7b50eb2e 500 log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
ec2c5e43 501
d84b686f
LP
502 dns_zone_item_probe_stop(i);
503
ec2c5e43
LP
504 /* Withdraw the conflict item */
505 i->state = DNS_ZONE_ITEM_WITHDRAWN;
506
c3036641
DR
507 dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
508
ec2c5e43 509 /* Maybe change the hostname */
1c02e7ba 510 if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
ec2c5e43
LP
511 manager_next_hostname(i->scope->manager);
512}
513
547973de 514void dns_zone_item_notify(DnsZoneItem *i) {
ec2c5e43
LP
515 assert(i);
516 assert(i->probe_transaction);
517
518 if (i->block_ready > 0)
519 return;
520
547973de 521 if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
ec2c5e43
LP
522 return;
523
a4076574
LP
524 if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
525 bool we_lost = false;
ec2c5e43 526
a4076574 527 /* The probe got a successful reply. If we so far
008d4ab7
DR
528 * weren't established we just give up.
529 *
530 * In LLMNR case if we already
a4076574 531 * were established, and the peer has the
4d91eec4 532 * lexicographically larger IP address we continue
a4076574
LP
533 * and defend it. */
534
2fb3034c
LP
535 if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) {
536 log_debug("Got a successful probe for not yet established RR, we lost.");
a4076574 537 we_lost = true;
008d4ab7 538 } else if (i->probe_transaction->scope->protocol == DNS_PROTOCOL_LLMNR) {
a4076574 539 assert(i->probe_transaction->received);
4d91eec4 540 we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0;
2fb3034c 541 if (we_lost)
4d91eec4 542 log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost.");
a4076574
LP
543 }
544
545 if (we_lost) {
546 dns_zone_item_conflict(i);
547 return;
548 }
549
550 log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
551 }
552
7b50eb2e 553 log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
ec2c5e43 554
a4076574
LP
555 dns_zone_item_probe_stop(i);
556 i->state = DNS_ZONE_ITEM_ESTABLISHED;
557}
558
559static int dns_zone_item_verify(DnsZoneItem *i) {
560 int r;
561
562 assert(i);
563
564 if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
565 return 0;
566
7b50eb2e 567 log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
2fb3034c 568
a4076574
LP
569 i->state = DNS_ZONE_ITEM_VERIFYING;
570 r = dns_zone_item_probe_start(i);
571 if (r < 0) {
da927ba9 572 log_error_errno(r, "Failed to start probing for verifying RR: %m");
ec2c5e43 573 i->state = DNS_ZONE_ITEM_ESTABLISHED;
a4076574
LP
574 return r;
575 }
576
577 return 0;
578}
579
580int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
581 DnsZoneItem *i, *first;
bf1594f5 582 int c = 0;
a4076574
LP
583
584 assert(zone);
585 assert(rr);
586
587 /* This checks whether a response RR we received from somebody
588 * else is one that we actually thought was uniquely ours. If
589 * so, we'll verify our RRs. */
590
591 /* No conflict if we don't have the name at all. */
1c02e7ba 592 first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key));
a4076574
LP
593 if (!first)
594 return 0;
595
596 /* No conflict if we have the exact same RR */
597 if (dns_zone_get(zone, rr))
598 return 0;
599
d686f15c
DR
600 /* No conflict if it is DNS-SD RR used for service enumeration. */
601 if (dns_resource_key_is_dnssd_ptr(rr->key))
602 return 0;
603
a4076574
LP
604 /* OK, somebody else has RRs for the same name. Yuck! Let's
605 * start probing again */
606
607 LIST_FOREACH(by_name, i, first) {
608 if (dns_resource_record_equal(i->rr, rr))
609 continue;
610
611 dns_zone_item_verify(i);
612 c++;
613 }
614
615 return c;
616}
617
618int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
619 DnsZoneItem *i, *first;
bf1594f5 620 int c = 0;
a4076574
LP
621
622 assert(zone);
623
624 /* Somebody else notified us about a possible conflict. Let's
625 * verify if that's true. */
626
1c02e7ba 627 first = hashmap_get(zone->by_name, dns_resource_key_name(key));
a4076574
LP
628 if (!first)
629 return 0;
630
631 LIST_FOREACH(by_name, i, first) {
632 dns_zone_item_verify(i);
633 c++;
634 }
635
636 return c;
ec2c5e43 637}
902bb5d8
LP
638
639void dns_zone_verify_all(DnsZone *zone) {
640 DnsZoneItem *i;
641 Iterator iterator;
642
643 assert(zone);
644
645 HASHMAP_FOREACH(i, zone->by_key, iterator) {
646 DnsZoneItem *j;
647
648 LIST_FOREACH(by_key, j, i)
649 dns_zone_item_verify(j);
650 }
651}
4d506d6b
LP
652
653void dns_zone_dump(DnsZone *zone, FILE *f) {
654 Iterator iterator;
655 DnsZoneItem *i;
4d506d6b
LP
656
657 if (!zone)
658 return;
659
660 if (!f)
661 f = stdout;
662
663 HASHMAP_FOREACH(i, zone->by_key, iterator) {
664 DnsZoneItem *j;
665
666 LIST_FOREACH(by_key, j, i) {
7b50eb2e 667 const char *t;
4d506d6b 668
7b50eb2e
LP
669 t = dns_resource_record_to_string(j->rr);
670 if (!t) {
4d506d6b
LP
671 log_oom();
672 continue;
673 }
674
675 fputc('\t', f);
676 fputs(t, f);
677 fputc('\n', f);
678 }
679 }
680}
681
682bool dns_zone_is_empty(DnsZone *zone) {
683 if (!zone)
684 return true;
685
686 return hashmap_isempty(zone->by_key);
687}
a2bf8a19
DR
688
689bool dns_zone_contains_name(DnsZone *z, const char *name) {
690 DnsZoneItem *i, *first;
691
692 first = hashmap_get(z->by_name, name);
693 if (!first)
694 return false;
695
696 LIST_FOREACH(by_name, i, first) {
697 if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
698 continue;
699
700 return true;
701 }
702
703 return false;
704}