]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-zone.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[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
1cc6c93a 490 *ret_answer = TAKE_PTR(answer);
623a4c97 491
1cc6c93a
YW
492 if (ret_soa)
493 *ret_soa = TAKE_PTR(soa);
8bf52d3d 494
ec2c5e43
LP
495 if (ret_tentative)
496 *ret_tentative = tentative;
497
623a4c97 498 return 1;
5032b16d
LP
499
500return_empty:
501 *ret_answer = NULL;
502
503 if (ret_soa)
504 *ret_soa = NULL;
505
506 if (ret_tentative)
507 *ret_tentative = false;
508
509 return 0;
623a4c97 510}
ec2c5e43
LP
511
512void dns_zone_item_conflict(DnsZoneItem *i) {
ec2c5e43
LP
513 assert(i);
514
a4076574
LP
515 if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED))
516 return;
517
7b50eb2e 518 log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr)));
ec2c5e43 519
d84b686f
LP
520 dns_zone_item_probe_stop(i);
521
ec2c5e43
LP
522 /* Withdraw the conflict item */
523 i->state = DNS_ZONE_ITEM_WITHDRAWN;
524
c3036641
DR
525 dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
526
ec2c5e43 527 /* Maybe change the hostname */
1c02e7ba 528 if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
ec2c5e43
LP
529 manager_next_hostname(i->scope->manager);
530}
531
547973de 532void dns_zone_item_notify(DnsZoneItem *i) {
ec2c5e43
LP
533 assert(i);
534 assert(i->probe_transaction);
535
536 if (i->block_ready > 0)
537 return;
538
547973de 539 if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_VALIDATING))
ec2c5e43
LP
540 return;
541
a4076574
LP
542 if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) {
543 bool we_lost = false;
ec2c5e43 544
a4076574 545 /* The probe got a successful reply. If we so far
008d4ab7
DR
546 * weren't established we just give up.
547 *
548 * In LLMNR case if we already
a4076574 549 * were established, and the peer has the
4d91eec4 550 * lexicographically larger IP address we continue
a4076574
LP
551 * and defend it. */
552
2fb3034c
LP
553 if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) {
554 log_debug("Got a successful probe for not yet established RR, we lost.");
a4076574 555 we_lost = true;
008d4ab7 556 } else if (i->probe_transaction->scope->protocol == DNS_PROTOCOL_LLMNR) {
a4076574 557 assert(i->probe_transaction->received);
4d91eec4 558 we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0;
2fb3034c 559 if (we_lost)
4d91eec4 560 log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost.");
a4076574
LP
561 }
562
563 if (we_lost) {
564 dns_zone_item_conflict(i);
565 return;
566 }
567
568 log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost.");
569 }
570
7b50eb2e 571 log_debug("Record %s successfully probed.", strna(dns_resource_record_to_string(i->rr)));
ec2c5e43 572
a4076574
LP
573 dns_zone_item_probe_stop(i);
574 i->state = DNS_ZONE_ITEM_ESTABLISHED;
575}
576
577static int dns_zone_item_verify(DnsZoneItem *i) {
578 int r;
579
580 assert(i);
581
582 if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
583 return 0;
584
7b50eb2e 585 log_debug("Verifying RR %s", strna(dns_resource_record_to_string(i->rr)));
2fb3034c 586
a4076574
LP
587 i->state = DNS_ZONE_ITEM_VERIFYING;
588 r = dns_zone_item_probe_start(i);
589 if (r < 0) {
da927ba9 590 log_error_errno(r, "Failed to start probing for verifying RR: %m");
ec2c5e43 591 i->state = DNS_ZONE_ITEM_ESTABLISHED;
a4076574
LP
592 return r;
593 }
594
595 return 0;
596}
597
598int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) {
599 DnsZoneItem *i, *first;
bf1594f5 600 int c = 0;
a4076574
LP
601
602 assert(zone);
603 assert(rr);
604
605 /* This checks whether a response RR we received from somebody
606 * else is one that we actually thought was uniquely ours. If
607 * so, we'll verify our RRs. */
608
609 /* No conflict if we don't have the name at all. */
1c02e7ba 610 first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key));
a4076574
LP
611 if (!first)
612 return 0;
613
614 /* No conflict if we have the exact same RR */
615 if (dns_zone_get(zone, rr))
616 return 0;
617
d686f15c
DR
618 /* No conflict if it is DNS-SD RR used for service enumeration. */
619 if (dns_resource_key_is_dnssd_ptr(rr->key))
620 return 0;
621
a4076574
LP
622 /* OK, somebody else has RRs for the same name. Yuck! Let's
623 * start probing again */
624
625 LIST_FOREACH(by_name, i, first) {
626 if (dns_resource_record_equal(i->rr, rr))
627 continue;
628
629 dns_zone_item_verify(i);
630 c++;
631 }
632
633 return c;
634}
635
636int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) {
637 DnsZoneItem *i, *first;
bf1594f5 638 int c = 0;
a4076574
LP
639
640 assert(zone);
641
642 /* Somebody else notified us about a possible conflict. Let's
643 * verify if that's true. */
644
1c02e7ba 645 first = hashmap_get(zone->by_name, dns_resource_key_name(key));
a4076574
LP
646 if (!first)
647 return 0;
648
649 LIST_FOREACH(by_name, i, first) {
650 dns_zone_item_verify(i);
651 c++;
652 }
653
654 return c;
ec2c5e43 655}
902bb5d8
LP
656
657void dns_zone_verify_all(DnsZone *zone) {
658 DnsZoneItem *i;
659 Iterator iterator;
660
661 assert(zone);
662
663 HASHMAP_FOREACH(i, zone->by_key, iterator) {
664 DnsZoneItem *j;
665
666 LIST_FOREACH(by_key, j, i)
667 dns_zone_item_verify(j);
668 }
669}
4d506d6b
LP
670
671void dns_zone_dump(DnsZone *zone, FILE *f) {
672 Iterator iterator;
673 DnsZoneItem *i;
4d506d6b
LP
674
675 if (!zone)
676 return;
677
678 if (!f)
679 f = stdout;
680
681 HASHMAP_FOREACH(i, zone->by_key, iterator) {
682 DnsZoneItem *j;
683
684 LIST_FOREACH(by_key, j, i) {
7b50eb2e 685 const char *t;
4d506d6b 686
7b50eb2e
LP
687 t = dns_resource_record_to_string(j->rr);
688 if (!t) {
4d506d6b
LP
689 log_oom();
690 continue;
691 }
692
693 fputc('\t', f);
694 fputs(t, f);
695 fputc('\n', f);
696 }
697 }
698}
699
700bool dns_zone_is_empty(DnsZone *zone) {
701 if (!zone)
702 return true;
703
704 return hashmap_isempty(zone->by_key);
705}
a2bf8a19
DR
706
707bool dns_zone_contains_name(DnsZone *z, const char *name) {
708 DnsZoneItem *i, *first;
709
710 first = hashmap_get(z->by_name, name);
711 if (!first)
712 return false;
713
714 LIST_FOREACH(by_name, i, first) {
715 if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
716 continue;
717
718 return true;
719 }
720
721 return false;
722}