]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-zone.c
Merge pull request #22791 from keszybz/bootctl-invert-order
[thirdparty/systemd.git] / src / resolve / resolved-dns-zone.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
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"
be28f72d 9#include "resolved-manager.h"
07630cea 10#include "string-util.h"
623a4c97
LP
11
12/* Never allow more than 1K entries */
13#define ZONE_MAX 1024
14
3ef64445 15void dns_zone_item_probe_stop(DnsZoneItem *i) {
ec2c5e43
LP
16 DnsTransaction *t;
17 assert(i);
623a4c97 18
ec2c5e43
LP
19 if (!i->probe_transaction)
20 return;
21
ae2a15bc 22 t = TAKE_PTR(i->probe_transaction);
ec2c5e43 23
547973de 24 set_remove(t->notify_zone_items, i);
35aa04e9 25 set_remove(t->notify_zone_items_done, i);
ec2c5e43
LP
26 dns_transaction_gc(t);
27}
623a4c97 28
75db809a 29static DnsZoneItem* dns_zone_item_free(DnsZoneItem *i) {
623a4c97 30 if (!i)
75db809a 31 return NULL;
623a4c97 32
ec2c5e43 33 dns_zone_item_probe_stop(i);
623a4c97 34 dns_resource_record_unref(i->rr);
ec2c5e43 35
75db809a 36 return mfree(i);
623a4c97 37}
623a4c97
LP
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 assert(z);
82 assert(rr);
83
03677889 84 LIST_FOREACH(by_key, i, (DnsZoneItem*) hashmap_get(z->by_key, rr->key))
3ef77d04 85 if (dns_resource_record_equal(i->rr, rr) > 0)
623a4c97
LP
86 return i;
87
88 return NULL;
89}
90
91void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) {
92 DnsZoneItem *i;
93
94 assert(z);
8cf9898a
LP
95
96 if (!rr)
97 return;
623a4c97
LP
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) {
29bd6012 164 _cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL;
ec2c5e43
LP
165 int r;
166
167 assert(i);
168
169 if (i->probe_transaction)
170 return 0;
171
775ae354
LP
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);
ec2c5e43 176 if (!t) {
1b4f6e79
LP
177 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
178
1c02e7ba 179 key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key));
1b4f6e79
LP
180 if (!key)
181 return -ENOMEM;
182
775ae354 183 r = dns_transaction_new(&t, i->scope, key, NULL, SD_RESOLVED_NO_CACHE|SD_RESOLVED_NO_ZONE);
ec2c5e43
LP
184 if (r < 0)
185 return r;
186 }
187
35aa04e9
LP
188 r = set_ensure_allocated(&t->notify_zone_items_done, NULL);
189 if (r < 0)
29bd6012 190 return r;
35aa04e9 191
de7fef4b 192 r = set_ensure_put(&t->notify_zone_items, NULL, i);
ec2c5e43 193 if (r < 0)
29bd6012 194 return r;
ec2c5e43 195
53fda2bb 196 t->probing = true;
29bd6012 197 i->probe_transaction = TAKE_PTR(t);
ec2c5e43 198
29bd6012 199 if (i->probe_transaction->state == DNS_TRANSACTION_NULL) {
ec2c5e43 200 i->block_ready++;
29bd6012 201 r = dns_transaction_go(i->probe_transaction);
ec2c5e43
LP
202 i->block_ready--;
203
204 if (r < 0) {
205 dns_zone_item_probe_stop(i);
206 return r;
207 }
208 }
209
547973de 210 dns_zone_item_notify(i);
ec2c5e43 211 return 0;
ec2c5e43
LP
212}
213
214int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
623a4c97
LP
215 _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL;
216 DnsZoneItem *existing;
217 int r;
218
219 assert(z);
ec2c5e43 220 assert(s);
623a4c97
LP
221 assert(rr);
222
222148b6 223 if (dns_class_is_pseudo(rr->key->class))
1d3b690f 224 return -EINVAL;
222148b6 225 if (dns_type_is_pseudo(rr->key->type))
1d3b690f
LP
226 return -EINVAL;
227
623a4c97
LP
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
1ed31408 236 i = new(DnsZoneItem, 1);
623a4c97
LP
237 if (!i)
238 return -ENOMEM;
239
1ed31408
LP
240 *i = (DnsZoneItem) {
241 .scope = s,
242 .rr = dns_resource_record_ref(rr),
243 .probing_enabled = probe,
244 };
623a4c97
LP
245
246 r = dns_zone_link_item(z, i);
247 if (r < 0)
248 return r;
249
ec2c5e43 250 if (probe) {
cd1b20f9
LP
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
5238e957 255 * we don't need to probe again. */
cd1b20f9 256
03677889 257 LIST_FOREACH_OTHERS(by_name, j, i)
cd1b20f9
LP
258 if (j->state == DNS_ZONE_ITEM_ESTABLISHED)
259 established = true;
ec2c5e43 260
cd1b20f9
LP
261 if (established)
262 i->state = DNS_ZONE_ITEM_ESTABLISHED;
263 else {
60eb3f7c
LP
264 i->state = DNS_ZONE_ITEM_PROBING;
265
cd1b20f9
LP
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 }
cd1b20f9 272 }
ec2c5e43
LP
273 } else
274 i->state = DNS_ZONE_ITEM_ESTABLISHED;
275
623a4c97
LP
276 i = NULL;
277 return 0;
278}
279
748a548e
DR
280static 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
04617bf8 295 return dns_answer_add(a, i->rr, ifindex, flags, NULL);
748a548e
DR
296}
297
97ebebbc 298int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
8bf52d3d 299 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
5032b16d 300 unsigned n_answer = 0;
03677889 301 DnsZoneItem *first;
5032b16d 302 bool tentative = true, need_soa = false;
d5323661 303 int r;
623a4c97 304
97ebebbc
LP
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
623a4c97 308 assert(z);
5032b16d 309 assert(key);
8bf52d3d 310 assert(ret_answer);
623a4c97 311
5032b16d 312 /* First iteration, count what we have */
ec2c5e43 313
5032b16d
LP
314 if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
315 bool found = false, added = false;
316 int k;
623a4c97 317
5032b16d
LP
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 */
623a4c97 321
1c02e7ba 322 first = hashmap_get(z->by_name, dns_resource_key_name(key));
5032b16d
LP
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;
d5323661 326
5032b16d 327 found = true;
d5323661 328
801ad6a6 329 k = dns_resource_key_match_rr(key, j->rr, NULL);
5032b16d
LP
330 if (k < 0)
331 return k;
332 if (k > 0) {
333 n_answer++;
334 added = true;
335 }
ec2c5e43 336
5032b16d 337 }
ec2c5e43 338
5032b16d
LP
339 if (found && !added)
340 need_soa = true;
ec2c5e43 341
5032b16d
LP
342 } else {
343 bool found = false;
d5323661 344
5032b16d
LP
345 /* If this is a specific match, then look for
346 * the right key immediately */
ec2c5e43 347
5032b16d
LP
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;
ec2c5e43 352
5032b16d
LP
353 found = true;
354 n_answer++;
355 }
ec2c5e43 356
5032b16d 357 if (!found) {
1c02e7ba 358 first = hashmap_get(z->by_name, dns_resource_key_name(key));
5032b16d 359 LIST_FOREACH(by_name, j, first) {
ec2c5e43
LP
360 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
361 continue;
362
5032b16d
LP
363 need_soa = true;
364 break;
d5323661 365 }
d5323661 366 }
623a4c97
LP
367 }
368
5032b16d
LP
369 if (n_answer <= 0 && !need_soa)
370 goto return_empty;
623a4c97 371
8bf52d3d
LP
372 if (n_answer > 0) {
373 answer = dns_answer_new(n_answer);
374 if (!answer)
375 return -ENOMEM;
376 }
623a4c97 377
5032b16d
LP
378 if (need_soa) {
379 soa = dns_answer_new(1);
8bf52d3d
LP
380 if (!soa)
381 return -ENOMEM;
382 }
383
384 /* Second iteration, actually add the RRs to the answers */
5032b16d
LP
385 if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
386 bool found = false, added = false;
387 int k;
d5323661 388
1c02e7ba 389 first = hashmap_get(z->by_name, dns_resource_key_name(key));
5032b16d
LP
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;
ec2c5e43 393
5032b16d 394 found = true;
ec2c5e43 395
5032b16d
LP
396 if (j->state != DNS_ZONE_ITEM_PROBING)
397 tentative = false;
ec2c5e43 398
801ad6a6 399 k = dns_resource_key_match_rr(key, j->rr, NULL);
5032b16d
LP
400 if (k < 0)
401 return k;
402 if (k > 0) {
748a548e 403 r = dns_zone_add_authenticated_answer(answer, j, ifindex);
d5323661
LP
404 if (r < 0)
405 return r;
5032b16d
LP
406
407 added = true;
d5323661 408 }
5032b16d 409 }
d5323661 410
5032b16d 411 if (found && !added) {
97ebebbc 412 r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
5032b16d
LP
413 if (r < 0)
414 return r;
415 }
416 } else {
417 bool found = false;
ec2c5e43 418
5032b16d
LP
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;
ec2c5e43 423
5032b16d 424 found = true;
ec2c5e43 425
5032b16d
LP
426 if (j->state != DNS_ZONE_ITEM_PROBING)
427 tentative = false;
ec2c5e43 428
748a548e 429 r = dns_zone_add_authenticated_answer(answer, j, ifindex);
5032b16d
LP
430 if (r < 0)
431 return r;
432 }
ec2c5e43 433
5032b16d
LP
434 if (!found) {
435 bool add_soa = false;
ec2c5e43 436
1c02e7ba 437 first = hashmap_get(z->by_name, dns_resource_key_name(key));
5032b16d
LP
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;
ec2c5e43 441
5032b16d
LP
442 if (j->state != DNS_ZONE_ITEM_PROBING)
443 tentative = false;
ec2c5e43 444
5032b16d
LP
445 add_soa = true;
446 }
447
448 if (add_soa) {
97ebebbc 449 r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
5032b16d
LP
450 if (r < 0)
451 return r;
d5323661 452 }
623a4c97
LP
453 }
454 }
455
5032b16d
LP
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
1cc6c93a 462 *ret_answer = TAKE_PTR(answer);
623a4c97 463
1cc6c93a
YW
464 if (ret_soa)
465 *ret_soa = TAKE_PTR(soa);
8bf52d3d 466
ec2c5e43
LP
467 if (ret_tentative)
468 *ret_tentative = tentative;
469
623a4c97 470 return 1;
5032b16d
LP
471
472return_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;
623a4c97 482}
ec2c5e43
LP
483
484void dns_zone_item_conflict(DnsZoneItem *i) {
ec2c5e43
LP
485 assert(i);
486
a4076574
LP
487 if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
488 return;
489
7b50eb2e 490 log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
ec2c5e43 491
d84b686f
LP
492 dns_zone_item_probe_stop(i);
493
ec2c5e43
LP
494 /* Withdraw the conflict item */
495 i->state = DNS_ZONE_ITEM_WITHDRAWN;
496
b8d6689a 497 (void) dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
c3036641 498
ec2c5e43 499 /* Maybe change the hostname */
1c02e7ba 500 if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
ec2c5e43
LP
501 manager_next_hostname(i->scope->manager);
502}
503
547973de 504void dns_zone_item_notify(DnsZoneItem *i) {
ec2c5e43
LP
505 assert(i);
506 assert(i->probe_transaction);
507
508 if (i->block_ready > 0)
509 return;
510
547973de 511 if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
ec2c5e43
LP
512 return;
513
a4076574
LP
514 if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
515 bool we_lost = false;
ec2c5e43 516
a4076574 517 /* The probe got a successful reply. If we so far
008d4ab7
DR
518 * weren't established we just give up.
519 *
520 * In LLMNR case if we already
a4076574 521 * were established, and the peer has the
4d91eec4 522 * lexicographically larger IP address we continue
a4076574
LP
523 * and defend it. */
524
2fb3034c
LP
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.");
a4076574 527 we_lost = true;
008d4ab7 528 } else if (i->probe_transaction->scope->protocol == DNS_PROTOCOL_LLMNR) {
a4076574 529 assert(i->probe_transaction->received);
4d91eec4 530 we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0;
2fb3034c 531 if (we_lost)
4d91eec4 532 log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost.");
a4076574
LP
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
7b50eb2e 543 log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
ec2c5e43 544
a4076574
LP
545 dns_zone_item_probe_stop(i);
546 i->state = DNS_ZONE_ITEM_ESTABLISHED;
547}
548
549static 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
7b50eb2e 557 log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
2fb3034c 558
a4076574
LP
559 i->state = DNS_ZONE_ITEM_VERIFYING;
560 r = dns_zone_item_probe_start(i);
561 if (r < 0) {
da927ba9 562 log_error_errno(r, "Failed to start probing for verifying RR: %m");
ec2c5e43 563 i->state = DNS_ZONE_ITEM_ESTABLISHED;
a4076574
LP
564 return r;
565 }
566
567 return 0;
568}
569
570int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
03677889 571 DnsZoneItem *first;
bf1594f5 572 int c = 0;
a4076574
LP
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. */
1c02e7ba 582 first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key));
a4076574
LP
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
d686f15c
DR
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
a4076574
LP
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
608int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
03677889 609 DnsZoneItem *first;
bf1594f5 610 int c = 0;
a4076574
LP
611
612 assert(zone);
613
614 /* Somebody else notified us about a possible conflict. Let's
615 * verify if that's true. */
616
1c02e7ba 617 first = hashmap_get(zone->by_name, dns_resource_key_name(key));
a4076574
LP
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;
ec2c5e43 627}
902bb5d8
LP
628
629void dns_zone_verify_all(DnsZone *zone) {
630 DnsZoneItem *i;
902bb5d8
LP
631
632 assert(zone);
633
03677889 634 HASHMAP_FOREACH(i, zone->by_key)
902bb5d8
LP
635 LIST_FOREACH(by_key, j, i)
636 dns_zone_item_verify(j);
902bb5d8 637}
4d506d6b
LP
638
639void dns_zone_dump(DnsZone *zone, FILE *f) {
4d506d6b 640 DnsZoneItem *i;
4d506d6b
LP
641
642 if (!zone)
643 return;
644
645 if (!f)
646 f = stdout;
647
03677889 648 HASHMAP_FOREACH(i, zone->by_key)
4d506d6b 649 LIST_FOREACH(by_key, j, i) {
7b50eb2e 650 const char *t;
4d506d6b 651
7b50eb2e
LP
652 t = dns_resource_record_to_string(j->rr);
653 if (!t) {
4d506d6b
LP
654 log_oom();
655 continue;
656 }
657
658 fputc('\t', f);
659 fputs(t, f);
660 fputc('\n', f);
661 }
4d506d6b
LP
662}
663
664bool dns_zone_is_empty(DnsZone *zone) {
665 if (!zone)
666 return true;
667
668 return hashmap_isempty(zone->by_key);
669}
a2bf8a19
DR
670
671bool dns_zone_contains_name(DnsZone *z, const char *name) {
03677889 672 DnsZoneItem *first;
a2bf8a19
DR
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}