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