LCOV - code coverage report
Current view: top level - resolve - resolved-dns-cache.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 526 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 27 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 521 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       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 (30 * USEC_PER_SEC)
      24                 :            : 
      25                 :            : typedef enum DnsCacheItemType DnsCacheItemType;
      26                 :            : typedef struct DnsCacheItem DnsCacheItem;
      27                 :            : 
      28                 :            : enum DnsCacheItemType {
      29                 :            :         DNS_CACHE_POSITIVE,
      30                 :            :         DNS_CACHE_NODATA,
      31                 :            :         DNS_CACHE_NXDOMAIN,
      32                 :            :         DNS_CACHE_RCODE,      /* "strange" RCODE (effective only SERVFAIL for now) */
      33                 :            : };
      34                 :            : 
      35                 :            : struct DnsCacheItem {
      36                 :            :         DnsCacheItemType type;
      37                 :            :         DnsResourceKey *key;
      38                 :            :         DnsResourceRecord *rr;
      39                 :            :         int rcode;
      40                 :            : 
      41                 :            :         usec_t until;
      42                 :            :         bool authenticated:1;
      43                 :            :         bool shared_owner:1;
      44                 :            : 
      45                 :            :         int ifindex;
      46                 :            :         int owner_family;
      47                 :            :         union in_addr_union owner_address;
      48                 :            : 
      49                 :            :         unsigned prioq_idx;
      50                 :            :         LIST_FIELDS(DnsCacheItem, by_key);
      51                 :            : };
      52                 :            : 
      53                 :          0 : static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
      54         [ #  # ]:          0 :         assert(item);
      55                 :            : 
      56   [ #  #  #  #  :          0 :         switch (item->type) {
                      # ]
      57                 :            : 
      58                 :          0 :         case DNS_CACHE_POSITIVE:
      59                 :          0 :                 return "POSITIVE";
      60                 :            : 
      61                 :          0 :         case DNS_CACHE_NODATA:
      62                 :          0 :                 return "NODATA";
      63                 :            : 
      64                 :          0 :         case DNS_CACHE_NXDOMAIN:
      65                 :          0 :                 return "NXDOMAIN";
      66                 :            : 
      67                 :          0 :         case DNS_CACHE_RCODE:
      68                 :          0 :                 return dns_rcode_to_string(item->rcode);
      69                 :            :         }
      70                 :            : 
      71                 :          0 :         return NULL;
      72                 :            : }
      73                 :            : 
      74                 :          0 : static void dns_cache_item_free(DnsCacheItem *i) {
      75         [ #  # ]:          0 :         if (!i)
      76                 :          0 :                 return;
      77                 :            : 
      78                 :          0 :         dns_resource_record_unref(i->rr);
      79                 :          0 :         dns_resource_key_unref(i->key);
      80                 :          0 :         free(i);
      81                 :            : }
      82                 :            : 
      83         [ #  # ]:          0 : DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
      84                 :            : 
      85                 :          0 : static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
      86                 :            :         DnsCacheItem *first;
      87                 :            : 
      88         [ #  # ]:          0 :         assert(c);
      89                 :            : 
      90         [ #  # ]:          0 :         if (!i)
      91                 :          0 :                 return;
      92                 :            : 
      93                 :          0 :         first = hashmap_get(c->by_key, i->key);
      94   [ #  #  #  #  :          0 :         LIST_REMOVE(by_key, first, i);
             #  #  #  # ]
      95                 :            : 
      96         [ #  # ]:          0 :         if (first)
      97         [ #  # ]:          0 :                 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
      98                 :            :         else
      99                 :          0 :                 hashmap_remove(c->by_key, i->key);
     100                 :            : 
     101                 :          0 :         prioq_remove(c->by_expiry, i, &i->prioq_idx);
     102                 :            : 
     103                 :          0 :         dns_cache_item_free(i);
     104                 :            : }
     105                 :            : 
     106                 :          0 : static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
     107                 :            :         DnsCacheItem *first, *i;
     108                 :            :         int r;
     109                 :            : 
     110                 :          0 :         first = hashmap_get(c->by_key, rr->key);
     111         [ #  # ]:          0 :         LIST_FOREACH(by_key, i, first) {
     112                 :          0 :                 r = dns_resource_record_equal(i->rr, rr);
     113         [ #  # ]:          0 :                 if (r < 0)
     114                 :          0 :                         return r;
     115         [ #  # ]:          0 :                 if (r > 0) {
     116                 :          0 :                         dns_cache_item_unlink_and_free(c, i);
     117                 :          0 :                         return true;
     118                 :            :                 }
     119                 :            :         }
     120                 :            : 
     121                 :          0 :         return false;
     122                 :            : }
     123                 :            : 
     124                 :          0 : static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
     125                 :            :         DnsCacheItem *first, *i, *n;
     126                 :            : 
     127         [ #  # ]:          0 :         assert(c);
     128         [ #  # ]:          0 :         assert(key);
     129                 :            : 
     130                 :          0 :         first = hashmap_remove(c->by_key, key);
     131         [ #  # ]:          0 :         if (!first)
     132                 :          0 :                 return false;
     133                 :            : 
     134         [ #  # ]:          0 :         LIST_FOREACH_SAFE(by_key, i, n, first) {
     135                 :          0 :                 prioq_remove(c->by_expiry, i, &i->prioq_idx);
     136                 :          0 :                 dns_cache_item_free(i);
     137                 :            :         }
     138                 :            : 
     139                 :          0 :         return true;
     140                 :            : }
     141                 :            : 
     142                 :          0 : void dns_cache_flush(DnsCache *c) {
     143                 :            :         DnsResourceKey *key;
     144                 :            : 
     145         [ #  # ]:          0 :         assert(c);
     146                 :            : 
     147         [ #  # ]:          0 :         while ((key = hashmap_first_key(c->by_key)))
     148                 :          0 :                 dns_cache_remove_by_key(c, key);
     149                 :            : 
     150         [ #  # ]:          0 :         assert(hashmap_size(c->by_key) == 0);
     151         [ #  # ]:          0 :         assert(prioq_size(c->by_expiry) == 0);
     152                 :            : 
     153                 :          0 :         c->by_key = hashmap_free(c->by_key);
     154                 :          0 :         c->by_expiry = prioq_free(c->by_expiry);
     155                 :          0 : }
     156                 :            : 
     157                 :          0 : static void dns_cache_make_space(DnsCache *c, unsigned add) {
     158         [ #  # ]:          0 :         assert(c);
     159                 :            : 
     160         [ #  # ]:          0 :         if (add <= 0)
     161                 :          0 :                 return;
     162                 :            : 
     163                 :            :         /* Makes space for n new entries. Note that we actually allow
     164                 :            :          * the cache to grow beyond CACHE_MAX, but only when we shall
     165                 :            :          * add more RRs to the cache than CACHE_MAX at once. In that
     166                 :            :          * case the cache will be emptied completely otherwise. */
     167                 :            : 
     168                 :          0 :         for (;;) {
     169         [ #  # ]:          0 :                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
     170                 :            :                 DnsCacheItem *i;
     171                 :            : 
     172         [ #  # ]:          0 :                 if (prioq_size(c->by_expiry) <= 0)
     173                 :          0 :                         break;
     174                 :            : 
     175         [ #  # ]:          0 :                 if (prioq_size(c->by_expiry) + add < CACHE_MAX)
     176                 :          0 :                         break;
     177                 :            : 
     178                 :          0 :                 i = prioq_peek(c->by_expiry);
     179         [ #  # ]:          0 :                 assert(i);
     180                 :            : 
     181                 :            :                 /* Take an extra reference to the key so that it
     182                 :            :                  * doesn't go away in the middle of the remove call */
     183                 :          0 :                 key = dns_resource_key_ref(i->key);
     184                 :          0 :                 dns_cache_remove_by_key(c, key);
     185                 :            :         }
     186                 :            : }
     187                 :            : 
     188                 :          0 : void dns_cache_prune(DnsCache *c) {
     189                 :          0 :         usec_t t = 0;
     190                 :            : 
     191         [ #  # ]:          0 :         assert(c);
     192                 :            : 
     193                 :            :         /* Remove all entries that are past their TTL */
     194                 :            : 
     195                 :          0 :         for (;;) {
     196                 :            :                 DnsCacheItem *i;
     197                 :            :                 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
     198                 :            : 
     199                 :          0 :                 i = prioq_peek(c->by_expiry);
     200         [ #  # ]:          0 :                 if (!i)
     201                 :          0 :                         break;
     202                 :            : 
     203         [ #  # ]:          0 :                 if (t <= 0)
     204                 :          0 :                         t = now(clock_boottime_or_monotonic());
     205                 :            : 
     206         [ #  # ]:          0 :                 if (i->until > t)
     207                 :          0 :                         break;
     208                 :            : 
     209                 :            :                 /* Depending whether this is an mDNS shared entry
     210                 :            :                  * either remove only this one RR or the whole RRset */
     211   [ #  #  #  # ]:          0 :                 log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)",
     212                 :            :                           i->shared_owner ? "shared " : "",
     213                 :            :                           dns_resource_key_to_string(i->key, key_str, sizeof key_str),
     214                 :            :                           (t - i->until) / USEC_PER_SEC);
     215                 :            : 
     216         [ #  # ]:          0 :                 if (i->shared_owner)
     217                 :          0 :                         dns_cache_item_unlink_and_free(c, i);
     218                 :            :                 else {
     219                 :          0 :                         _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
     220                 :            : 
     221                 :            :                         /* Take an extra reference to the key so that it
     222                 :            :                          * doesn't go away in the middle of the remove call */
     223                 :          0 :                         key = dns_resource_key_ref(i->key);
     224                 :          0 :                         dns_cache_remove_by_key(c, key);
     225                 :            :                 }
     226                 :            :         }
     227                 :          0 : }
     228                 :            : 
     229                 :          0 : static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
     230                 :          0 :         const DnsCacheItem *x = a, *y = b;
     231                 :            : 
     232         [ #  # ]:          0 :         return CMP(x->until, y->until);
     233                 :            : }
     234                 :            : 
     235                 :          0 : static int dns_cache_init(DnsCache *c) {
     236                 :            :         int r;
     237                 :            : 
     238         [ #  # ]:          0 :         assert(c);
     239                 :            : 
     240                 :          0 :         r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
     241         [ #  # ]:          0 :         if (r < 0)
     242                 :          0 :                 return r;
     243                 :            : 
     244                 :          0 :         r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
     245         [ #  # ]:          0 :         if (r < 0)
     246                 :          0 :                 return r;
     247                 :            : 
     248                 :          0 :         return r;
     249                 :            : }
     250                 :            : 
     251                 :          0 : static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
     252                 :            :         DnsCacheItem *first;
     253                 :            :         int r;
     254                 :            : 
     255         [ #  # ]:          0 :         assert(c);
     256         [ #  # ]:          0 :         assert(i);
     257                 :            : 
     258                 :          0 :         r = prioq_put(c->by_expiry, i, &i->prioq_idx);
     259         [ #  # ]:          0 :         if (r < 0)
     260                 :          0 :                 return r;
     261                 :            : 
     262                 :          0 :         first = hashmap_get(c->by_key, i->key);
     263         [ #  # ]:          0 :         if (first) {
     264                 :          0 :                 _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
     265                 :            : 
     266                 :            :                 /* Keep a reference to the original key, while we manipulate the list. */
     267                 :          0 :                 k = dns_resource_key_ref(first->key);
     268                 :            : 
     269                 :            :                 /* Now, try to reduce the number of keys we keep */
     270                 :          0 :                 dns_resource_key_reduce(&first->key, &i->key);
     271                 :            : 
     272         [ #  # ]:          0 :                 if (first->rr)
     273                 :          0 :                         dns_resource_key_reduce(&first->rr->key, &i->key);
     274         [ #  # ]:          0 :                 if (i->rr)
     275                 :          0 :                         dns_resource_key_reduce(&i->rr->key, &i->key);
     276                 :            : 
     277   [ #  #  #  # ]:          0 :                 LIST_PREPEND(by_key, first, i);
     278         [ #  # ]:          0 :                 assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
     279                 :            :         } else {
     280                 :          0 :                 r = hashmap_put(c->by_key, i->key, i);
     281         [ #  # ]:          0 :                 if (r < 0) {
     282                 :          0 :                         prioq_remove(c->by_expiry, i, &i->prioq_idx);
     283                 :          0 :                         return r;
     284                 :            :                 }
     285                 :            :         }
     286                 :            : 
     287                 :          0 :         return 0;
     288                 :            : }
     289                 :            : 
     290                 :          0 : static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
     291                 :            :         DnsCacheItem *i;
     292                 :            : 
     293         [ #  # ]:          0 :         assert(c);
     294         [ #  # ]:          0 :         assert(rr);
     295                 :            : 
     296         [ #  # ]:          0 :         LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key))
     297   [ #  #  #  # ]:          0 :                 if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
     298                 :          0 :                         return i;
     299                 :            : 
     300                 :          0 :         return NULL;
     301                 :            : }
     302                 :            : 
     303                 :          0 : static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) {
     304                 :            :         uint32_t ttl;
     305                 :            :         usec_t u;
     306                 :            : 
     307         [ #  # ]:          0 :         assert(rr);
     308                 :            : 
     309                 :          0 :         ttl = MIN(rr->ttl, nsec_ttl);
     310   [ #  #  #  # ]:          0 :         if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
     311                 :            :                 /* If this is a SOA RR, and it is requested, clamp to
     312                 :            :                  * the SOA's minimum field. This is used when we do
     313                 :            :                  * negative caching, to determine the TTL for the
     314                 :            :                  * negative caching entry.  See RFC 2308, Section
     315                 :            :                  * 5. */
     316                 :            : 
     317         [ #  # ]:          0 :                 if (ttl > rr->soa.minimum)
     318                 :          0 :                         ttl = rr->soa.minimum;
     319                 :            :         }
     320                 :            : 
     321                 :          0 :         u = ttl * USEC_PER_SEC;
     322         [ #  # ]:          0 :         if (u > CACHE_TTL_MAX_USEC)
     323                 :          0 :                 u = CACHE_TTL_MAX_USEC;
     324                 :            : 
     325         [ #  # ]:          0 :         if (rr->expiry != USEC_INFINITY) {
     326                 :            :                 usec_t left;
     327                 :            : 
     328                 :            :                 /* Make use of the DNSSEC RRSIG expiry info, if we
     329                 :            :                  * have it */
     330                 :            : 
     331         [ #  # ]:          0 :                 left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
     332         [ #  # ]:          0 :                 if (u > left)
     333                 :          0 :                         u = left;
     334                 :            :         }
     335                 :            : 
     336                 :          0 :         return timestamp + u;
     337                 :            : }
     338                 :            : 
     339                 :          0 : static void dns_cache_item_update_positive(
     340                 :            :                 DnsCache *c,
     341                 :            :                 DnsCacheItem *i,
     342                 :            :                 DnsResourceRecord *rr,
     343                 :            :                 bool authenticated,
     344                 :            :                 bool shared_owner,
     345                 :            :                 usec_t timestamp,
     346                 :            :                 int ifindex,
     347                 :            :                 int owner_family,
     348                 :            :                 const union in_addr_union *owner_address) {
     349                 :            : 
     350         [ #  # ]:          0 :         assert(c);
     351         [ #  # ]:          0 :         assert(i);
     352         [ #  # ]:          0 :         assert(rr);
     353         [ #  # ]:          0 :         assert(owner_address);
     354                 :            : 
     355                 :          0 :         i->type = DNS_CACHE_POSITIVE;
     356                 :            : 
     357         [ #  # ]:          0 :         if (!i->by_key_prev)
     358                 :            :                 /* We are the first item in the list, we need to
     359                 :            :                  * update the key used in the hashmap */
     360                 :            : 
     361         [ #  # ]:          0 :                 assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
     362                 :            : 
     363                 :          0 :         dns_resource_record_ref(rr);
     364                 :          0 :         dns_resource_record_unref(i->rr);
     365                 :          0 :         i->rr = rr;
     366                 :            : 
     367                 :          0 :         dns_resource_key_unref(i->key);
     368                 :          0 :         i->key = dns_resource_key_ref(rr->key);
     369                 :            : 
     370                 :          0 :         i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
     371                 :          0 :         i->authenticated = authenticated;
     372                 :          0 :         i->shared_owner = shared_owner;
     373                 :            : 
     374                 :          0 :         i->ifindex = ifindex;
     375                 :            : 
     376                 :          0 :         i->owner_family = owner_family;
     377                 :          0 :         i->owner_address = *owner_address;
     378                 :            : 
     379                 :          0 :         prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
     380                 :          0 : }
     381                 :            : 
     382                 :          0 : static int dns_cache_put_positive(
     383                 :            :                 DnsCache *c,
     384                 :            :                 DnsResourceRecord *rr,
     385                 :            :                 bool authenticated,
     386                 :            :                 bool shared_owner,
     387                 :            :                 usec_t timestamp,
     388                 :            :                 int ifindex,
     389                 :            :                 int owner_family,
     390                 :            :                 const union in_addr_union *owner_address) {
     391                 :            : 
     392                 :          0 :         _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
     393                 :            :         DnsCacheItem *existing;
     394                 :            :         char key_str[DNS_RESOURCE_KEY_STRING_MAX];
     395                 :            :         int r, k;
     396                 :            : 
     397         [ #  # ]:          0 :         assert(c);
     398         [ #  # ]:          0 :         assert(rr);
     399         [ #  # ]:          0 :         assert(owner_address);
     400                 :            : 
     401                 :            :         /* Never cache pseudo RRs */
     402         [ #  # ]:          0 :         if (dns_class_is_pseudo(rr->key->class))
     403                 :          0 :                 return 0;
     404         [ #  # ]:          0 :         if (dns_type_is_pseudo(rr->key->type))
     405                 :          0 :                 return 0;
     406                 :            : 
     407                 :            :         /* New TTL is 0? Delete this specific entry... */
     408         [ #  # ]:          0 :         if (rr->ttl <= 0) {
     409                 :          0 :                 k = dns_cache_remove_by_rr(c, rr);
     410   [ #  #  #  # ]:          0 :                 log_debug("%s: %s",
     411                 :            :                           k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry",
     412                 :            :                           dns_resource_key_to_string(rr->key, key_str, sizeof key_str));
     413                 :          0 :                 return 0;
     414                 :            :         }
     415                 :            : 
     416                 :            :         /* Entry exists already? Update TTL, timestamp and owner */
     417                 :          0 :         existing = dns_cache_get(c, rr);
     418         [ #  # ]:          0 :         if (existing) {
     419                 :          0 :                 dns_cache_item_update_positive(
     420                 :            :                                 c,
     421                 :            :                                 existing,
     422                 :            :                                 rr,
     423                 :            :                                 authenticated,
     424                 :            :                                 shared_owner,
     425                 :            :                                 timestamp,
     426                 :            :                                 ifindex,
     427                 :            :                                 owner_family,
     428                 :            :                                 owner_address);
     429                 :          0 :                 return 0;
     430                 :            :         }
     431                 :            : 
     432                 :            :         /* Otherwise, add the new RR */
     433                 :          0 :         r = dns_cache_init(c);
     434         [ #  # ]:          0 :         if (r < 0)
     435                 :          0 :                 return r;
     436                 :            : 
     437                 :          0 :         dns_cache_make_space(c, 1);
     438                 :            : 
     439                 :          0 :         i = new0(DnsCacheItem, 1);
     440         [ #  # ]:          0 :         if (!i)
     441                 :          0 :                 return -ENOMEM;
     442                 :            : 
     443                 :          0 :         i->type = DNS_CACHE_POSITIVE;
     444                 :          0 :         i->key = dns_resource_key_ref(rr->key);
     445                 :          0 :         i->rr = dns_resource_record_ref(rr);
     446                 :          0 :         i->until = calculate_until(rr, (uint32_t) -1, timestamp, false);
     447                 :          0 :         i->authenticated = authenticated;
     448                 :          0 :         i->shared_owner = shared_owner;
     449                 :          0 :         i->ifindex = ifindex;
     450                 :          0 :         i->owner_family = owner_family;
     451                 :          0 :         i->owner_address = *owner_address;
     452                 :          0 :         i->prioq_idx = PRIOQ_IDX_NULL;
     453                 :            : 
     454                 :          0 :         r = dns_cache_link_item(c, i);
     455         [ #  # ]:          0 :         if (r < 0)
     456                 :          0 :                 return r;
     457                 :            : 
     458         [ #  # ]:          0 :         if (DEBUG_LOGGING) {
     459                 :          0 :                 _cleanup_free_ char *t = NULL;
     460                 :            :                 char ifname[IF_NAMESIZE + 1];
     461                 :            : 
     462                 :          0 :                 (void) in_addr_to_string(i->owner_family, &i->owner_address, &t);
     463                 :            : 
     464   [ #  #  #  #  :          0 :                 log_debug("Added positive %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s",
             #  #  #  # ]
     465                 :            :                           i->authenticated ? "authenticated" : "unauthenticated",
     466                 :            :                           i->shared_owner ? " shared" : "",
     467                 :            :                           dns_resource_key_to_string(i->key, key_str, sizeof key_str),
     468                 :            :                           (i->until - timestamp) / USEC_PER_SEC,
     469                 :            :                           i->ifindex == 0 ? "*" : strna(format_ifname(i->ifindex, ifname)),
     470                 :            :                           af_to_name_short(i->owner_family),
     471                 :            :                           strna(t));
     472                 :            :         }
     473                 :            : 
     474                 :          0 :         i = NULL;
     475                 :          0 :         return 0;
     476                 :            : }
     477                 :            : 
     478                 :          0 : static int dns_cache_put_negative(
     479                 :            :                 DnsCache *c,
     480                 :            :                 DnsResourceKey *key,
     481                 :            :                 int rcode,
     482                 :            :                 bool authenticated,
     483                 :            :                 uint32_t nsec_ttl,
     484                 :            :                 usec_t timestamp,
     485                 :            :                 DnsResourceRecord *soa,
     486                 :            :                 int owner_family,
     487                 :            :                 const union in_addr_union *owner_address) {
     488                 :            : 
     489                 :          0 :         _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
     490                 :            :         char key_str[DNS_RESOURCE_KEY_STRING_MAX];
     491                 :            :         int r;
     492                 :            : 
     493         [ #  # ]:          0 :         assert(c);
     494         [ #  # ]:          0 :         assert(key);
     495         [ #  # ]:          0 :         assert(owner_address);
     496                 :            : 
     497                 :            :         /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
     498                 :            :          * important to filter out as we use this as a pseudo-type for
     499                 :            :          * NXDOMAIN entries */
     500         [ #  # ]:          0 :         if (dns_class_is_pseudo(key->class))
     501                 :          0 :                 return 0;
     502         [ #  # ]:          0 :         if (dns_type_is_pseudo(key->type))
     503                 :          0 :                 return 0;
     504                 :            : 
     505   [ #  #  #  # ]:          0 :         if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
     506         [ #  # ]:          0 :                 if (!soa)
     507                 :          0 :                         return 0;
     508                 :            : 
     509                 :            :                 /* For negative replies, check if we have a TTL of a SOA */
     510   [ #  #  #  #  :          0 :                 if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
                   #  # ]
     511         [ #  # ]:          0 :                         log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
     512                 :            :                                   dns_resource_key_to_string(key, key_str, sizeof key_str));
     513                 :          0 :                         return 0;
     514                 :            :                 }
     515         [ #  # ]:          0 :         } else if (rcode != DNS_RCODE_SERVFAIL)
     516                 :          0 :                 return 0;
     517                 :            : 
     518                 :          0 :         r = dns_cache_init(c);
     519         [ #  # ]:          0 :         if (r < 0)
     520                 :          0 :                 return r;
     521                 :            : 
     522                 :          0 :         dns_cache_make_space(c, 1);
     523                 :            : 
     524                 :          0 :         i = new0(DnsCacheItem, 1);
     525         [ #  # ]:          0 :         if (!i)
     526                 :          0 :                 return -ENOMEM;
     527                 :            : 
     528                 :          0 :         i->type =
     529   [ #  #  #  # ]:          0 :                 rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
     530                 :            :                 rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE;
     531                 :          0 :         i->until =
     532         [ #  # ]:          0 :                 i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
     533                 :          0 :                 calculate_until(soa, nsec_ttl, timestamp, true);
     534                 :          0 :         i->authenticated = authenticated;
     535                 :          0 :         i->owner_family = owner_family;
     536                 :          0 :         i->owner_address = *owner_address;
     537                 :          0 :         i->prioq_idx = PRIOQ_IDX_NULL;
     538                 :          0 :         i->rcode = rcode;
     539                 :            : 
     540         [ #  # ]:          0 :         if (i->type == DNS_CACHE_NXDOMAIN) {
     541                 :            :                 /* NXDOMAIN entries should apply equally to all types, so we use ANY as
     542                 :            :                  * a pseudo type for this purpose here. */
     543                 :          0 :                 i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key));
     544         [ #  # ]:          0 :                 if (!i->key)
     545                 :          0 :                         return -ENOMEM;
     546                 :            : 
     547                 :            :                 /* Make sure to remove any previous entry for this
     548                 :            :                  * specific ANY key. (For non-ANY keys the cache data
     549                 :            :                  * is already cleared by the caller.) Note that we
     550                 :            :                  * don't bother removing positive or NODATA cache
     551                 :            :                  * items in this case, because it would either be slow
     552                 :            :                  * or require explicit indexing by name */
     553                 :          0 :                 dns_cache_remove_by_key(c, key);
     554                 :            :         } else
     555                 :          0 :                 i->key = dns_resource_key_ref(key);
     556                 :            : 
     557                 :          0 :         r = dns_cache_link_item(c, i);
     558         [ #  # ]:          0 :         if (r < 0)
     559                 :          0 :                 return r;
     560                 :            : 
     561         [ #  # ]:          0 :         log_debug("Added %s cache entry for %s "USEC_FMT"s",
     562                 :            :                   dns_cache_item_type_to_string(i),
     563                 :            :                   dns_resource_key_to_string(i->key, key_str, sizeof key_str),
     564                 :            :                   (i->until - timestamp) / USEC_PER_SEC);
     565                 :            : 
     566                 :          0 :         i = NULL;
     567                 :          0 :         return 0;
     568                 :            : }
     569                 :            : 
     570                 :          0 : static void dns_cache_remove_previous(
     571                 :            :                 DnsCache *c,
     572                 :            :                 DnsResourceKey *key,
     573                 :            :                 DnsAnswer *answer) {
     574                 :            : 
     575                 :            :         DnsResourceRecord *rr;
     576                 :            :         DnsAnswerFlags flags;
     577                 :            : 
     578         [ #  # ]:          0 :         assert(c);
     579                 :            : 
     580                 :            :         /* First, if we were passed a key (i.e. on LLMNR/DNS, but
     581                 :            :          * not on mDNS), delete all matching old RRs, so that we only
     582                 :            :          * keep complete by_key in place. */
     583         [ #  # ]:          0 :         if (key)
     584                 :          0 :                 dns_cache_remove_by_key(c, key);
     585                 :            : 
     586                 :            :         /* Second, flush all entries matching the answer, unless this
     587                 :            :          * is an RR that is explicitly marked to be "shared" between
     588                 :            :          * peers (i.e. mDNS RRs without the flush-cache bit set). */
     589   [ #  #  #  #  :          0 :         DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     590         [ #  # ]:          0 :                 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
     591                 :          0 :                         continue;
     592                 :            : 
     593         [ #  # ]:          0 :                 if (flags & DNS_ANSWER_SHARED_OWNER)
     594                 :          0 :                         continue;
     595                 :            : 
     596                 :          0 :                 dns_cache_remove_by_key(c, rr->key);
     597                 :            :         }
     598                 :          0 : }
     599                 :            : 
     600                 :          0 : static bool rr_eligible(DnsResourceRecord *rr) {
     601         [ #  # ]:          0 :         assert(rr);
     602                 :            : 
     603                 :            :         /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
     604                 :            :          * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
     605                 :            :          * existence from any cached NSEC/NSEC3, but that should be fine. */
     606                 :            : 
     607      [ #  #  # ]:          0 :         switch (rr->key->type) {
     608                 :            : 
     609                 :          0 :         case DNS_TYPE_NSEC:
     610   [ #  #  #  # ]:          0 :                 return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
     611                 :          0 :                         bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
     612                 :            : 
     613                 :          0 :         case DNS_TYPE_NSEC3:
     614   [ #  #  #  # ]:          0 :                 return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
     615                 :          0 :                         bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
     616                 :            : 
     617                 :          0 :         default:
     618                 :          0 :                 return true;
     619                 :            :         }
     620                 :            : }
     621                 :            : 
     622                 :          0 : int dns_cache_put(
     623                 :            :                 DnsCache *c,
     624                 :            :                 DnsCacheMode cache_mode,
     625                 :            :                 DnsResourceKey *key,
     626                 :            :                 int rcode,
     627                 :            :                 DnsAnswer *answer,
     628                 :            :                 bool authenticated,
     629                 :            :                 uint32_t nsec_ttl,
     630                 :            :                 usec_t timestamp,
     631                 :            :                 int owner_family,
     632                 :            :                 const union in_addr_union *owner_address) {
     633                 :            : 
     634                 :          0 :         DnsResourceRecord *soa = NULL, *rr;
     635                 :          0 :         bool weird_rcode = false;
     636                 :            :         DnsAnswerFlags flags;
     637                 :            :         unsigned cache_keys;
     638                 :            :         int r, ifindex;
     639                 :            : 
     640         [ #  # ]:          0 :         assert(c);
     641         [ #  # ]:          0 :         assert(owner_address);
     642                 :            : 
     643                 :          0 :         dns_cache_remove_previous(c, key, answer);
     644                 :            : 
     645                 :            :         /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
     646                 :            :          * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
     647                 :            :          * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
     648                 :            :          * short time.) */
     649                 :            : 
     650   [ #  #  #  # ]:          0 :         if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
     651         [ #  # ]:          0 :                 if (dns_answer_size(answer) <= 0) {
     652         [ #  # ]:          0 :                         if (key) {
     653                 :            :                                 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
     654                 :            : 
     655         [ #  # ]:          0 :                                 log_debug("Not caching negative entry without a SOA record: %s",
     656                 :            :                                           dns_resource_key_to_string(key, key_str, sizeof key_str));
     657                 :            :                         }
     658                 :          0 :                         return 0;
     659                 :            :                 }
     660                 :            : 
     661                 :            :         } else {
     662                 :            :                 /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
     663                 :            :                  * beneficial. */
     664         [ #  # ]:          0 :                 if (rcode != DNS_RCODE_SERVFAIL)
     665                 :          0 :                         return 0;
     666                 :            : 
     667                 :          0 :                 weird_rcode = true;
     668                 :            :         }
     669                 :            : 
     670                 :          0 :         cache_keys = dns_answer_size(answer);
     671         [ #  # ]:          0 :         if (key)
     672                 :          0 :                 cache_keys++;
     673                 :            : 
     674                 :            :         /* Make some space for our new entries */
     675                 :          0 :         dns_cache_make_space(c, cache_keys);
     676                 :            : 
     677         [ #  # ]:          0 :         if (timestamp <= 0)
     678                 :          0 :                 timestamp = now(clock_boottime_or_monotonic());
     679                 :            : 
     680                 :            :         /* Second, add in positive entries for all contained RRs */
     681   [ #  #  #  #  :          0 :         DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
     682         [ #  # ]:          0 :                 if ((flags & DNS_ANSWER_CACHEABLE) == 0 ||
     683         [ #  # ]:          0 :                     !rr_eligible(rr))
     684                 :          0 :                         continue;
     685                 :            : 
     686                 :          0 :                 r = dns_cache_put_positive(
     687                 :            :                                 c,
     688                 :            :                                 rr,
     689                 :          0 :                                 flags & DNS_ANSWER_AUTHENTICATED,
     690                 :          0 :                                 flags & DNS_ANSWER_SHARED_OWNER,
     691                 :            :                                 timestamp,
     692                 :            :                                 ifindex,
     693                 :            :                                 owner_family, owner_address);
     694         [ #  # ]:          0 :                 if (r < 0)
     695                 :          0 :                         goto fail;
     696                 :            :         }
     697                 :            : 
     698         [ #  # ]:          0 :         if (!key) /* mDNS doesn't know negative caching, really */
     699                 :          0 :                 return 0;
     700                 :            : 
     701                 :            :         /* Third, add in negative entries if the key has no RR */
     702                 :          0 :         r = dns_answer_match_key(answer, key, NULL);
     703         [ #  # ]:          0 :         if (r < 0)
     704                 :          0 :                 goto fail;
     705         [ #  # ]:          0 :         if (r > 0)
     706                 :          0 :                 return 0;
     707                 :            : 
     708                 :            :         /* But not if it has a matching CNAME/DNAME (the negative
     709                 :            :          * caching will be done on the canonical name, not on the
     710                 :            :          * alias) */
     711                 :          0 :         r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
     712         [ #  # ]:          0 :         if (r < 0)
     713                 :          0 :                 goto fail;
     714         [ #  # ]:          0 :         if (r > 0)
     715                 :          0 :                 return 0;
     716                 :            : 
     717                 :            :         /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
     718                 :            :          * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
     719                 :            :          * regardless of a SOA. */
     720                 :          0 :         r = dns_answer_find_soa(answer, key, &soa, &flags);
     721         [ #  # ]:          0 :         if (r < 0)
     722                 :          0 :                 goto fail;
     723   [ #  #  #  # ]:          0 :         if (r == 0 && !weird_rcode)
     724                 :          0 :                 return 0;
     725         [ #  # ]:          0 :         if (r > 0) {
     726                 :            :                 /* Refuse using the SOA data if it is unsigned, but the key is
     727                 :            :                  * signed */
     728   [ #  #  #  # ]:          0 :                 if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0)
     729                 :          0 :                         return 0;
     730                 :            :         }
     731                 :            : 
     732         [ #  # ]:          0 :         if (cache_mode == DNS_CACHE_MODE_NO_NEGATIVE) {
     733                 :            :                 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
     734         [ #  # ]:          0 :                 log_debug("Not caching negative entry for: %s, cache mode set to no-negative",
     735                 :            :                         dns_resource_key_to_string(key, key_str, sizeof key_str));
     736                 :          0 :                 return 0;
     737                 :            :         }
     738                 :            : 
     739                 :          0 :         r = dns_cache_put_negative(
     740                 :            :                         c,
     741                 :            :                         key,
     742                 :            :                         rcode,
     743                 :            :                         authenticated,
     744                 :            :                         nsec_ttl,
     745                 :            :                         timestamp,
     746                 :            :                         soa,
     747                 :            :                         owner_family, owner_address);
     748         [ #  # ]:          0 :         if (r < 0)
     749                 :          0 :                 goto fail;
     750                 :            : 
     751                 :          0 :         return 0;
     752                 :            : 
     753                 :          0 : fail:
     754                 :            :         /* Adding all RRs failed. Let's clean up what we already
     755                 :            :          * added, just in case */
     756                 :            : 
     757         [ #  # ]:          0 :         if (key)
     758                 :          0 :                 dns_cache_remove_by_key(c, key);
     759                 :            : 
     760   [ #  #  #  #  :          0 :         DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     761         [ #  # ]:          0 :                 if ((flags & DNS_ANSWER_CACHEABLE) == 0)
     762                 :          0 :                         continue;
     763                 :            : 
     764                 :          0 :                 dns_cache_remove_by_key(c, rr->key);
     765                 :            :         }
     766                 :            : 
     767                 :          0 :         return r;
     768                 :            : }
     769                 :            : 
     770                 :          0 : static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
     771                 :            :         DnsCacheItem *i;
     772                 :            :         const char *n;
     773                 :            :         int r;
     774                 :            : 
     775         [ #  # ]:          0 :         assert(c);
     776         [ #  # ]:          0 :         assert(k);
     777                 :            : 
     778                 :            :         /* If we hit some OOM error, or suchlike, we don't care too
     779                 :            :          * much, after all this is just a cache */
     780                 :            : 
     781                 :          0 :         i = hashmap_get(c->by_key, k);
     782         [ #  # ]:          0 :         if (i)
     783                 :          0 :                 return i;
     784                 :            : 
     785                 :          0 :         n = dns_resource_key_name(k);
     786                 :            : 
     787                 :            :         /* Check if we have an NXDOMAIN cache item for the name, notice that we use
     788                 :            :          * the pseudo-type ANY for NXDOMAIN cache items. */
     789                 :          0 :         i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
     790   [ #  #  #  # ]:          0 :         if (i && i->type == DNS_CACHE_NXDOMAIN)
     791                 :          0 :                 return i;
     792                 :            : 
     793         [ #  # ]:          0 :         if (dns_type_may_redirect(k->type)) {
     794                 :            :                 /* Check if we have a CNAME record instead */
     795                 :          0 :                 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
     796   [ #  #  #  # ]:          0 :                 if (i && i->type != DNS_CACHE_NODATA)
     797                 :          0 :                         return i;
     798                 :            : 
     799                 :            :                 /* OK, let's look for cached DNAME records. */
     800                 :            :                 for (;;) {
     801         [ #  # ]:          0 :                         if (isempty(n))
     802                 :          0 :                                 return NULL;
     803                 :            : 
     804                 :          0 :                         i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
     805   [ #  #  #  # ]:          0 :                         if (i && i->type != DNS_CACHE_NODATA)
     806                 :          0 :                                 return i;
     807                 :            : 
     808                 :            :                         /* Jump one label ahead */
     809                 :          0 :                         r = dns_name_parent(&n);
     810         [ #  # ]:          0 :                         if (r <= 0)
     811                 :          0 :                                 return NULL;
     812                 :            :                 }
     813                 :            :         }
     814                 :            : 
     815         [ #  # ]:          0 :         if (k->type != DNS_TYPE_NSEC) {
     816                 :            :                 /* Check if we have an NSEC record instead for the name. */
     817                 :          0 :                 i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
     818         [ #  # ]:          0 :                 if (i)
     819                 :          0 :                         return i;
     820                 :            :         }
     821                 :            : 
     822                 :          0 :         return NULL;
     823                 :            : }
     824                 :            : 
     825                 :          0 : int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **ret, bool *authenticated) {
     826                 :          0 :         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
     827                 :            :         char key_str[DNS_RESOURCE_KEY_STRING_MAX];
     828                 :          0 :         unsigned n = 0;
     829                 :            :         int r;
     830                 :          0 :         bool nxdomain = false;
     831                 :          0 :         DnsCacheItem *j, *first, *nsec = NULL;
     832                 :          0 :         bool have_authenticated = false, have_non_authenticated = false;
     833                 :            :         usec_t current;
     834                 :          0 :         int found_rcode = -1;
     835                 :            : 
     836         [ #  # ]:          0 :         assert(c);
     837         [ #  # ]:          0 :         assert(key);
     838         [ #  # ]:          0 :         assert(rcode);
     839         [ #  # ]:          0 :         assert(ret);
     840         [ #  # ]:          0 :         assert(authenticated);
     841                 :            : 
     842   [ #  #  #  # ]:          0 :         if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
     843                 :            :                 /* If we have ANY lookups we don't use the cache, so
     844                 :            :                  * that the caller refreshes via the network. */
     845                 :            : 
     846         [ #  # ]:          0 :                 log_debug("Ignoring cache for ANY lookup: %s",
     847                 :            :                           dns_resource_key_to_string(key, key_str, sizeof key_str));
     848                 :            : 
     849                 :          0 :                 c->n_miss++;
     850                 :            : 
     851                 :          0 :                 *ret = NULL;
     852                 :          0 :                 *rcode = DNS_RCODE_SUCCESS;
     853                 :          0 :                 *authenticated = false;
     854                 :            : 
     855                 :          0 :                 return 0;
     856                 :            :         }
     857                 :            : 
     858                 :          0 :         first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
     859         [ #  # ]:          0 :         if (!first) {
     860                 :            :                 /* If one question cannot be answered we need to refresh */
     861                 :            : 
     862         [ #  # ]:          0 :                 log_debug("Cache miss for %s",
     863                 :            :                           dns_resource_key_to_string(key, key_str, sizeof key_str));
     864                 :            : 
     865                 :          0 :                 c->n_miss++;
     866                 :            : 
     867                 :          0 :                 *ret = NULL;
     868                 :          0 :                 *rcode = DNS_RCODE_SUCCESS;
     869                 :          0 :                 *authenticated = false;
     870                 :            : 
     871                 :          0 :                 return 0;
     872                 :            :         }
     873                 :            : 
     874         [ #  # ]:          0 :         LIST_FOREACH(by_key, j, first) {
     875         [ #  # ]:          0 :                 if (j->rr) {
     876         [ #  # ]:          0 :                         if (j->rr->key->type == DNS_TYPE_NSEC)
     877                 :          0 :                                 nsec = j;
     878                 :            : 
     879                 :          0 :                         n++;
     880         [ #  # ]:          0 :                 } else if (j->type == DNS_CACHE_NXDOMAIN)
     881                 :          0 :                         nxdomain = true;
     882         [ #  # ]:          0 :                 else if (j->type == DNS_CACHE_RCODE)
     883                 :          0 :                         found_rcode = j->rcode;
     884                 :            : 
     885         [ #  # ]:          0 :                 if (j->authenticated)
     886                 :          0 :                         have_authenticated = true;
     887                 :            :                 else
     888                 :          0 :                         have_non_authenticated = true;
     889                 :            :         }
     890                 :            : 
     891         [ #  # ]:          0 :         if (found_rcode >= 0) {
     892         [ #  # ]:          0 :                 log_debug("RCODE %s cache hit for %s",
     893                 :            :                           dns_rcode_to_string(found_rcode),
     894                 :            :                           dns_resource_key_to_string(key, key_str, sizeof(key_str)));
     895                 :            : 
     896                 :          0 :                 *ret = NULL;
     897                 :          0 :                 *rcode = found_rcode;
     898                 :          0 :                 *authenticated = false;
     899                 :            : 
     900                 :          0 :                 c->n_hit++;
     901                 :          0 :                 return 1;
     902                 :            :         }
     903                 :            : 
     904   [ #  #  #  #  :          0 :         if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
                   #  # ]
     905                 :            :                 /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
     906                 :            :                  * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
     907                 :            : 
     908         [ #  # ]:          0 :                 log_debug("NSEC NODATA cache hit for %s",
     909                 :            :                           dns_resource_key_to_string(key, key_str, sizeof key_str));
     910                 :            : 
     911                 :            :                 /* We only found an NSEC record that matches our name.
     912                 :            :                  * If it says the type doesn't exist report
     913                 :            :                  * NODATA. Otherwise report a cache miss. */
     914                 :            : 
     915                 :          0 :                 *ret = NULL;
     916                 :          0 :                 *rcode = DNS_RCODE_SUCCESS;
     917                 :          0 :                 *authenticated = nsec->authenticated;
     918                 :            : 
     919         [ #  # ]:          0 :                 if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
     920         [ #  # ]:          0 :                     !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
     921         [ #  # ]:          0 :                     !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
     922                 :          0 :                         c->n_hit++;
     923                 :          0 :                         return 1;
     924                 :            :                 }
     925                 :            : 
     926                 :          0 :                 c->n_miss++;
     927                 :          0 :                 return 0;
     928                 :            :         }
     929                 :            : 
     930   [ #  #  #  #  :          0 :         log_debug("%s cache hit for %s",
                   #  # ]
     931                 :            :                   n > 0    ? "Positive" :
     932                 :            :                   nxdomain ? "NXDOMAIN" : "NODATA",
     933                 :            :                   dns_resource_key_to_string(key, key_str, sizeof key_str));
     934                 :            : 
     935         [ #  # ]:          0 :         if (n <= 0) {
     936                 :          0 :                 c->n_hit++;
     937                 :            : 
     938                 :          0 :                 *ret = NULL;
     939         [ #  # ]:          0 :                 *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
     940   [ #  #  #  # ]:          0 :                 *authenticated = have_authenticated && !have_non_authenticated;
     941                 :          0 :                 return 1;
     942                 :            :         }
     943                 :            : 
     944                 :          0 :         answer = dns_answer_new(n);
     945         [ #  # ]:          0 :         if (!answer)
     946                 :          0 :                 return -ENOMEM;
     947                 :            : 
     948         [ #  # ]:          0 :         if (clamp_ttl)
     949                 :          0 :                 current = now(clock_boottime_or_monotonic());
     950                 :            : 
     951         [ #  # ]:          0 :         LIST_FOREACH(by_key, j, first) {
     952      [ #  #  # ]:          0 :                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
     953                 :            : 
     954         [ #  # ]:          0 :                 if (!j->rr)
     955                 :          0 :                         continue;
     956                 :            : 
     957         [ #  # ]:          0 :                 if (clamp_ttl) {
     958                 :          0 :                         rr = dns_resource_record_ref(j->rr);
     959                 :            : 
     960         [ #  # ]:          0 :                         r = dns_resource_record_clamp_ttl(&rr, LESS_BY(j->until, current) / USEC_PER_SEC);
     961         [ #  # ]:          0 :                         if (r < 0)
     962                 :          0 :                                 return r;
     963                 :            :                 }
     964                 :            : 
     965         [ #  # ]:          0 :                 r = dns_answer_add(answer, rr ?: j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0);
     966         [ #  # ]:          0 :                 if (r < 0)
     967                 :          0 :                         return r;
     968                 :            :         }
     969                 :            : 
     970                 :          0 :         c->n_hit++;
     971                 :            : 
     972                 :          0 :         *ret = answer;
     973                 :          0 :         *rcode = DNS_RCODE_SUCCESS;
     974   [ #  #  #  # ]:          0 :         *authenticated = have_authenticated && !have_non_authenticated;
     975                 :          0 :         answer = NULL;
     976                 :            : 
     977                 :          0 :         return n;
     978                 :            : }
     979                 :            : 
     980                 :          0 : int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
     981                 :            :         DnsCacheItem *i, *first;
     982                 :          0 :         bool same_owner = true;
     983                 :            : 
     984         [ #  # ]:          0 :         assert(cache);
     985         [ #  # ]:          0 :         assert(rr);
     986                 :            : 
     987                 :          0 :         dns_cache_prune(cache);
     988                 :            : 
     989                 :            :         /* See if there's a cache entry for the same key. If there
     990                 :            :          * isn't there's no conflict */
     991                 :          0 :         first = hashmap_get(cache->by_key, rr->key);
     992         [ #  # ]:          0 :         if (!first)
     993                 :          0 :                 return 0;
     994                 :            : 
     995                 :            :         /* See if the RR key is owned by the same owner, if so, there
     996                 :            :          * isn't a conflict either */
     997         [ #  # ]:          0 :         LIST_FOREACH(by_key, i, first) {
     998   [ #  #  #  # ]:          0 :                 if (i->owner_family != owner_family ||
     999                 :          0 :                     !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
    1000                 :          0 :                         same_owner = false;
    1001                 :          0 :                         break;
    1002                 :            :                 }
    1003                 :            :         }
    1004         [ #  # ]:          0 :         if (same_owner)
    1005                 :          0 :                 return 0;
    1006                 :            : 
    1007                 :            :         /* See if there's the exact same RR in the cache. If yes, then
    1008                 :            :          * there's no conflict. */
    1009         [ #  # ]:          0 :         if (dns_cache_get(cache, rr))
    1010                 :          0 :                 return 0;
    1011                 :            : 
    1012                 :            :         /* There's a conflict */
    1013                 :          0 :         return 1;
    1014                 :            : }
    1015                 :            : 
    1016                 :          0 : int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
    1017                 :          0 :         unsigned ancount = 0;
    1018                 :            :         Iterator iterator;
    1019                 :            :         DnsCacheItem *i;
    1020                 :            :         int r;
    1021                 :            : 
    1022         [ #  # ]:          0 :         assert(cache);
    1023         [ #  # ]:          0 :         assert(p);
    1024                 :            : 
    1025         [ #  # ]:          0 :         HASHMAP_FOREACH(i, cache->by_key, iterator) {
    1026                 :            :                 DnsCacheItem *j;
    1027                 :            : 
    1028         [ #  # ]:          0 :                 LIST_FOREACH(by_key, j, i) {
    1029         [ #  # ]:          0 :                         if (!j->rr)
    1030                 :          0 :                                 continue;
    1031                 :            : 
    1032         [ #  # ]:          0 :                         if (!j->shared_owner)
    1033                 :          0 :                                 continue;
    1034                 :            : 
    1035                 :          0 :                         r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
    1036   [ #  #  #  # ]:          0 :                         if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
    1037                 :            :                                 /* For mDNS, if we're unable to stuff all known answers into the given packet,
    1038                 :            :                                  * allocate a new one, push the RR into that one and link it to the current one.
    1039                 :            :                                  */
    1040                 :            : 
    1041                 :          0 :                                 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
    1042                 :          0 :                                 ancount = 0;
    1043                 :            : 
    1044                 :          0 :                                 r = dns_packet_new_query(&p->more, p->protocol, 0, true);
    1045         [ #  # ]:          0 :                                 if (r < 0)
    1046                 :          0 :                                         return r;
    1047                 :            : 
    1048                 :            :                                 /* continue with new packet */
    1049                 :          0 :                                 p = p->more;
    1050                 :          0 :                                 r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
    1051                 :            :                         }
    1052                 :            : 
    1053         [ #  # ]:          0 :                         if (r < 0)
    1054                 :          0 :                                 return r;
    1055                 :            : 
    1056                 :          0 :                         ancount++;
    1057                 :            :                 }
    1058                 :            :         }
    1059                 :            : 
    1060                 :          0 :         DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
    1061                 :            : 
    1062                 :          0 :         return 0;
    1063                 :            : }
    1064                 :            : 
    1065                 :          0 : void dns_cache_dump(DnsCache *cache, FILE *f) {
    1066                 :            :         Iterator iterator;
    1067                 :            :         DnsCacheItem *i;
    1068                 :            : 
    1069         [ #  # ]:          0 :         if (!cache)
    1070                 :          0 :                 return;
    1071                 :            : 
    1072         [ #  # ]:          0 :         if (!f)
    1073                 :          0 :                 f = stdout;
    1074                 :            : 
    1075         [ #  # ]:          0 :         HASHMAP_FOREACH(i, cache->by_key, iterator) {
    1076                 :            :                 DnsCacheItem *j;
    1077                 :            : 
    1078         [ #  # ]:          0 :                 LIST_FOREACH(by_key, j, i) {
    1079                 :            : 
    1080                 :          0 :                         fputc('\t', f);
    1081                 :            : 
    1082         [ #  # ]:          0 :                         if (j->rr) {
    1083                 :            :                                 const char *t;
    1084                 :          0 :                                 t = dns_resource_record_to_string(j->rr);
    1085         [ #  # ]:          0 :                                 if (!t) {
    1086                 :          0 :                                         log_oom();
    1087                 :          0 :                                         continue;
    1088                 :            :                                 }
    1089                 :            : 
    1090                 :          0 :                                 fputs(t, f);
    1091                 :          0 :                                 fputc('\n', f);
    1092                 :            :                         } else {
    1093                 :            :                                 char key_str[DNS_RESOURCE_KEY_STRING_MAX];
    1094                 :            : 
    1095                 :          0 :                                 fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
    1096                 :          0 :                                 fputs(" -- ", f);
    1097                 :          0 :                                 fputs(dns_cache_item_type_to_string(j), f);
    1098                 :          0 :                                 fputc('\n', f);
    1099                 :            :                         }
    1100                 :            :                 }
    1101                 :            :         }
    1102                 :            : }
    1103                 :            : 
    1104                 :          0 : bool dns_cache_is_empty(DnsCache *cache) {
    1105         [ #  # ]:          0 :         if (!cache)
    1106                 :          0 :                 return true;
    1107                 :            : 
    1108                 :          0 :         return hashmap_isempty(cache->by_key);
    1109                 :            : }
    1110                 :            : 
    1111                 :          0 : unsigned dns_cache_size(DnsCache *cache) {
    1112         [ #  # ]:          0 :         if (!cache)
    1113                 :          0 :                 return 0;
    1114                 :            : 
    1115                 :          0 :         return hashmap_size(cache->by_key);
    1116                 :            : }

Generated by: LCOV version 1.14