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