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