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