1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <net/if.h>
4 
5 #include "af-list.h"
6 #include "alloc-util.h"
7 #include "dns-domain.h"
8 #include "format-util.h"
9 #include "resolved-dns-answer.h"
10 #include "resolved-dns-cache.h"
11 #include "resolved-dns-packet.h"
12 #include "string-util.h"
13 
14 /* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
15  * leave DNS caches unbounded, but that's crazy. */
16 #define CACHE_MAX 4096
17 
18 /* We never keep any item longer than 2h in our cache */
19 #define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
20 
21 /* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
22  * now) */
23 #define CACHE_TTL_STRANGE_RCODE_USEC (10 * USEC_PER_SEC)
24 
25 #define CACHEABLE_QUERY_FLAGS (SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL)
26 
27 typedef enum DnsCacheItemType DnsCacheItemType;
28 typedef struct DnsCacheItem DnsCacheItem;
29 
30 enum DnsCacheItemType {
31         DNS_CACHE_POSITIVE,
32         DNS_CACHE_NODATA,
33         DNS_CACHE_NXDOMAIN,
34         DNS_CACHE_RCODE,      /* "strange" RCODE (effective only SERVFAIL for now) */
35 };
36 
37 struct DnsCacheItem {
38         DnsCacheItemType type;
39         int rcode;
40         DnsResourceKey *key;     /* The key for this item, i.e. the lookup key */
41         DnsResourceRecord *rr;   /* The RR for this item, i.e. the lookup value for positive queries */
42         DnsAnswer *answer;       /* The full validated answer, if this is an RRset acquired via a "primary" lookup */
43         DnsPacket *full_packet;  /* The full packet this information was acquired with */
44 
45         usec_t until;
46         uint64_t query_flags;    /* SD_RESOLVED_AUTHENTICATED and/or SD_RESOLVED_CONFIDENTIAL */
47         DnssecResult dnssec_result;
48 
49         int ifindex;
50         int owner_family;
51         union in_addr_union owner_address;
52 
53         unsigned prioq_idx;
54         LIST_FIELDS(DnsCacheItem, by_key);
55 
56         bool shared_owner;
57 };
58 
59 /* Returns true if this is a cache item created as result of an explicit lookup, or created as "side-effect"
60  * of another request. "Primary" entries will carry the full answer data (with NSEC, …) that can aso prove
61  * wildcard expansion, non-existance and such, while entries that were created as "side-effect" just contain
62  * immediate RR data for the specified RR key, but nothing else. */
63 #define DNS_CACHE_ITEM_IS_PRIMARY(item) (!!(item)->answer)
64 
dns_cache_item_type_to_string(DnsCacheItem * item)65 static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
66         assert(item);
67 
68         switch (item->type) {
69 
70         case DNS_CACHE_POSITIVE:
71                 return "POSITIVE";
72 
73         case DNS_CACHE_NODATA:
74                 return "NODATA";
75 
76         case DNS_CACHE_NXDOMAIN:
77                 return "NXDOMAIN";
78 
79         case DNS_CACHE_RCODE:
80                 return dns_rcode_to_string(item->rcode);
81         }
82 
83         return NULL;
84 }
85 
dns_cache_item_free(DnsCacheItem * i)86 static DnsCacheItem* dns_cache_item_free(DnsCacheItem *i) {
87         if (!i)
88                 return NULL;
89 
90         dns_resource_record_unref(i->rr);
91         dns_resource_key_unref(i->key);
92         dns_answer_unref(i->answer);
93         dns_packet_unref(i->full_packet);
94         return mfree(i);
95 }
96 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
97 
dns_cache_item_unlink_and_free(DnsCache * c,DnsCacheItem * i)98 static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
99         DnsCacheItem *first;
100 
101         assert(c);
102 
103         if (!i)
104                 return;
105 
106         first = hashmap_get(c->by_key, i->key);
107         LIST_REMOVE(by_key, first, i);
108 
109         if (first)
110                 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
111         else
112                 hashmap_remove(c->by_key, i->key);
113 
114         prioq_remove(c->by_expiry, i, &i->prioq_idx);
115 
116         dns_cache_item_free(i);
117 }
118 
dns_cache_remove_by_rr(DnsCache * c,DnsResourceRecord * rr)119 static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
120         DnsCacheItem *first;
121         int r;
122 
123         first = hashmap_get(c->by_key, rr->key);
124         LIST_FOREACH(by_key, i, first) {
125                 r = dns_resource_record_equal(i->rr, rr);
126                 if (r < 0)
127                         return r;
128                 if (r > 0) {
129                         dns_cache_item_unlink_and_free(c, i);
130                         return true;
131                 }
132         }
133 
134         return false;
135 }
136 
dns_cache_remove_by_key(DnsCache * c,DnsResourceKey * key)137 static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
138         DnsCacheItem *first;
139 
140         assert(c);
141         assert(key);
142 
143         first = hashmap_remove(c->by_key, key);
144         if (!first)
145                 return false;
146 
147         LIST_FOREACH(by_key, i, first) {
148                 prioq_remove(c->by_expiry, i, &i->prioq_idx);
149                 dns_cache_item_free(i);
150         }
151 
152         return true;
153 }
154 
dns_cache_flush(DnsCache * c)155 void dns_cache_flush(DnsCache *c) {
156         DnsResourceKey *key;
157 
158         assert(c);
159 
160         while ((key = hashmap_first_key(c->by_key)))
161                 dns_cache_remove_by_key(c, key);
162 
163         assert(hashmap_size(c->by_key) == 0);
164         assert(prioq_size(c->by_expiry) == 0);
165 
166         c->by_key = hashmap_free(c->by_key);
167         c->by_expiry = prioq_free(c->by_expiry);
168 }
169 
dns_cache_make_space(DnsCache * c,unsigned add)170 static void dns_cache_make_space(DnsCache *c, unsigned add) {
171         assert(c);
172 
173         if (add <= 0)
174                 return;
175 
176         /* Makes space for n new entries. Note that we actually allow
177          * the cache to grow beyond CACHE_MAX, but only when we shall
178          * add more RRs to the cache than CACHE_MAX at once. In that
179          * case the cache will be emptied completely otherwise. */
180 
181         for (;;) {
182                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
183                 DnsCacheItem *i;
184 
185                 if (prioq_size(c->by_expiry) <= 0)
186                         break;
187 
188                 if (prioq_size(c->by_expiry) + add < CACHE_MAX)
189                         break;
190 
191                 i = prioq_peek(c->by_expiry);
192                 assert(i);
193 
194                 /* Take an extra reference to the key so that it
195                  * doesn't go away in the middle of the remove call */
196                 key = dns_resource_key_ref(i->key);
197                 dns_cache_remove_by_key(c, key);
198         }
199 }
200 
dns_cache_prune(DnsCache * c)201 void dns_cache_prune(DnsCache *c) {
202         usec_t t = 0;
203 
204         assert(c);
205 
206         /* Remove all entries that are past their TTL */
207 
208         for (;;) {
209                 DnsCacheItem *i;
210                 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
211 
212                 i = prioq_peek(c->by_expiry);
213                 if (!i)
214                         break;
215 
216                 if (t <= 0)
217                         t = now(CLOCK_BOOTTIME);
218 
219                 if (i->until > t)
220                         break;
221 
222                 /* Depending whether this is an mDNS shared entry
223                  * either remove only this one RR or the whole RRset */
224                 log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)",
225                           i->shared_owner ? "shared " : "",
226                           dns_resource_key_to_string(i->key, key_str, sizeof key_str),
227                           (t - i->until) / USEC_PER_SEC);
228 
229                 if (i->shared_owner)
230                         dns_cache_item_unlink_and_free(c, i);
231                 else {
232                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
233 
234                         /* Take an extra reference to the key so that it
235                          * doesn't go away in the middle of the remove call */
236                         key = dns_resource_key_ref(i->key);
237                         dns_cache_remove_by_key(c, key);
238                 }
239         }
240 }
241 
dns_cache_item_prioq_compare_func(const void * a,const void * b)242 static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
243         const DnsCacheItem *x = a, *y = b;
244 
245         return CMP(x->until, y->until);
246 }
247 
dns_cache_init(DnsCache * c)248 static int dns_cache_init(DnsCache *c) {
249         int r;
250 
251         assert(c);
252 
253         r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
254         if (r < 0)
255                 return r;
256 
257         r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
258         if (r < 0)
259                 return r;
260 
261         return r;
262 }
263 
dns_cache_link_item(DnsCache * c,DnsCacheItem * i)264 static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
265         DnsCacheItem *first;
266         int r;
267 
268         assert(c);
269         assert(i);
270 
271         r = prioq_put(c->by_expiry, i, &i->prioq_idx);
272         if (r < 0)
273                 return r;
274 
275         first = hashmap_get(c->by_key, i->key);
276         if (first) {
277                 _unused_ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
278 
279                 /* Keep a reference to the original key, while we manipulate the list. */
280                 k = dns_resource_key_ref(first->key);
281 
282                 /* Now, try to reduce the number of keys we keep */
283                 dns_resource_key_reduce(&first->key, &i->key);
284 
285                 if (first->rr)
286                         dns_resource_key_reduce(&first->rr->key, &i->key);
287                 if (i->rr)
288                         dns_resource_key_reduce(&i->rr->key, &i->key);
289 
290                 LIST_PREPEND(by_key, first, i);
291                 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
292         } else {
293                 r = hashmap_put(c->by_key, i->key, i);
294                 if (r < 0) {
295                         prioq_remove(c->by_expiry, i, &i->prioq_idx);
296                         return r;
297                 }
298         }
299 
300         return 0;
301 }
302 
dns_cache_get(DnsCache * c,DnsResourceRecord * rr)303 static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
304         assert(c);
305         assert(rr);
306 
307         LIST_FOREACH(by_key, i, (DnsCacheItem*) hashmap_get(c->by_key, rr->key))
308                 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
309                         return i;
310 
311         return NULL;
312 }
313 
calculate_until(DnsResourceRecord * rr,uint32_t min_ttl,uint32_t nsec_ttl,usec_t timestamp,bool use_soa_minimum)314 static usec_t calculate_until(
315                 DnsResourceRecord *rr,
316                 uint32_t min_ttl,
317                 uint32_t nsec_ttl,
318                 usec_t timestamp,
319                 bool use_soa_minimum) {
320 
321         uint32_t ttl;
322         usec_t u;
323 
324         assert(rr);
325 
326         ttl = MIN(min_ttl, nsec_ttl);
327         if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
328                 /* If this is a SOA RR, and it is requested, clamp to the SOA's minimum field. This is used
329                  * when we do negative caching, to determine the TTL for the negative caching entry. See RFC
330                  * 2308, Section 5. */
331 
332                 if (ttl > rr->soa.minimum)
333                         ttl = rr->soa.minimum;
334         }
335 
336         u = ttl * USEC_PER_SEC;
337         if (u > CACHE_TTL_MAX_USEC)
338                 u = CACHE_TTL_MAX_USEC;
339 
340         if (rr->expiry != USEC_INFINITY) {
341                 usec_t left;
342 
343                 /* Make use of the DNSSEC RRSIG expiry info, if we have it */
344 
345                 left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
346                 if (u > left)
347                         u = left;
348         }
349 
350         return timestamp + u;
351 }
352 
dns_cache_item_update_positive(DnsCache * c,DnsCacheItem * i,DnsResourceRecord * rr,DnsAnswer * answer,DnsPacket * full_packet,uint32_t min_ttl,uint64_t query_flags,bool shared_owner,DnssecResult dnssec_result,usec_t timestamp,int ifindex,int owner_family,const union in_addr_union * owner_address)353 static void dns_cache_item_update_positive(
354                 DnsCache *c,
355                 DnsCacheItem *i,
356                 DnsResourceRecord *rr,
357                 DnsAnswer *answer,
358                 DnsPacket *full_packet,
359                 uint32_t min_ttl,
360                 uint64_t query_flags,
361                 bool shared_owner,
362                 DnssecResult dnssec_result,
363                 usec_t timestamp,
364                 int ifindex,
365                 int owner_family,
366                 const union in_addr_union *owner_address) {
367 
368         assert(c);
369         assert(i);
370         assert(rr);
371         assert(owner_address);
372 
373         i->type = DNS_CACHE_POSITIVE;
374 
375         if (!i->by_key_prev)
376                 /* We are the first item in the list, we need to
377                  * update the key used in the hashmap */
378 
379                 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
380 
381         DNS_RR_REPLACE(i->rr, dns_resource_record_ref(rr));
382 
383         DNS_RESOURCE_KEY_REPLACE(i->key, dns_resource_key_ref(rr->key));
384 
385         DNS_ANSWER_REPLACE(i->answer, dns_answer_ref(answer));
386 
387         DNS_PACKET_REPLACE(i->full_packet, dns_packet_ref(full_packet));
388 
389         i->until = calculate_until(rr, min_ttl, UINT32_MAX, timestamp, false);
390         i->query_flags = query_flags & CACHEABLE_QUERY_FLAGS;
391         i->shared_owner = shared_owner;
392         i->dnssec_result = dnssec_result;
393 
394         i->ifindex = ifindex;
395 
396         i->owner_family = owner_family;
397         i->owner_address = *owner_address;
398 
399         prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
400 }
401 
dns_cache_put_positive(DnsCache * c,DnsResourceRecord * rr,DnsAnswer * answer,DnsPacket * full_packet,uint64_t query_flags,bool shared_owner,DnssecResult dnssec_result,usec_t timestamp,int ifindex,int owner_family,const union in_addr_union * owner_address)402 static int dns_cache_put_positive(
403                 DnsCache *c,
404                 DnsResourceRecord *rr,
405                 DnsAnswer *answer,
406                 DnsPacket *full_packet,
407                 uint64_t query_flags,
408                 bool shared_owner,
409                 DnssecResult dnssec_result,
410                 usec_t timestamp,
411                 int ifindex,
412                 int owner_family,
413                 const union in_addr_union *owner_address) {
414 
415         _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
416         char key_str[DNS_RESOURCE_KEY_STRING_MAX];
417         DnsCacheItem *existing;
418         uint32_t min_ttl;
419         int r;
420 
421         assert(c);
422         assert(rr);
423         assert(owner_address);
424 
425         /* Never cache pseudo RRs */
426         if (dns_class_is_pseudo(rr->key->class))
427                 return 0;
428         if (dns_type_is_pseudo(rr->key->type))
429                 return 0;
430 
431         /* Determine the minimal TTL of all RRs in the answer plus the one by the main RR we are supposed to
432          * cache. Since we cache whole answers to questions we should never return answers where only some
433          * RRs are still valid, hence find the lowest here */
434         min_ttl = MIN(dns_answer_min_ttl(answer), rr->ttl);
435 
436         /* New TTL is 0? Delete this specific entry... */
437         if (min_ttl <= 0) {
438                 r = dns_cache_remove_by_rr(c, rr);
439                 log_debug("%s: %s",
440                           r > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry",
441                           dns_resource_key_to_string(rr->key, key_str, sizeof key_str));
442                 return 0;
443         }
444 
445         /* Entry exists already? Update TTL, timestamp and owner */
446         existing = dns_cache_get(c, rr);
447         if (existing) {
448                 dns_cache_item_update_positive(
449                                 c,
450                                 existing,
451                                 rr,
452                                 answer,
453                                 full_packet,
454                                 min_ttl,
455                                 query_flags,
456                                 shared_owner,
457                                 dnssec_result,
458                                 timestamp,
459                                 ifindex,
460                                 owner_family,
461                                 owner_address);
462                 return 0;
463         }
464 
465         /* Otherwise, add the new RR */
466         r = dns_cache_init(c);
467         if (r < 0)
468                 return r;
469 
470         dns_cache_make_space(c, 1);
471 
472         i = new(DnsCacheItem, 1);
473         if (!i)
474                 return -ENOMEM;
475 
476         *i = (DnsCacheItem) {
477                 .type = DNS_CACHE_POSITIVE,
478                 .key = dns_resource_key_ref(rr->key),
479                 .rr = dns_resource_record_ref(rr),
480                 .answer = dns_answer_ref(answer),
481                 .full_packet = dns_packet_ref(full_packet),
482                 .until = calculate_until(rr, min_ttl, UINT32_MAX, timestamp, false),
483                 .query_flags = query_flags & CACHEABLE_QUERY_FLAGS,
484                 .shared_owner = shared_owner,
485                 .dnssec_result = dnssec_result,
486                 .ifindex = ifindex,
487                 .owner_family = owner_family,
488                 .owner_address = *owner_address,
489                 .prioq_idx = PRIOQ_IDX_NULL,
490         };
491 
492         r = dns_cache_link_item(c, i);
493         if (r < 0)
494                 return r;
495 
496         if (DEBUG_LOGGING) {
497                 _cleanup_free_ char *t = NULL;
498 
499                 (void) in_addr_to_string(i->owner_family, &i->owner_address, &t);
500 
501                 log_debug("Added positive %s %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s",
502                           FLAGS_SET(i->query_flags, SD_RESOLVED_AUTHENTICATED) ? "authenticated" : "unauthenticated",
503                           FLAGS_SET(i->query_flags, SD_RESOLVED_CONFIDENTIAL) ? "confidential" : "non-confidential",
504                           i->shared_owner ? " shared" : "",
505                           dns_resource_key_to_string(i->key, key_str, sizeof key_str),
506                           (i->until - timestamp) / USEC_PER_SEC,
507                           i->ifindex == 0 ? "*" : FORMAT_IFNAME(i->ifindex),
508                           af_to_name_short(i->owner_family),
509                           strna(t));
510         }
511 
512         i = NULL;
513         return 0;
514 }
515 
dns_cache_put_negative(DnsCache * c,DnsResourceKey * key,int rcode,DnsAnswer * answer,DnsPacket * full_packet,uint64_t query_flags,DnssecResult dnssec_result,uint32_t nsec_ttl,usec_t timestamp,DnsResourceRecord * soa,int owner_family,const union in_addr_union * owner_address)516 static int dns_cache_put_negative(
517                 DnsCache *c,
518                 DnsResourceKey *key,
519                 int rcode,
520                 DnsAnswer *answer,
521                 DnsPacket *full_packet,
522                 uint64_t query_flags,
523                 DnssecResult dnssec_result,
524                 uint32_t nsec_ttl,
525                 usec_t timestamp,
526                 DnsResourceRecord *soa,
527                 int owner_family,
528                 const union in_addr_union *owner_address) {
529 
530         _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
531         char key_str[DNS_RESOURCE_KEY_STRING_MAX];
532         int r;
533 
534         assert(c);
535         assert(key);
536         assert(owner_address);
537 
538         /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
539          * important to filter out as we use this as a pseudo-type for
540          * NXDOMAIN entries */
541         if (dns_class_is_pseudo(key->class))
542                 return 0;
543         if (dns_type_is_pseudo(key->type))
544                 return 0;
545 
546         if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
547                 if (!soa)
548                         return 0;
549 
550                 /* For negative replies, check if we have a TTL of a SOA */
551                 if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
552                         log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
553                                   dns_resource_key_to_string(key, key_str, sizeof key_str));
554                         return 0;
555                 }
556         } else if (rcode != DNS_RCODE_SERVFAIL)
557                 return 0;
558 
559         r = dns_cache_init(c);
560         if (r < 0)
561                 return r;
562 
563         dns_cache_make_space(c, 1);
564 
565         i = new(DnsCacheItem, 1);
566         if (!i)
567                 return -ENOMEM;
568 
569         *i = (DnsCacheItem) {
570                 .type =
571                         rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
572                         rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE,
573                 .query_flags = query_flags & CACHEABLE_QUERY_FLAGS,
574                 .dnssec_result = dnssec_result,
575                 .owner_family = owner_family,
576                 .owner_address = *owner_address,
577                 .prioq_idx = PRIOQ_IDX_NULL,
578                 .rcode = rcode,
579                 .answer = dns_answer_ref(answer),
580                 .full_packet = dns_packet_ref(full_packet),
581         };
582 
583         /* Determine how long to cache this entry. In case we have some RRs in the answer use the lowest TTL
584          * of any of them. Typically that's the SOA's TTL, which is OK, but could possibly be lower because
585          * of some other RR. Let's better take the lowest option here than a needlessly high one */
586         i->until =
587                 i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
588                 calculate_until(soa, dns_answer_min_ttl(answer), nsec_ttl, timestamp, true);
589 
590         if (i->type == DNS_CACHE_NXDOMAIN) {
591                 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
592                  * a pseudo type for this purpose here. */
593                 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key));
594                 if (!i->key)
595                         return -ENOMEM;
596 
597                 /* Make sure to remove any previous entry for this
598                  * specific ANY key. (For non-ANY keys the cache data
599                  * is already cleared by the caller.) Note that we
600                  * don't bother removing positive or NODATA cache
601                  * items in this case, because it would either be slow
602                  * or require explicit indexing by name */
603                 dns_cache_remove_by_key(c, key);
604         } else
605                 i->key = dns_resource_key_ref(key);
606 
607         r = dns_cache_link_item(c, i);
608         if (r < 0)
609                 return r;
610 
611         log_debug("Added %s cache entry for %s "USEC_FMT"s",
612                   dns_cache_item_type_to_string(i),
613                   dns_resource_key_to_string(i->key, key_str, sizeof key_str),
614                   (i->until - timestamp) / USEC_PER_SEC);
615 
616         i = NULL;
617         return 0;
618 }
619 
dns_cache_remove_previous(DnsCache * c,DnsResourceKey * key,DnsAnswer * answer)620 static void dns_cache_remove_previous(
621                 DnsCache *c,
622                 DnsResourceKey *key,
623                 DnsAnswer *answer) {
624 
625         DnsResourceRecord *rr;
626         DnsAnswerFlags flags;
627 
628         assert(c);
629 
630         /* First, if we were passed a key (i.e. on LLMNR/DNS, but
631          * not on mDNS), delete all matching old RRs, so that we only
632          * keep complete by_key in place. */
633         if (key)
634                 dns_cache_remove_by_key(c, key);
635 
636         /* Second, flush all entries matching the answer, unless this
637          * is an RR that is explicitly marked to be "shared" between
638          * peers (i.e. mDNS RRs without the flush-cache bit set). */
639         DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
640                 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
641                         continue;
642 
643                 if (flags & DNS_ANSWER_SHARED_OWNER)
644                         continue;
645 
646                 dns_cache_remove_by_key(c, rr->key);
647         }
648 }
649 
rr_eligible(DnsResourceRecord * rr)650 static bool rr_eligible(DnsResourceRecord *rr) {
651         assert(rr);
652 
653         /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
654          * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
655          * existence from any cached NSEC/NSEC3, but that should be fine. */
656 
657         switch (rr->key->type) {
658 
659         case DNS_TYPE_NSEC:
660                 return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
661                         bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
662 
663         case DNS_TYPE_NSEC3:
664                 return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
665                         bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
666 
667         default:
668                 return true;
669         }
670 }
671 
dns_cache_put(DnsCache * c,DnsCacheMode cache_mode,DnsResourceKey * key,int rcode,DnsAnswer * answer,DnsPacket * full_packet,uint64_t query_flags,DnssecResult dnssec_result,uint32_t nsec_ttl,int owner_family,const union in_addr_union * owner_address)672 int dns_cache_put(
673                 DnsCache *c,
674                 DnsCacheMode cache_mode,
675                 DnsResourceKey *key,
676                 int rcode,
677                 DnsAnswer *answer,
678                 DnsPacket *full_packet,
679                 uint64_t query_flags,
680                 DnssecResult dnssec_result,
681                 uint32_t nsec_ttl,
682                 int owner_family,
683                 const union in_addr_union *owner_address) {
684 
685         DnsResourceRecord *soa = NULL;
686         bool weird_rcode = false;
687         DnsAnswerItem *item;
688         DnsAnswerFlags flags;
689         unsigned cache_keys;
690         usec_t timestamp;
691         int r;
692 
693         assert(c);
694         assert(owner_address);
695 
696         dns_cache_remove_previous(c, key, answer);
697 
698         /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
699          * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
700          * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
701          * short time.) */
702 
703         if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
704                 if (dns_answer_isempty(answer)) {
705                         if (key) {
706                                 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
707 
708                                 log_debug("Not caching negative entry without a SOA record: %s",
709                                           dns_resource_key_to_string(key, key_str, sizeof key_str));
710                         }
711 
712                         return 0;
713                 }
714 
715         } else {
716                 /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
717                  * beneficial. */
718                 if (rcode != DNS_RCODE_SERVFAIL)
719                         return 0;
720 
721                 weird_rcode = true;
722         }
723 
724         cache_keys = dns_answer_size(answer);
725         if (key)
726                 cache_keys++;
727 
728         /* Make some space for our new entries */
729         dns_cache_make_space(c, cache_keys);
730 
731         timestamp = now(CLOCK_BOOTTIME);
732 
733         /* Second, add in positive entries for all contained RRs */
734         DNS_ANSWER_FOREACH_ITEM(item, answer) {
735                 int primary = false;
736 
737                 if (!FLAGS_SET(item->flags, DNS_ANSWER_CACHEABLE) ||
738                     !rr_eligible(item->rr))
739                         continue;
740 
741                 if (key) {
742                         /* We store the auxiliary RRs and packet data in the cache only if they were in
743                          * direct response to the original query. If we cache an RR we also received, and
744                          * that is just auxiliary information we can't use the data, hence don't. */
745 
746                         primary = dns_resource_key_match_rr(key, item->rr, NULL);
747                         if (primary < 0)
748                                 return primary;
749                         if (primary == 0) {
750                                 primary = dns_resource_key_match_cname_or_dname(key, item->rr->key, NULL);
751                                 if (primary < 0)
752                                         return primary;
753                         }
754                 }
755 
756                 if (!primary) {
757                         DnsCacheItem *first;
758 
759                         /* Do not replace existing cache items for primary lookups with non-primary
760                          * data. After all the primary lookup data is a lot more useful. */
761                         first = hashmap_get(c->by_key, item->rr->key);
762                         if (first && DNS_CACHE_ITEM_IS_PRIMARY(first))
763                                 return 0;
764                 }
765 
766                 r = dns_cache_put_positive(
767                                 c,
768                                 item->rr,
769                                 primary ? answer : NULL,
770                                 primary ? full_packet : NULL,
771                                 ((item->flags & DNS_ANSWER_AUTHENTICATED) ? SD_RESOLVED_AUTHENTICATED : 0) |
772                                 (query_flags & SD_RESOLVED_CONFIDENTIAL),
773                                 item->flags & DNS_ANSWER_SHARED_OWNER,
774                                 dnssec_result,
775                                 timestamp,
776                                 item->ifindex,
777                                 owner_family,
778                                 owner_address);
779                 if (r < 0)
780                         goto fail;
781         }
782 
783         if (!key) /* mDNS doesn't know negative caching, really */
784                 return 0;
785 
786         /* Third, add in negative entries if the key has no RR */
787         r = dns_answer_match_key(answer, key, NULL);
788         if (r < 0)
789                 goto fail;
790         if (r > 0)
791                 return 0;
792 
793         /* But not if it has a matching CNAME/DNAME (the negative caching will be done on the canonical name,
794          * not on the alias) */
795         r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
796         if (r < 0)
797                 goto fail;
798         if (r > 0)
799                 return 0;
800 
801         /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
802          * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
803          * regardless of a SOA. */
804         r = dns_answer_find_soa(answer, key, &soa, &flags);
805         if (r < 0)
806                 goto fail;
807         if (r == 0 && !weird_rcode)
808                 return 0;
809         if (r > 0) {
810                 /* Refuse using the SOA data if it is unsigned, but the key is signed */
811                 if (FLAGS_SET(query_flags, SD_RESOLVED_AUTHENTICATED) &&
812                     (flags & DNS_ANSWER_AUTHENTICATED) == 0)
813                         return 0;
814         }
815 
816         if (cache_mode == DNS_CACHE_MODE_NO_NEGATIVE) {
817                 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
818                 log_debug("Not caching negative entry for: %s, cache mode set to no-negative",
819                           dns_resource_key_to_string(key, key_str, sizeof key_str));
820                 return 0;
821         }
822 
823         r = dns_cache_put_negative(
824                         c,
825                         key,
826                         rcode,
827                         answer,
828                         full_packet,
829                         query_flags,
830                         dnssec_result,
831                         nsec_ttl,
832                         timestamp,
833                         soa,
834                         owner_family, owner_address);
835         if (r < 0)
836                 goto fail;
837 
838         return 0;
839 
840 fail:
841         /* Adding all RRs failed. Let's clean up what we already
842          * added, just in case */
843 
844         if (key)
845                 dns_cache_remove_by_key(c, key);
846 
847         DNS_ANSWER_FOREACH_ITEM(item, answer) {
848                 if ((item->flags & DNS_ANSWER_CACHEABLE) == 0)
849                         continue;
850 
851                 dns_cache_remove_by_key(c, item->rr->key);
852         }
853 
854         return r;
855 }
856 
dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache * c,DnsResourceKey * k)857 static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
858         DnsCacheItem *i;
859         const char *n;
860         int r;
861 
862         assert(c);
863         assert(k);
864 
865         /* If we hit some OOM error, or suchlike, we don't care too
866          * much, after all this is just a cache */
867 
868         i = hashmap_get(c->by_key, k);
869         if (i)
870                 return i;
871 
872         n = dns_resource_key_name(k);
873 
874         /* Check if we have an NXDOMAIN cache item for the name, notice that we use
875          * the pseudo-type ANY for NXDOMAIN cache items. */
876         i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
877         if (i && i->type == DNS_CACHE_NXDOMAIN)
878                 return i;
879 
880         if (dns_type_may_redirect(k->type)) {
881                 /* Check if we have a CNAME record instead */
882                 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
883                 if (i && i->type != DNS_CACHE_NODATA)
884                         return i;
885 
886                 /* OK, let's look for cached DNAME records. */
887                 for (;;) {
888                         if (isempty(n))
889                                 return NULL;
890 
891                         i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
892                         if (i && i->type != DNS_CACHE_NODATA)
893                                 return i;
894 
895                         /* Jump one label ahead */
896                         r = dns_name_parent(&n);
897                         if (r <= 0)
898                                 return NULL;
899                 }
900         }
901 
902         if (k->type != DNS_TYPE_NSEC) {
903                 /* Check if we have an NSEC record instead for the name. */
904                 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
905                 if (i)
906                         return i;
907         }
908 
909         return NULL;
910 }
911 
answer_add_clamp_ttl(DnsAnswer ** answer,DnsResourceRecord * rr,int ifindex,DnsAnswerFlags answer_flags,DnsResourceRecord * rrsig,uint64_t query_flags,usec_t until,usec_t current)912 static int answer_add_clamp_ttl(
913                 DnsAnswer **answer,
914                 DnsResourceRecord *rr,
915                 int ifindex,
916                 DnsAnswerFlags answer_flags,
917                 DnsResourceRecord *rrsig,
918                 uint64_t query_flags,
919                 usec_t until,
920                 usec_t current) {
921 
922         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *patched = NULL, *patched_rrsig = NULL;
923         int r;
924 
925         assert(answer);
926         assert(rr);
927 
928         if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) {
929                 uint32_t left_ttl;
930 
931                 assert(current > 0);
932 
933                 /* Let's determine how much time is left for this cache entry. Note that we round down, but
934                  * clamp this to be 1s at minimum, since we usually want records to remain cached better too
935                  * short a time than too long a time, but otoh don't want to return 0 ever, since that has
936                  * special semantics in various contexts — in particular in mDNS */
937 
938                 left_ttl = MAX(1U, LESS_BY(until, current) / USEC_PER_SEC);
939 
940                 patched = dns_resource_record_ref(rr);
941 
942                 r = dns_resource_record_clamp_ttl(&patched, left_ttl);
943                 if (r < 0)
944                         return r;
945 
946                 rr = patched;
947 
948                 if (rrsig) {
949                         patched_rrsig = dns_resource_record_ref(rrsig);
950                         r = dns_resource_record_clamp_ttl(&patched_rrsig, left_ttl);
951                         if (r < 0)
952                                 return r;
953 
954                         rrsig = patched_rrsig;
955                 }
956         }
957 
958         r = dns_answer_add_extend(answer, rr, ifindex, answer_flags, rrsig);
959         if (r < 0)
960                 return r;
961 
962         return 0;
963 }
964 
dns_cache_lookup(DnsCache * c,DnsResourceKey * key,uint64_t query_flags,int * ret_rcode,DnsAnswer ** ret_answer,DnsPacket ** ret_full_packet,uint64_t * ret_query_flags,DnssecResult * ret_dnssec_result)965 int dns_cache_lookup(
966                 DnsCache *c,
967                 DnsResourceKey *key,
968                 uint64_t query_flags,
969                 int *ret_rcode,
970                 DnsAnswer **ret_answer,
971                 DnsPacket **ret_full_packet,
972                 uint64_t *ret_query_flags,
973                 DnssecResult *ret_dnssec_result) {
974 
975         _cleanup_(dns_packet_unrefp) DnsPacket *full_packet = NULL;
976         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
977         char key_str[DNS_RESOURCE_KEY_STRING_MAX];
978         unsigned n = 0;
979         int r;
980         bool nxdomain = false;
981         DnsCacheItem *first, *nsec = NULL;
982         bool have_authenticated = false, have_non_authenticated = false, have_confidential = false, have_non_confidential = false;
983         usec_t current = 0;
984         int found_rcode = -1;
985         DnssecResult dnssec_result = -1;
986         int have_dnssec_result = -1;
987 
988         assert(c);
989         assert(key);
990 
991         if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
992                 /* If we have ANY lookups we don't use the cache, so that the caller refreshes via the
993                  * network. */
994 
995                 log_debug("Ignoring cache for ANY lookup: %s",
996                           dns_resource_key_to_string(key, key_str, sizeof key_str));
997                 goto miss;
998         }
999 
1000         first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
1001         if (!first) {
1002                 /* If one question cannot be answered we need to refresh */
1003 
1004                 log_debug("Cache miss for %s",
1005                           dns_resource_key_to_string(key, key_str, sizeof key_str));
1006                 goto miss;
1007         }
1008 
1009         if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) {
1010                 /* 'current' is always passed to answer_add_clamp_ttl(), but is only used conditionally.
1011                  * We'll do the same assert there to make sure that it was initialized properly. */
1012                 current = now(CLOCK_BOOTTIME);
1013                 assert(current > 0);
1014         }
1015 
1016         LIST_FOREACH(by_key, j, first) {
1017                 /* If the caller doesn't allow us to answer questions from cache data learned from
1018                  * "side-effect", skip this entry. */
1019                 if (FLAGS_SET(query_flags, SD_RESOLVED_REQUIRE_PRIMARY) &&
1020                     !DNS_CACHE_ITEM_IS_PRIMARY(j)) {
1021                         log_debug("Primary answer was requested for cache lookup for %s, which we don't have.",
1022                                   dns_resource_key_to_string(key, key_str, sizeof key_str));
1023 
1024                         goto miss;
1025                 }
1026 
1027                 if (j->type == DNS_CACHE_NXDOMAIN)
1028                         nxdomain = true;
1029                 else if (j->type == DNS_CACHE_RCODE)
1030                         found_rcode = j->rcode;
1031                 else if (j->rr) {
1032                         if (j->rr->key->type == DNS_TYPE_NSEC)
1033                                 nsec = j;
1034 
1035                         n++;
1036                 }
1037 
1038                 if (FLAGS_SET(j->query_flags, SD_RESOLVED_AUTHENTICATED))
1039                         have_authenticated = true;
1040                 else
1041                         have_non_authenticated = true;
1042 
1043                 if (FLAGS_SET(j->query_flags, SD_RESOLVED_CONFIDENTIAL))
1044                         have_confidential = true;
1045                 else
1046                         have_non_confidential = true;
1047 
1048                 if (j->dnssec_result < 0) {
1049                         have_dnssec_result = false; /* an entry without dnssec result? then invalidate things for good */
1050                         dnssec_result = _DNSSEC_RESULT_INVALID;
1051                 } else if (have_dnssec_result < 0) {
1052                         have_dnssec_result = true; /* So far no result seen, let's pick this one up */
1053                         dnssec_result = j->dnssec_result;
1054                 } else if (have_dnssec_result > 0 && j->dnssec_result != dnssec_result) {
1055                         have_dnssec_result = false; /* conflicting result seen? then invalidate for good */
1056                         dnssec_result = _DNSSEC_RESULT_INVALID;
1057                 }
1058 
1059                 /* Append the answer RRs to our answer. Ideally we have the answer object, which we
1060                  * preferably use. But if the cached entry was generated as "side-effect" of a reply,
1061                  * i.e. from validated auxiliary records rather than from the main reply, then we use the
1062                  * individual RRs only instead. */
1063                 if (j->answer) {
1064 
1065                         /* Minor optimization, if the full answer object of this and the previous RR is the
1066                          * same, don't bother adding it again. Typically we store a full RRset here, hence
1067                          * that should be the case. */
1068                         if (!j->by_key_prev || j->answer != j->by_key_prev->answer) {
1069                                 DnsAnswerItem *item;
1070 
1071                                 DNS_ANSWER_FOREACH_ITEM(item, j->answer) {
1072                                         r = answer_add_clamp_ttl(
1073                                                         &answer,
1074                                                         item->rr,
1075                                                         item->ifindex,
1076                                                         item->flags,
1077                                                         item->rrsig,
1078                                                         query_flags,
1079                                                         j->until,
1080                                                         current);
1081                                         if (r < 0)
1082                                                 return r;
1083                                 }
1084                         }
1085 
1086                 } else if (j->rr) {
1087                         r = answer_add_clamp_ttl(
1088                                         &answer,
1089                                         j->rr,
1090                                         j->ifindex,
1091                                         FLAGS_SET(j->query_flags, SD_RESOLVED_AUTHENTICATED) ? DNS_ANSWER_AUTHENTICATED : 0,
1092                                         NULL,
1093                                         query_flags,
1094                                         j->until,
1095                                         current);
1096                         if (r < 0)
1097                                 return r;
1098                 }
1099 
1100                 /* We'll return any packet we have for this. Typically all cache entries for the same key
1101                  * should come from the same packet anyway, hence it doesn't really matter which packet we
1102                  * return here, they should all resolve to the same anyway. */
1103                 if (!full_packet && j->full_packet)
1104                         full_packet = dns_packet_ref(j->full_packet);
1105         }
1106 
1107         if (found_rcode >= 0) {
1108                 log_debug("RCODE %s cache hit for %s",
1109                           dns_rcode_to_string(found_rcode),
1110                           dns_resource_key_to_string(key, key_str, sizeof(key_str)));
1111 
1112                 if (ret_rcode)
1113                         *ret_rcode = found_rcode;
1114                 if (ret_answer)
1115                         *ret_answer = TAKE_PTR(answer);
1116                 if (ret_full_packet)
1117                         *ret_full_packet = TAKE_PTR(full_packet);
1118                 if (ret_query_flags)
1119                         *ret_query_flags = 0;
1120                 if (ret_dnssec_result)
1121                         *ret_dnssec_result = dnssec_result;
1122 
1123                 c->n_hit++;
1124                 return 1;
1125         }
1126 
1127         if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
1128                 /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC
1129                  * RRs from the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
1130 
1131                 log_debug("NSEC NODATA cache hit for %s",
1132                           dns_resource_key_to_string(key, key_str, sizeof key_str));
1133 
1134                 /* We only found an NSEC record that matches our name.  If it says the type doesn't exist
1135                  * report NODATA. Otherwise report a cache miss. */
1136 
1137                 if (ret_rcode)
1138                         *ret_rcode = DNS_RCODE_SUCCESS;
1139                 if (ret_answer)
1140                         *ret_answer = TAKE_PTR(answer);
1141                 if (ret_full_packet)
1142                         *ret_full_packet = TAKE_PTR(full_packet);
1143                 if (ret_query_flags)
1144                         *ret_query_flags = nsec->query_flags;
1145                 if (ret_dnssec_result)
1146                         *ret_dnssec_result = nsec->dnssec_result;
1147 
1148                 if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
1149                     !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
1150                     !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
1151                         c->n_hit++;
1152                         return 1;
1153                 }
1154 
1155                 c->n_miss++;
1156                 return 0;
1157         }
1158 
1159         log_debug("%s cache hit for %s",
1160                   n > 0    ? "Positive" :
1161                   nxdomain ? "NXDOMAIN" : "NODATA",
1162                   dns_resource_key_to_string(key, key_str, sizeof key_str));
1163 
1164         if (n <= 0) {
1165                 c->n_hit++;
1166 
1167                 if (ret_rcode)
1168                         *ret_rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
1169                 if (ret_answer)
1170                         *ret_answer = TAKE_PTR(answer);
1171                 if (ret_full_packet)
1172                         *ret_full_packet = TAKE_PTR(full_packet);
1173                 if (ret_query_flags)
1174                         *ret_query_flags =
1175                                 ((have_authenticated && !have_non_authenticated) ? SD_RESOLVED_AUTHENTICATED : 0) |
1176                                 ((have_confidential && !have_non_confidential) ? SD_RESOLVED_CONFIDENTIAL : 0);
1177                 if (ret_dnssec_result)
1178                         *ret_dnssec_result = dnssec_result;
1179 
1180                 return 1;
1181         }
1182 
1183         c->n_hit++;
1184 
1185         if (ret_rcode)
1186                 *ret_rcode = DNS_RCODE_SUCCESS;
1187         if (ret_answer)
1188                 *ret_answer = TAKE_PTR(answer);
1189         if (ret_full_packet)
1190                 *ret_full_packet = TAKE_PTR(full_packet);
1191         if (ret_query_flags)
1192                 *ret_query_flags =
1193                         ((have_authenticated && !have_non_authenticated) ? SD_RESOLVED_AUTHENTICATED : 0) |
1194                         ((have_confidential && !have_non_confidential) ? SD_RESOLVED_CONFIDENTIAL : 0);
1195         if (ret_dnssec_result)
1196                 *ret_dnssec_result = dnssec_result;
1197 
1198         return n;
1199 
1200 miss:
1201         if (ret_rcode)
1202                 *ret_rcode = DNS_RCODE_SUCCESS;
1203         if (ret_answer)
1204                 *ret_answer = NULL;
1205         if (ret_full_packet)
1206                 *ret_full_packet = NULL;
1207         if (ret_query_flags)
1208                 *ret_query_flags = 0;
1209         if (ret_dnssec_result)
1210                 *ret_dnssec_result = _DNSSEC_RESULT_INVALID;
1211 
1212         c->n_miss++;
1213         return 0;
1214 }
1215 
dns_cache_check_conflicts(DnsCache * cache,DnsResourceRecord * rr,int owner_family,const union in_addr_union * owner_address)1216 int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
1217         DnsCacheItem *first;
1218         bool same_owner = true;
1219 
1220         assert(cache);
1221         assert(rr);
1222 
1223         dns_cache_prune(cache);
1224 
1225         /* See if there's a cache entry for the same key. If there
1226          * isn't there's no conflict */
1227         first = hashmap_get(cache->by_key, rr->key);
1228         if (!first)
1229                 return 0;
1230 
1231         /* See if the RR key is owned by the same owner, if so, there
1232          * isn't a conflict either */
1233         LIST_FOREACH(by_key, i, first) {
1234                 if (i->owner_family != owner_family ||
1235                     !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
1236                         same_owner = false;
1237                         break;
1238                 }
1239         }
1240         if (same_owner)
1241                 return 0;
1242 
1243         /* See if there's the exact same RR in the cache. If yes, then
1244          * there's no conflict. */
1245         if (dns_cache_get(cache, rr))
1246                 return 0;
1247 
1248         /* There's a conflict */
1249         return 1;
1250 }
1251 
dns_cache_export_shared_to_packet(DnsCache * cache,DnsPacket * p)1252 int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
1253         unsigned ancount = 0;
1254         DnsCacheItem *i;
1255         int r;
1256 
1257         assert(cache);
1258         assert(p);
1259 
1260         HASHMAP_FOREACH(i, cache->by_key)
1261                 LIST_FOREACH(by_key, j, i) {
1262                         if (!j->rr)
1263                                 continue;
1264 
1265                         if (!j->shared_owner)
1266                                 continue;
1267 
1268                         r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
1269                         if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
1270                                 /* For mDNS, if we're unable to stuff all known answers into the given packet,
1271                                  * allocate a new one, push the RR into that one and link it to the current one.
1272                                  */
1273 
1274                                 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1275                                 ancount = 0;
1276 
1277                                 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
1278                                 if (r < 0)
1279                                         return r;
1280 
1281                                 /* continue with new packet */
1282                                 p = p->more;
1283                                 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
1284                         }
1285 
1286                         if (r < 0)
1287                                 return r;
1288 
1289                         ancount++;
1290                 }
1291 
1292         DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
1293 
1294         return 0;
1295 }
1296 
dns_cache_dump(DnsCache * cache,FILE * f)1297 void dns_cache_dump(DnsCache *cache, FILE *f) {
1298         DnsCacheItem *i;
1299 
1300         if (!cache)
1301                 return;
1302 
1303         if (!f)
1304                 f = stdout;
1305 
1306         HASHMAP_FOREACH(i, cache->by_key)
1307                 LIST_FOREACH(by_key, j, i) {
1308 
1309                         fputc('\t', f);
1310 
1311                         if (j->rr) {
1312                                 const char *t;
1313                                 t = dns_resource_record_to_string(j->rr);
1314                                 if (!t) {
1315                                         log_oom();
1316                                         continue;
1317                                 }
1318 
1319                                 fputs(t, f);
1320                                 fputc('\n', f);
1321                         } else {
1322                                 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
1323 
1324                                 fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
1325                                 fputs(" -- ", f);
1326                                 fputs(dns_cache_item_type_to_string(j), f);
1327                                 fputc('\n', f);
1328                         }
1329                 }
1330 }
1331 
dns_cache_is_empty(DnsCache * cache)1332 bool dns_cache_is_empty(DnsCache *cache) {
1333         if (!cache)
1334                 return true;
1335 
1336         return hashmap_isempty(cache->by_key);
1337 }
1338 
dns_cache_size(DnsCache * cache)1339 unsigned dns_cache_size(DnsCache *cache) {
1340         if (!cache)
1341                 return 0;
1342 
1343         return hashmap_size(cache->by_key);
1344 }
1345