]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-zone.c
macro: introduce TAKE_PTR() macro
[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 25#include "resolved-dns-zone.h"
c3036641 26#include "resolved-dnssd.h"
07630cea 27#include "string-util.h"
623a4c97
LP
28
29/* Never allow more than 1K entries */
30#define ZONE_MAX 1024
31
3ef64445 32void dns_zone_item_probe_stop(DnsZoneItem *i) {
ec2c5e43
LP
33 DnsTransaction *t;
34 assert(i);
623a4c97 35
ec2c5e43
LP
36 if (!i->probe_transaction)
37 return;
38
ae2a15bc 39 t = TAKE_PTR(i->probe_transaction);
ec2c5e43 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
8d67e72c 98DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
623a4c97
LP
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
748a548e
DR
308static int dns_zone_add_authenticated_answer(DnsAnswer *a, DnsZoneItem *i, int ifindex) {
309 DnsAnswerFlags flags;
310
311 /* From RFC 6762, Section 10.2
312 * "They (the rules about when to set the cache-flush bit) apply to
313 * startup announcements as described in Section 8.3, "Announcing",
314 * and to responses generated as a result of receiving query messages."
315 * So, set the cache-flush bit for mDNS answers except for DNS-SD
316 * service enumeration PTRs described in RFC 6763, Section 4.1. */
317 if (i->scope->protocol == DNS_PROTOCOL_MDNS &&
318 !dns_resource_key_is_dnssd_ptr(i->rr->key))
319 flags = DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHE_FLUSH;
320 else
321 flags = DNS_ANSWER_AUTHENTICATED;
322
323 return dns_answer_add(a, i->rr, ifindex, flags);
324}
325
97ebebbc 326int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
8bf52d3d 327 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
5032b16d
LP
328 unsigned n_answer = 0;
329 DnsZoneItem *j, *first;
330 bool tentative = true, need_soa = false;
d5323661 331 int r;
623a4c97 332
97ebebbc
LP
333 /* Note that we don't actually need the ifindex for anything. However when it is passed we'll initialize the
334 * ifindex field in the answer with it */
335
623a4c97 336 assert(z);
5032b16d 337 assert(key);
8bf52d3d 338 assert(ret_answer);
623a4c97 339
5032b16d 340 /* First iteration, count what we have */
ec2c5e43 341
5032b16d
LP
342 if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
343 bool found = false, added = false;
344 int k;
623a4c97 345
5032b16d
LP
346 /* If this is a generic match, then we have to
347 * go through the list by the name and look
348 * for everything manually */
623a4c97 349
1c02e7ba 350 first = hashmap_get(z->by_name, dns_resource_key_name(key));
5032b16d
LP
351 LIST_FOREACH(by_name, j, first) {
352 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
353 continue;
d5323661 354
5032b16d 355 found = true;
d5323661 356
801ad6a6 357 k = dns_resource_key_match_rr(key, j->rr, NULL);
5032b16d
LP
358 if (k < 0)
359 return k;
360 if (k > 0) {
361 n_answer++;
362 added = true;
363 }
ec2c5e43 364
5032b16d 365 }
ec2c5e43 366
5032b16d
LP
367 if (found && !added)
368 need_soa = true;
ec2c5e43 369
5032b16d
LP
370 } else {
371 bool found = false;
d5323661 372
5032b16d
LP
373 /* If this is a specific match, then look for
374 * the right key immediately */
ec2c5e43 375
5032b16d
LP
376 first = hashmap_get(z->by_key, key);
377 LIST_FOREACH(by_key, j, first) {
378 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
379 continue;
ec2c5e43 380
5032b16d
LP
381 found = true;
382 n_answer++;
383 }
ec2c5e43 384
5032b16d 385 if (!found) {
1c02e7ba 386 first = hashmap_get(z->by_name, dns_resource_key_name(key));
5032b16d 387 LIST_FOREACH(by_name, j, first) {
ec2c5e43
LP
388 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
389 continue;
390
5032b16d
LP
391 need_soa = true;
392 break;
d5323661 393 }
d5323661 394 }
623a4c97
LP
395 }
396
5032b16d
LP
397 if (n_answer <= 0 && !need_soa)
398 goto return_empty;
623a4c97 399
8bf52d3d
LP
400 if (n_answer > 0) {
401 answer = dns_answer_new(n_answer);
402 if (!answer)
403 return -ENOMEM;
404 }
623a4c97 405
5032b16d
LP
406 if (need_soa) {
407 soa = dns_answer_new(1);
8bf52d3d
LP
408 if (!soa)
409 return -ENOMEM;
410 }
411
412 /* Second iteration, actually add the RRs to the answers */
5032b16d
LP
413 if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
414 bool found = false, added = false;
415 int k;
d5323661 416
1c02e7ba 417 first = hashmap_get(z->by_name, dns_resource_key_name(key));
5032b16d
LP
418 LIST_FOREACH(by_name, j, first) {
419 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
420 continue;
ec2c5e43 421
5032b16d 422 found = true;
ec2c5e43 423
5032b16d
LP
424 if (j->state != DNS_ZONE_ITEM_PROBING)
425 tentative = false;
ec2c5e43 426
801ad6a6 427 k = dns_resource_key_match_rr(key, j->rr, NULL);
5032b16d
LP
428 if (k < 0)
429 return k;
430 if (k > 0) {
748a548e 431 r = dns_zone_add_authenticated_answer(answer, j, ifindex);
d5323661
LP
432 if (r < 0)
433 return r;
5032b16d
LP
434
435 added = true;
d5323661 436 }
5032b16d 437 }
d5323661 438
5032b16d 439 if (found && !added) {
97ebebbc 440 r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
5032b16d
LP
441 if (r < 0)
442 return r;
443 }
444 } else {
445 bool found = false;
ec2c5e43 446
5032b16d
LP
447 first = hashmap_get(z->by_key, key);
448 LIST_FOREACH(by_key, 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 452 found = true;
ec2c5e43 453
5032b16d
LP
454 if (j->state != DNS_ZONE_ITEM_PROBING)
455 tentative = false;
ec2c5e43 456
748a548e 457 r = dns_zone_add_authenticated_answer(answer, j, ifindex);
5032b16d
LP
458 if (r < 0)
459 return r;
460 }
ec2c5e43 461
5032b16d
LP
462 if (!found) {
463 bool add_soa = false;
ec2c5e43 464
1c02e7ba 465 first = hashmap_get(z->by_name, dns_resource_key_name(key));
5032b16d
LP
466 LIST_FOREACH(by_name, j, first) {
467 if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
468 continue;
ec2c5e43 469
5032b16d
LP
470 if (j->state != DNS_ZONE_ITEM_PROBING)
471 tentative = false;
ec2c5e43 472
5032b16d
LP
473 add_soa = true;
474 }
475
476 if (add_soa) {
97ebebbc 477 r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex);
5032b16d
LP
478 if (r < 0)
479 return r;
d5323661 480 }
623a4c97
LP
481 }
482 }
483
5032b16d
LP
484 /* If the caller sets ret_tentative to NULL, then use this as
485 * indication to not return tentative entries */
486
487 if (!ret_tentative && tentative)
488 goto return_empty;
489
8bf52d3d 490 *ret_answer = answer;
623a4c97
LP
491 answer = NULL;
492
5032b16d
LP
493 if (ret_soa) {
494 *ret_soa = soa;
495 soa = NULL;
496 }
8bf52d3d 497
ec2c5e43
LP
498 if (ret_tentative)
499 *ret_tentative = tentative;
500
623a4c97 501 return 1;
5032b16d
LP
502
503return_empty:
504 *ret_answer = NULL;
505
506 if (ret_soa)
507 *ret_soa = NULL;
508
509 if (ret_tentative)
510 *ret_tentative = false;
511
512 return 0;
623a4c97 513}
ec2c5e43
LP
514
515void dns_zone_item_conflict(DnsZoneItem *i) {
ec2c5e43
LP
516 assert(i);
517
a4076574
LP
518 if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
519 return;
520
7b50eb2e 521 log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
ec2c5e43 522
d84b686f
LP
523 dns_zone_item_probe_stop(i);
524
ec2c5e43
LP
525 /* Withdraw the conflict item */
526 i->state = DNS_ZONE_ITEM_WITHDRAWN;
527
c3036641
DR
528 dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
529
ec2c5e43 530 /* Maybe change the hostname */
1c02e7ba 531 if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
ec2c5e43
LP
532 manager_next_hostname(i->scope->manager);
533}
534
547973de 535void dns_zone_item_notify(DnsZoneItem *i) {
ec2c5e43
LP
536 assert(i);
537 assert(i->probe_transaction);
538
539 if (i->block_ready > 0)
540 return;
541
547973de 542 if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
ec2c5e43
LP
543 return;
544
a4076574
LP
545 if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
546 bool we_lost = false;
ec2c5e43 547
a4076574 548 /* The probe got a successful reply. If we so far
008d4ab7
DR
549 * weren't established we just give up.
550 *
551 * In LLMNR case if we already
a4076574 552 * were established, and the peer has the
4d91eec4 553 * lexicographically larger IP address we continue
a4076574
LP
554 * and defend it. */
555
2fb3034c
LP
556 if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) {
557 log_debug("Got a successful probe for not yet established RR, we lost.");
a4076574 558 we_lost = true;
008d4ab7 559 } else if (i->probe_transaction->scope->protocol == DNS_PROTOCOL_LLMNR) {
a4076574 560 assert(i->probe_transaction->received);
4d91eec4 561 we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0;
2fb3034c 562 if (we_lost)
4d91eec4 563 log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost.");
a4076574
LP
564 }
565
566 if (we_lost) {
567 dns_zone_item_conflict(i);
568 return;
569 }
570
571 log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
572 }
573
7b50eb2e 574 log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
ec2c5e43 575
a4076574
LP
576 dns_zone_item_probe_stop(i);
577 i->state = DNS_ZONE_ITEM_ESTABLISHED;
578}
579
580static int dns_zone_item_verify(DnsZoneItem *i) {
581 int r;
582
583 assert(i);
584
585 if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
586 return 0;
587
7b50eb2e 588 log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
2fb3034c 589
a4076574
LP
590 i->state = DNS_ZONE_ITEM_VERIFYING;
591 r = dns_zone_item_probe_start(i);
592 if (r < 0) {
da927ba9 593 log_error_errno(r, "Failed to start probing for verifying RR: %m");
ec2c5e43 594 i->state = DNS_ZONE_ITEM_ESTABLISHED;
a4076574
LP
595 return r;
596 }
597
598 return 0;
599}
600
601int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
602 DnsZoneItem *i, *first;
bf1594f5 603 int c = 0;
a4076574
LP
604
605 assert(zone);
606 assert(rr);
607
608 /* This checks whether a response RR we received from somebody
609 * else is one that we actually thought was uniquely ours. If
610 * so, we'll verify our RRs. */
611
612 /* No conflict if we don't have the name at all. */
1c02e7ba 613 first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key));
a4076574
LP
614 if (!first)
615 return 0;
616
617 /* No conflict if we have the exact same RR */
618 if (dns_zone_get(zone, rr))
619 return 0;
620
d686f15c
DR
621 /* No conflict if it is DNS-SD RR used for service enumeration. */
622 if (dns_resource_key_is_dnssd_ptr(rr->key))
623 return 0;
624
a4076574
LP
625 /* OK, somebody else has RRs for the same name. Yuck! Let's
626 * start probing again */
627
628 LIST_FOREACH(by_name, i, first) {
629 if (dns_resource_record_equal(i->rr, rr))
630 continue;
631
632 dns_zone_item_verify(i);
633 c++;
634 }
635
636 return c;
637}
638
639int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
640 DnsZoneItem *i, *first;
bf1594f5 641 int c = 0;
a4076574
LP
642
643 assert(zone);
644
645 /* Somebody else notified us about a possible conflict. Let's
646 * verify if that's true. */
647
1c02e7ba 648 first = hashmap_get(zone->by_name, dns_resource_key_name(key));
a4076574
LP
649 if (!first)
650 return 0;
651
652 LIST_FOREACH(by_name, i, first) {
653 dns_zone_item_verify(i);
654 c++;
655 }
656
657 return c;
ec2c5e43 658}
902bb5d8
LP
659
660void dns_zone_verify_all(DnsZone *zone) {
661 DnsZoneItem *i;
662 Iterator iterator;
663
664 assert(zone);
665
666 HASHMAP_FOREACH(i, zone->by_key, iterator) {
667 DnsZoneItem *j;
668
669 LIST_FOREACH(by_key, j, i)
670 dns_zone_item_verify(j);
671 }
672}
4d506d6b
LP
673
674void dns_zone_dump(DnsZone *zone, FILE *f) {
675 Iterator iterator;
676 DnsZoneItem *i;
4d506d6b
LP
677
678 if (!zone)
679 return;
680
681 if (!f)
682 f = stdout;
683
684 HASHMAP_FOREACH(i, zone->by_key, iterator) {
685 DnsZoneItem *j;
686
687 LIST_FOREACH(by_key, j, i) {
7b50eb2e 688 const char *t;
4d506d6b 689
7b50eb2e
LP
690 t = dns_resource_record_to_string(j->rr);
691 if (!t) {
4d506d6b
LP
692 log_oom();
693 continue;
694 }
695
696 fputc('\t', f);
697 fputs(t, f);
698 fputc('\n', f);
699 }
700 }
701}
702
703bool dns_zone_is_empty(DnsZone *zone) {
704 if (!zone)
705 return true;
706
707 return hashmap_isempty(zone->by_key);
708}
a2bf8a19
DR
709
710bool dns_zone_contains_name(DnsZone *z, const char *name) {
711 DnsZoneItem *i, *first;
712
713 first = hashmap_get(z->by_name, name);
714 if (!first)
715 return false;
716
717 LIST_FOREACH(by_name, i, first) {
718 if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
719 continue;
720
721 return true;
722 }
723
724 return false;
725}