]>
Commit | Line | Data |
---|---|---|
623a4c97 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2014 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
4ad7f276 | 22 | #include "dns-domain.h" |
07630cea | 23 | #include "list.h" |
623a4c97 | 24 | #include "resolved-dns-packet.h" |
07630cea LP |
25 | #include "resolved-dns-zone.h" |
26 | #include "string-util.h" | |
623a4c97 LP |
27 | |
28 | /* Never allow more than 1K entries */ | |
29 | #define ZONE_MAX 1024 | |
30 | ||
3ef64445 | 31 | void dns_zone_item_probe_stop(DnsZoneItem *i) { |
ec2c5e43 LP |
32 | DnsTransaction *t; |
33 | assert(i); | |
623a4c97 | 34 | |
ec2c5e43 LP |
35 | if (!i->probe_transaction) |
36 | return; | |
37 | ||
38 | t = i->probe_transaction; | |
39 | i->probe_transaction = NULL; | |
40 | ||
41 | set_remove(t->zone_items, i); | |
42 | dns_transaction_gc(t); | |
43 | } | |
623a4c97 LP |
44 | |
45 | static void dns_zone_item_free(DnsZoneItem *i) { | |
46 | if (!i) | |
47 | return; | |
48 | ||
ec2c5e43 | 49 | dns_zone_item_probe_stop(i); |
623a4c97 | 50 | dns_resource_record_unref(i->rr); |
ec2c5e43 | 51 | |
623a4c97 LP |
52 | free(i); |
53 | } | |
54 | ||
55 | DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free); | |
56 | ||
57 | static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) { | |
58 | DnsZoneItem *first; | |
59 | ||
60 | assert(z); | |
61 | ||
62 | if (!i) | |
63 | return; | |
64 | ||
65 | first = hashmap_get(z->by_key, i->rr->key); | |
66 | LIST_REMOVE(by_key, first, i); | |
67 | if (first) | |
68 | assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0); | |
69 | else | |
70 | hashmap_remove(z->by_key, i->rr->key); | |
71 | ||
72 | first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); | |
73 | LIST_REMOVE(by_name, first, i); | |
74 | if (first) | |
75 | assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0); | |
76 | else | |
77 | hashmap_remove(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); | |
78 | ||
79 | dns_zone_item_free(i); | |
80 | } | |
81 | ||
82 | void dns_zone_flush(DnsZone *z) { | |
83 | DnsZoneItem *i; | |
84 | ||
85 | assert(z); | |
86 | ||
87 | while ((i = hashmap_first(z->by_key))) | |
88 | dns_zone_item_remove_and_free(z, i); | |
89 | ||
90 | assert(hashmap_size(z->by_key) == 0); | |
91 | assert(hashmap_size(z->by_name) == 0); | |
92 | ||
525d3cc7 LP |
93 | z->by_key = hashmap_free(z->by_key); |
94 | z->by_name = hashmap_free(z->by_name); | |
623a4c97 LP |
95 | } |
96 | ||
97 | static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) { | |
98 | DnsZoneItem *i; | |
99 | ||
100 | assert(z); | |
101 | assert(rr); | |
102 | ||
103 | LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key)) | |
3ef77d04 | 104 | if (dns_resource_record_equal(i->rr, rr) > 0) |
623a4c97 LP |
105 | return i; |
106 | ||
107 | return NULL; | |
108 | } | |
109 | ||
110 | void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) { | |
111 | DnsZoneItem *i; | |
112 | ||
113 | assert(z); | |
114 | assert(rr); | |
115 | ||
116 | i = dns_zone_get(z, rr); | |
117 | if (i) | |
118 | dns_zone_item_remove_and_free(z, i); | |
119 | } | |
120 | ||
121 | static int dns_zone_init(DnsZone *z) { | |
122 | int r; | |
123 | ||
124 | assert(z); | |
125 | ||
d5099efc | 126 | r = hashmap_ensure_allocated(&z->by_key, &dns_resource_key_hash_ops); |
623a4c97 LP |
127 | if (r < 0) |
128 | return r; | |
129 | ||
d5099efc | 130 | r = hashmap_ensure_allocated(&z->by_name, &dns_name_hash_ops); |
623a4c97 LP |
131 | if (r < 0) |
132 | return r; | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { | |
138 | DnsZoneItem *first; | |
139 | int r; | |
140 | ||
141 | first = hashmap_get(z->by_key, i->rr->key); | |
142 | if (first) { | |
143 | LIST_PREPEND(by_key, first, i); | |
144 | assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0); | |
145 | } else { | |
146 | r = hashmap_put(z->by_key, i->rr->key, i); | |
147 | if (r < 0) | |
148 | return r; | |
149 | } | |
150 | ||
151 | first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); | |
152 | if (first) { | |
153 | LIST_PREPEND(by_name, first, i); | |
154 | assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0); | |
155 | } else { | |
156 | r = hashmap_put(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key), i); | |
157 | if (r < 0) | |
158 | return r; | |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
ec2c5e43 LP |
164 | static int dns_zone_item_probe_start(DnsZoneItem *i) { |
165 | _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; | |
ec2c5e43 LP |
166 | DnsTransaction *t; |
167 | int r; | |
168 | ||
169 | assert(i); | |
170 | ||
171 | if (i->probe_transaction) | |
172 | return 0; | |
173 | ||
174 | key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)); | |
175 | if (!key) | |
176 | return -ENOMEM; | |
177 | ||
f52e61da | 178 | t = dns_scope_find_transaction(i->scope, key, false); |
ec2c5e43 | 179 | if (!t) { |
f52e61da | 180 | r = dns_transaction_new(&t, i->scope, key); |
ec2c5e43 LP |
181 | if (r < 0) |
182 | return r; | |
183 | } | |
184 | ||
d5099efc | 185 | r = set_ensure_allocated(&t->zone_items, NULL); |
ec2c5e43 LP |
186 | if (r < 0) |
187 | goto gc; | |
188 | ||
189 | r = set_put(t->zone_items, i); | |
190 | if (r < 0) | |
191 | goto gc; | |
192 | ||
193 | i->probe_transaction = t; | |
194 | ||
195 | if (t->state == DNS_TRANSACTION_NULL) { | |
196 | ||
197 | i->block_ready++; | |
198 | r = dns_transaction_go(t); | |
199 | i->block_ready--; | |
200 | ||
201 | if (r < 0) { | |
202 | dns_zone_item_probe_stop(i); | |
203 | return r; | |
204 | } | |
205 | } | |
206 | ||
207 | dns_zone_item_ready(i); | |
ec2c5e43 LP |
208 | return 0; |
209 | ||
210 | gc: | |
211 | dns_transaction_gc(t); | |
212 | return r; | |
213 | } | |
214 | ||
215 | int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { | |
623a4c97 LP |
216 | _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL; |
217 | DnsZoneItem *existing; | |
218 | int r; | |
219 | ||
220 | assert(z); | |
ec2c5e43 | 221 | assert(s); |
623a4c97 LP |
222 | assert(rr); |
223 | ||
1d3b690f LP |
224 | if (rr->key->class == DNS_CLASS_ANY) |
225 | return -EINVAL; | |
226 | if (rr->key->type == DNS_TYPE_ANY) | |
227 | return -EINVAL; | |
228 | ||
623a4c97 LP |
229 | existing = dns_zone_get(z, rr); |
230 | if (existing) | |
231 | return 0; | |
232 | ||
233 | r = dns_zone_init(z); | |
234 | if (r < 0) | |
235 | return r; | |
236 | ||
237 | i = new0(DnsZoneItem, 1); | |
238 | if (!i) | |
239 | return -ENOMEM; | |
240 | ||
ec2c5e43 | 241 | i->scope = s; |
623a4c97 | 242 | i->rr = dns_resource_record_ref(rr); |
ec2c5e43 | 243 | i->probing_enabled = probe; |
623a4c97 LP |
244 | |
245 | r = dns_zone_link_item(z, i); | |
246 | if (r < 0) | |
247 | return r; | |
248 | ||
ec2c5e43 | 249 | if (probe) { |
cd1b20f9 LP |
250 | DnsZoneItem *first, *j; |
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 | |
255 | * we don't ned to probe again. */ | |
256 | ||
257 | LIST_FIND_HEAD(by_name, i, first); | |
258 | LIST_FOREACH(by_name, j, first) { | |
259 | if (i == j) | |
260 | continue; | |
261 | ||
262 | if (j->state == DNS_ZONE_ITEM_ESTABLISHED) | |
263 | established = true; | |
ec2c5e43 LP |
264 | } |
265 | ||
cd1b20f9 LP |
266 | if (established) |
267 | i->state = DNS_ZONE_ITEM_ESTABLISHED; | |
268 | else { | |
60eb3f7c LP |
269 | i->state = DNS_ZONE_ITEM_PROBING; |
270 | ||
cd1b20f9 LP |
271 | r = dns_zone_item_probe_start(i); |
272 | if (r < 0) { | |
273 | dns_zone_item_remove_and_free(z, i); | |
274 | i = NULL; | |
275 | return r; | |
276 | } | |
cd1b20f9 | 277 | } |
ec2c5e43 LP |
278 | } else |
279 | i->state = DNS_ZONE_ITEM_ESTABLISHED; | |
280 | ||
623a4c97 LP |
281 | i = NULL; |
282 | return 0; | |
283 | } | |
284 | ||
ec2c5e43 | 285 | int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { |
8bf52d3d LP |
286 | _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; |
287 | unsigned i, n_answer = 0, n_soa = 0; | |
ec2c5e43 | 288 | bool tentative = true; |
d5323661 | 289 | int r; |
623a4c97 LP |
290 | |
291 | assert(z); | |
292 | assert(q); | |
8bf52d3d LP |
293 | assert(ret_answer); |
294 | assert(ret_soa); | |
623a4c97 LP |
295 | |
296 | if (q->n_keys <= 0) { | |
8bf52d3d LP |
297 | *ret_answer = NULL; |
298 | *ret_soa = NULL; | |
ec2c5e43 LP |
299 | |
300 | if (ret_tentative) | |
301 | *ret_tentative = false; | |
302 | ||
623a4c97 LP |
303 | return 0; |
304 | } | |
305 | ||
8bf52d3d | 306 | /* First iteration, count what we have */ |
623a4c97 | 307 | for (i = 0; i < q->n_keys; i++) { |
ec2c5e43 | 308 | DnsZoneItem *j, *first; |
623a4c97 | 309 | |
d5323661 LP |
310 | if (q->keys[i]->type == DNS_TYPE_ANY || |
311 | q->keys[i]->class == DNS_CLASS_ANY) { | |
ec2c5e43 | 312 | bool found = false, added = false; |
d5323661 LP |
313 | int k; |
314 | ||
315 | /* If this is a generic match, then we have to | |
316 | * go through the list by the name and look | |
317 | * for everything manually */ | |
318 | ||
ec2c5e43 LP |
319 | first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); |
320 | LIST_FOREACH(by_name, j, first) { | |
321 | if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) | |
322 | continue; | |
323 | ||
324 | found = true; | |
325 | ||
d5323661 LP |
326 | k = dns_resource_key_match_rr(q->keys[i], j->rr); |
327 | if (k < 0) | |
328 | return k; | |
ec2c5e43 | 329 | if (k > 0) { |
8bf52d3d | 330 | n_answer++; |
ec2c5e43 LP |
331 | added = true; |
332 | } | |
333 | ||
d5323661 LP |
334 | } |
335 | ||
ec2c5e43 LP |
336 | if (found && !added) |
337 | n_soa++; | |
338 | ||
d5323661 | 339 | } else { |
ec2c5e43 LP |
340 | bool found = false; |
341 | ||
342 | /* If this is a specific match, then look for | |
343 | * the right key immediately */ | |
344 | ||
345 | first = hashmap_get(z->by_key, q->keys[i]); | |
346 | LIST_FOREACH(by_key, j, first) { | |
347 | if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) | |
348 | continue; | |
349 | ||
350 | found = true; | |
351 | n_answer++; | |
352 | } | |
353 | ||
354 | if (!found) { | |
355 | first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); | |
356 | LIST_FOREACH(by_name, j, first) { | |
357 | if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) | |
358 | continue; | |
359 | ||
360 | n_soa++; | |
361 | break; | |
362 | } | |
d5323661 | 363 | } |
d5323661 | 364 | } |
623a4c97 LP |
365 | } |
366 | ||
8bf52d3d LP |
367 | if (n_answer <= 0 && n_soa <= 0) { |
368 | *ret_answer = NULL; | |
369 | *ret_soa = NULL; | |
ec2c5e43 LP |
370 | |
371 | if (ret_tentative) | |
372 | *ret_tentative = false; | |
373 | ||
8bf52d3d | 374 | return 0; |
623a4c97 LP |
375 | } |
376 | ||
8bf52d3d LP |
377 | if (n_answer > 0) { |
378 | answer = dns_answer_new(n_answer); | |
379 | if (!answer) | |
380 | return -ENOMEM; | |
381 | } | |
623a4c97 | 382 | |
8bf52d3d LP |
383 | if (n_soa > 0) { |
384 | soa = dns_answer_new(n_soa); | |
385 | if (!soa) | |
386 | return -ENOMEM; | |
387 | } | |
388 | ||
389 | /* Second iteration, actually add the RRs to the answers */ | |
623a4c97 | 390 | for (i = 0; i < q->n_keys; i++) { |
ec2c5e43 | 391 | DnsZoneItem *j, *first; |
623a4c97 | 392 | |
d5323661 LP |
393 | if (q->keys[i]->type == DNS_TYPE_ANY || |
394 | q->keys[i]->class == DNS_CLASS_ANY) { | |
ec2c5e43 | 395 | bool found = false, added = false; |
d5323661 LP |
396 | int k; |
397 | ||
ec2c5e43 LP |
398 | first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); |
399 | LIST_FOREACH(by_name, j, first) { | |
400 | if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) | |
401 | continue; | |
402 | ||
403 | found = true; | |
404 | ||
405 | if (j->state != DNS_ZONE_ITEM_PROBING) | |
406 | tentative = false; | |
407 | ||
d5323661 LP |
408 | k = dns_resource_key_match_rr(q->keys[i], j->rr); |
409 | if (k < 0) | |
410 | return k; | |
ec2c5e43 | 411 | if (k > 0) { |
78c6a153 | 412 | r = dns_answer_add(answer, j->rr, 0); |
ec2c5e43 LP |
413 | if (r < 0) |
414 | return r; | |
415 | ||
416 | added = true; | |
417 | } | |
418 | } | |
419 | ||
420 | if (found && !added) { | |
421 | r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL); | |
d5323661 LP |
422 | if (r < 0) |
423 | return r; | |
424 | } | |
425 | } else { | |
ec2c5e43 | 426 | bool found = false; |
d5323661 | 427 | |
ec2c5e43 LP |
428 | first = hashmap_get(z->by_key, q->keys[i]); |
429 | LIST_FOREACH(by_key, j, first) { | |
430 | if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) | |
431 | continue; | |
432 | ||
433 | found = true; | |
434 | ||
435 | if (j->state != DNS_ZONE_ITEM_PROBING) | |
436 | tentative = false; | |
437 | ||
78c6a153 | 438 | r = dns_answer_add(answer, j->rr, 0); |
ec2c5e43 LP |
439 | if (r < 0) |
440 | return r; | |
441 | } | |
442 | ||
443 | if (!found) { | |
444 | bool add_soa = false; | |
445 | ||
446 | first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); | |
447 | LIST_FOREACH(by_name, j, first) { | |
448 | if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) | |
449 | continue; | |
450 | ||
451 | if (j->state != DNS_ZONE_ITEM_PROBING) | |
452 | tentative = false; | |
453 | ||
454 | add_soa = true; | |
8bf52d3d | 455 | } |
ec2c5e43 LP |
456 | |
457 | if (add_soa) { | |
57f5ad31 | 458 | r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL); |
8bf52d3d LP |
459 | if (r < 0) |
460 | return r; | |
461 | } | |
d5323661 | 462 | } |
623a4c97 LP |
463 | } |
464 | } | |
465 | ||
8bf52d3d | 466 | *ret_answer = answer; |
623a4c97 LP |
467 | answer = NULL; |
468 | ||
8bf52d3d LP |
469 | *ret_soa = soa; |
470 | soa = NULL; | |
471 | ||
ec2c5e43 LP |
472 | if (ret_tentative) |
473 | *ret_tentative = tentative; | |
474 | ||
623a4c97 LP |
475 | return 1; |
476 | } | |
ec2c5e43 LP |
477 | |
478 | void dns_zone_item_conflict(DnsZoneItem *i) { | |
479 | _cleanup_free_ char *pretty = NULL; | |
480 | ||
481 | assert(i); | |
482 | ||
a4076574 LP |
483 | if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED)) |
484 | return; | |
485 | ||
ec2c5e43 LP |
486 | dns_resource_record_to_string(i->rr, &pretty); |
487 | log_info("Detected conflict on %s", strna(pretty)); | |
488 | ||
d84b686f LP |
489 | dns_zone_item_probe_stop(i); |
490 | ||
ec2c5e43 LP |
491 | /* Withdraw the conflict item */ |
492 | i->state = DNS_ZONE_ITEM_WITHDRAWN; | |
493 | ||
494 | /* Maybe change the hostname */ | |
78c6a153 | 495 | if (manager_is_own_hostname(i->scope->manager, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0) |
ec2c5e43 LP |
496 | manager_next_hostname(i->scope->manager); |
497 | } | |
498 | ||
499 | void dns_zone_item_ready(DnsZoneItem *i) { | |
a4076574 LP |
500 | _cleanup_free_ char *pretty = NULL; |
501 | ||
ec2c5e43 LP |
502 | assert(i); |
503 | assert(i->probe_transaction); | |
504 | ||
505 | if (i->block_ready > 0) | |
506 | return; | |
507 | ||
508 | if (IN_SET(i->probe_transaction->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)) | |
509 | return; | |
510 | ||
a4076574 LP |
511 | if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) { |
512 | bool we_lost = false; | |
ec2c5e43 | 513 | |
a4076574 LP |
514 | /* The probe got a successful reply. If we so far |
515 | * weren't established we just give up. If we already | |
516 | * were established, and the peer has the | |
4d91eec4 | 517 | * lexicographically larger IP address we continue |
a4076574 LP |
518 | * and defend it. */ |
519 | ||
2fb3034c LP |
520 | if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) { |
521 | log_debug("Got a successful probe for not yet established RR, we lost."); | |
a4076574 | 522 | we_lost = true; |
2fb3034c | 523 | } else { |
a4076574 | 524 | assert(i->probe_transaction->received); |
4d91eec4 | 525 | we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0; |
2fb3034c | 526 | if (we_lost) |
4d91eec4 | 527 | log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost."); |
a4076574 LP |
528 | } |
529 | ||
530 | if (we_lost) { | |
531 | dns_zone_item_conflict(i); | |
532 | return; | |
533 | } | |
534 | ||
535 | log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost."); | |
536 | } | |
537 | ||
538 | dns_resource_record_to_string(i->rr, &pretty); | |
539 | log_debug("Record %s successfully probed.", strna(pretty)); | |
ec2c5e43 | 540 | |
a4076574 LP |
541 | dns_zone_item_probe_stop(i); |
542 | i->state = DNS_ZONE_ITEM_ESTABLISHED; | |
543 | } | |
544 | ||
545 | static int dns_zone_item_verify(DnsZoneItem *i) { | |
2fb3034c | 546 | _cleanup_free_ char *pretty = NULL; |
a4076574 LP |
547 | int r; |
548 | ||
549 | assert(i); | |
550 | ||
551 | if (i->state != DNS_ZONE_ITEM_ESTABLISHED) | |
552 | return 0; | |
553 | ||
2fb3034c LP |
554 | dns_resource_record_to_string(i->rr, &pretty); |
555 | log_debug("Verifying RR %s", strna(pretty)); | |
556 | ||
a4076574 LP |
557 | i->state = DNS_ZONE_ITEM_VERIFYING; |
558 | r = dns_zone_item_probe_start(i); | |
559 | if (r < 0) { | |
da927ba9 | 560 | log_error_errno(r, "Failed to start probing for verifying RR: %m"); |
ec2c5e43 | 561 | i->state = DNS_ZONE_ITEM_ESTABLISHED; |
a4076574 LP |
562 | return r; |
563 | } | |
564 | ||
565 | return 0; | |
566 | } | |
567 | ||
568 | int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { | |
569 | DnsZoneItem *i, *first; | |
bf1594f5 | 570 | int c = 0; |
a4076574 LP |
571 | |
572 | assert(zone); | |
573 | assert(rr); | |
574 | ||
575 | /* This checks whether a response RR we received from somebody | |
576 | * else is one that we actually thought was uniquely ours. If | |
577 | * so, we'll verify our RRs. */ | |
578 | ||
579 | /* No conflict if we don't have the name at all. */ | |
580 | first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(rr->key)); | |
581 | if (!first) | |
582 | return 0; | |
583 | ||
584 | /* No conflict if we have the exact same RR */ | |
585 | if (dns_zone_get(zone, rr)) | |
586 | return 0; | |
587 | ||
588 | /* OK, somebody else has RRs for the same name. Yuck! Let's | |
589 | * start probing again */ | |
590 | ||
591 | LIST_FOREACH(by_name, i, first) { | |
592 | if (dns_resource_record_equal(i->rr, rr)) | |
593 | continue; | |
594 | ||
595 | dns_zone_item_verify(i); | |
596 | c++; | |
597 | } | |
598 | ||
599 | return c; | |
600 | } | |
601 | ||
602 | int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) { | |
603 | DnsZoneItem *i, *first; | |
bf1594f5 | 604 | int c = 0; |
a4076574 LP |
605 | |
606 | assert(zone); | |
607 | ||
608 | /* Somebody else notified us about a possible conflict. Let's | |
609 | * verify if that's true. */ | |
610 | ||
611 | first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(key)); | |
612 | if (!first) | |
613 | return 0; | |
614 | ||
615 | LIST_FOREACH(by_name, i, first) { | |
616 | dns_zone_item_verify(i); | |
617 | c++; | |
618 | } | |
619 | ||
620 | return c; | |
ec2c5e43 | 621 | } |
902bb5d8 LP |
622 | |
623 | void dns_zone_verify_all(DnsZone *zone) { | |
624 | DnsZoneItem *i; | |
625 | Iterator iterator; | |
626 | ||
627 | assert(zone); | |
628 | ||
629 | HASHMAP_FOREACH(i, zone->by_key, iterator) { | |
630 | DnsZoneItem *j; | |
631 | ||
632 | LIST_FOREACH(by_key, j, i) | |
633 | dns_zone_item_verify(j); | |
634 | } | |
635 | } | |
4d506d6b LP |
636 | |
637 | void dns_zone_dump(DnsZone *zone, FILE *f) { | |
638 | Iterator iterator; | |
639 | DnsZoneItem *i; | |
640 | int r; | |
641 | ||
642 | if (!zone) | |
643 | return; | |
644 | ||
645 | if (!f) | |
646 | f = stdout; | |
647 | ||
648 | HASHMAP_FOREACH(i, zone->by_key, iterator) { | |
649 | DnsZoneItem *j; | |
650 | ||
651 | LIST_FOREACH(by_key, j, i) { | |
652 | _cleanup_free_ char *t = NULL; | |
653 | ||
654 | r = dns_resource_record_to_string(j->rr, &t); | |
655 | if (r < 0) { | |
656 | log_oom(); | |
657 | continue; | |
658 | } | |
659 | ||
660 | fputc('\t', f); | |
661 | fputs(t, f); | |
662 | fputc('\n', f); | |
663 | } | |
664 | } | |
665 | } | |
666 | ||
667 | bool dns_zone_is_empty(DnsZone *zone) { | |
668 | if (!zone) | |
669 | return true; | |
670 | ||
671 | return hashmap_isempty(zone->by_key); | |
672 | } |