• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.18
/src/resolve/resolved-dns-cache.c
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-ifname.h"
9
#include "resolved-dns-answer.h"
10
#include "resolved-dns-cache.h"
11
#include "resolved-dns-dnssec.h"
12
#include "resolved-dns-packet.h"
13
#include "resolved-dns-rr.h"
14
#include "string-util.h"
15

16
/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
17
 * leave DNS caches unbounded, but that's crazy. */
18
#define CACHE_MAX 4096
19

20
/* We never keep any item longer than 2h in our cache unless StaleRetentionSec is greater than zero. */
21
#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR)
22

23
/* The max TTL for stale data is set to 30 seconds. See RFC 8767, Section 6. */
24
#define CACHE_STALE_TTL_MAX_USEC (30 * USEC_PER_SEC)
25

26
/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for
27
 * now) */
28
#define CACHE_TTL_STRANGE_RCODE_USEC (10 * USEC_PER_SEC)
29

30
#define CACHEABLE_QUERY_FLAGS (SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL)
31

32
typedef enum DnsCacheItemType DnsCacheItemType;
33
typedef struct DnsCacheItem DnsCacheItem;
34

35
enum DnsCacheItemType {
36
        DNS_CACHE_POSITIVE,
37
        DNS_CACHE_NODATA,
38
        DNS_CACHE_NXDOMAIN,
39
        DNS_CACHE_RCODE,      /* "strange" RCODE (effective only SERVFAIL for now) */
40
};
41

42
struct DnsCacheItem {
43
        DnsCacheItemType type;
44
        int rcode;
45
        DnsResourceKey *key;     /* The key for this item, i.e. the lookup key */
46
        DnsResourceRecord *rr;   /* The RR for this item, i.e. the lookup value for positive queries */
47
        DnsAnswer *answer;       /* The full validated answer, if this is an RRset acquired via a "primary" lookup */
48
        DnsPacket *full_packet;  /* The full packet this information was acquired with */
49

50
        usec_t until;            /* If StaleRetentionSec is greater than zero, until is set to a duration of StaleRetentionSec from the time of TTL expiry. If StaleRetentionSec is zero, both until and until_valid will be set to ttl. */
51
        usec_t until_valid;      /* The key is for storing the time when the TTL set to expire. */
52
        uint64_t query_flags;    /* SD_RESOLVED_AUTHENTICATED and/or SD_RESOLVED_CONFIDENTIAL */
53
        DnssecResult dnssec_result;
54

55
        int ifindex;
56
        int owner_family;
57
        union in_addr_union owner_address;
58

59
        unsigned prioq_idx;
60
        LIST_FIELDS(DnsCacheItem, by_key);
61

62
        bool shared_owner;
63
};
64

65
/* Returns true if this is a cache item created as result of an explicit lookup, or created as "side-effect"
66
 * of another request. "Primary" entries will carry the full answer data (with NSEC, …) that can aso prove
67
 * wildcard expansion, non-existence and such, while entries that were created as "side-effect" just contain
68
 * immediate RR data for the specified RR key, but nothing else. */
69
#define DNS_CACHE_ITEM_IS_PRIMARY(item) (!!(item)->answer)
70

71
static const char *dns_cache_item_type_to_string(DnsCacheItem *item) {
58✔
72
        assert(item);
58✔
73

74
        switch (item->type) {
58✔
75

76
        case DNS_CACHE_POSITIVE:
77
                return "POSITIVE";
78

79
        case DNS_CACHE_NODATA:
14✔
80
                return "NODATA";
14✔
81

82
        case DNS_CACHE_NXDOMAIN:
11✔
83
                return "NXDOMAIN";
11✔
84

85
        case DNS_CACHE_RCODE:
33✔
86
                return dns_rcode_to_string(item->rcode);
33✔
87
        }
88

89
        return NULL;
×
90
}
91

92
static DnsCacheItem* dns_cache_item_free(DnsCacheItem *i) {
233✔
93
        if (!i)
233✔
94
                return NULL;
95

96
        dns_resource_record_unref(i->rr);
233✔
97
        dns_resource_key_unref(i->key);
233✔
98
        dns_answer_unref(i->answer);
233✔
99
        dns_packet_unref(i->full_packet);
233✔
100
        return mfree(i);
233✔
101
}
102
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
235✔
103

104
static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) {
1✔
105
        DnsCacheItem *first;
1✔
106

107
        assert(c);
1✔
108

109
        if (!i)
1✔
110
                return;
1✔
111

112
        first = hashmap_get(c->by_key, i->key);
1✔
113
        LIST_REMOVE(by_key, first, i);
1✔
114

115
        if (first)
1✔
116
                assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
×
117
        else
118
                hashmap_remove(c->by_key, i->key);
1✔
119

120
        prioq_remove(c->by_expiry, i, &i->prioq_idx);
1✔
121

122
        dns_cache_item_free(i);
1✔
123
}
124

125
static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) {
3✔
126
        DnsCacheItem *first;
3✔
127
        int r;
3✔
128

129
        first = hashmap_get(c->by_key, rr->key);
3✔
130
        LIST_FOREACH(by_key, i, first) {
3✔
131
                r = dns_resource_record_equal(i->rr, rr);
1✔
132
                if (r < 0)
1✔
133
                        return r;
134
                if (r > 0) {
1✔
135
                        dns_cache_item_unlink_and_free(c, i);
1✔
136
                        return true;
1✔
137
                }
138
        }
139

140
        return false;
141
}
142

143
static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) {
547✔
144
        DnsCacheItem *first;
547✔
145

146
        assert(c);
547✔
147
        assert(key);
547✔
148

149
        first = hashmap_remove(c->by_key, key);
547✔
150
        if (!first)
547✔
151
                return false;
152

153
        LIST_FOREACH(by_key, i, first) {
441✔
154
                prioq_remove(c->by_expiry, i, &i->prioq_idx);
232✔
155
                dns_cache_item_free(i);
232✔
156
        }
157

158
        return true;
159
}
160

161
void dns_cache_flush(DnsCache *c) {
10,772✔
162
        DnsResourceKey *key;
10,772✔
163

164
        assert(c);
10,772✔
165

166
        while ((key = hashmap_first_key(c->by_key)))
10,956✔
167
                dns_cache_remove_by_key(c, key);
184✔
168

169
        assert(hashmap_isempty(c->by_key));
10,772✔
170
        assert(prioq_isempty(c->by_expiry));
10,772✔
171

172
        c->by_key = hashmap_free(c->by_key);
10,772✔
173
        c->by_expiry = prioq_free(c->by_expiry);
10,772✔
174
}
10,772✔
175

176
static void dns_cache_make_space(DnsCache *c, unsigned add) {
391✔
177
        assert(c);
391✔
178

179
        if (add <= 0)
391✔
180
                return;
181

182
        /* Makes space for n new entries. Note that we actually allow
183
         * the cache to grow beyond CACHE_MAX, but only when we shall
184
         * add more RRs to the cache than CACHE_MAX at once. In that
185
         * case the cache will be emptied completely otherwise. */
186

187
        for (;;) {
×
188
                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
391✔
189
                DnsCacheItem *i;
391✔
190

191
                if (prioq_isempty(c->by_expiry))
391✔
192
                        break;
193

194
                if (prioq_size(c->by_expiry) + add < CACHE_MAX)
288✔
195
                        break;
196

197
                i = prioq_peek(c->by_expiry);
×
198
                assert(i);
×
199

200
                /* Take an extra reference to the key so that it
201
                 * doesn't go away in the middle of the remove call */
202
                key = dns_resource_key_ref(i->key);
×
203
                dns_cache_remove_by_key(c, key);
×
204
        }
205
}
206

207
void dns_cache_prune(DnsCache *c) {
9,933✔
208
        usec_t t = 0;
9,933✔
209

210
        assert(c);
9,933✔
211

212
        /* Remove all entries that are past their TTL */
213

214
        for (;;) {
3✔
215
                DnsCacheItem *i;
9,936✔
216
                char key_str[DNS_RESOURCE_KEY_STRING_MAX];
9,936✔
217

218
                i = prioq_peek(c->by_expiry);
9,936✔
219
                if (!i)
9,936✔
220
                        break;
221

222
                if (t <= 0)
258✔
223
                        t = now(CLOCK_BOOTTIME);
256✔
224

225
                if (i->until > t)
258✔
226
                        break;
227

228
                /* Depending whether this is an mDNS shared entry
229
                 * either remove only this one RR or the whole RRset */
230
                log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)",
6✔
231
                          i->shared_owner ? "shared " : "",
232
                          dns_resource_key_to_string(i->key, key_str, sizeof key_str),
233
                          (t - i->until) / USEC_PER_SEC);
234

235
                if (i->shared_owner)
3✔
236
                        dns_cache_item_unlink_and_free(c, i);
×
237
                else {
238
                        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
3✔
239

240
                        /* Take an extra reference to the key so that it
241
                         * doesn't go away in the middle of the remove call */
242
                        key = dns_resource_key_ref(i->key);
3✔
243
                        dns_cache_remove_by_key(c, key);
3✔
244
                }
245
        }
246
}
9,933✔
247

248
bool dns_cache_expiry_in_one_second(DnsCache *c, usec_t t) {
3✔
249
        DnsCacheItem *i;
3✔
250

251
        assert(c);
3✔
252

253
        /* Check if any items expire within the next second */
254
        i = prioq_peek(c->by_expiry);
3✔
255
        if (!i)
3✔
256
                return false;
257

258
        if (i->until <= usec_add(t, USEC_PER_SEC))
2✔
259
                return true;
2✔
260

261
        return false;
262
}
263

264
static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
589✔
265
        const DnsCacheItem *x = a, *y = b;
589✔
266

267
        return CMP(x->until, y->until);
589✔
268
}
269

270
static int dns_cache_init(DnsCache *c) {
233✔
271
        int r;
233✔
272

273
        assert(c);
233✔
274

275
        r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func);
233✔
276
        if (r < 0)
233✔
277
                return r;
278

279
        r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops);
233✔
280
        if (r < 0)
233✔
281
                return r;
233✔
282

283
        return r;
284
}
285

286
static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) {
233✔
287
        DnsCacheItem *first;
233✔
288
        int r;
233✔
289

290
        assert(c);
233✔
291
        assert(i);
233✔
292

293
        r = prioq_put(c->by_expiry, i, &i->prioq_idx);
233✔
294
        if (r < 0)
233✔
295
                return r;
233✔
296

297
        first = hashmap_get(c->by_key, i->key);
233✔
298
        if (first) {
233✔
299
                _unused_ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL;
×
300

301
                /* Keep a reference to the original key, while we manipulate the list. */
302
                k = dns_resource_key_ref(first->key);
23✔
303

304
                /* Now, try to reduce the number of keys we keep */
305
                dns_resource_key_reduce(&first->key, &i->key);
23✔
306

307
                if (first->rr)
23✔
308
                        dns_resource_key_reduce(&first->rr->key, &i->key);
22✔
309
                if (i->rr)
23✔
310
                        dns_resource_key_reduce(&i->rr->key, &i->key);
22✔
311

312
                LIST_PREPEND(by_key, first, i);
23✔
313
                assert_se(hashmap_replace(c->by_key, first->key, first) >= 0);
23✔
314
        } else {
315
                r = hashmap_put(c->by_key, i->key, i);
210✔
316
                if (r < 0) {
210✔
317
                        prioq_remove(c->by_expiry, i, &i->prioq_idx);
×
318
                        return r;
×
319
                }
320
        }
321

322
        return 0;
323
}
324

325
static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
204✔
326
        assert(c);
204✔
327
        assert(rr);
204✔
328

329
        LIST_FOREACH(by_key, i, (DnsCacheItem*) hashmap_get(c->by_key, rr->key))
229✔
330
                if (i->rr && dns_resource_record_equal(i->rr, rr) > 0)
26✔
331
                        return i;
332

333
        return NULL;
334
}
335

336
static usec_t calculate_until_valid(
225✔
337
                DnsResourceRecord *rr,
338
                uint32_t min_ttl,
339
                uint32_t nsec_ttl,
340
                usec_t timestamp,
341
                bool use_soa_minimum) {
342

343
        uint32_t ttl;
225✔
344
        usec_t u;
225✔
345

346
        assert(rr);
225✔
347

348
        ttl = MIN(min_ttl, nsec_ttl);
225✔
349
        if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) {
225✔
350
                /* If this is a SOA RR, and it is requested, clamp to the SOA's minimum field. This is used
351
                 * when we do negative caching, to determine the TTL for the negative caching entry. See RFC
352
                 * 2308, Section 5. */
353

354
                if (ttl > rr->soa.minimum)
22✔
355
                        ttl = rr->soa.minimum;
×
356
        }
357

358
        u = ttl * USEC_PER_SEC;
225✔
359
        if (u > CACHE_TTL_MAX_USEC)
225✔
360
                u = CACHE_TTL_MAX_USEC;
162✔
361

362
        if (rr->expiry != USEC_INFINITY) {
225✔
363
                usec_t left;
155✔
364

365
                /* Make use of the DNSSEC RRSIG expiry info, if we have it */
366

367
                left = LESS_BY(rr->expiry, now(CLOCK_REALTIME));
155✔
368
                if (u > left)
155✔
369
                        u = left;
×
370
        }
371

372
        return timestamp + u;
225✔
373
}
374

375
static usec_t calculate_until(
203✔
376
                usec_t until_valid,
377
                usec_t stale_retention_usec) {
378

379
        return stale_retention_usec > 0 ? usec_add(until_valid, stale_retention_usec) : until_valid;
203✔
380
}
381

382
static void dns_cache_item_update_positive(
1✔
383
                DnsCache *c,
384
                DnsCacheItem *i,
385
                DnsResourceRecord *rr,
386
                DnsAnswer *answer,
387
                DnsPacket *full_packet,
388
                uint32_t min_ttl,
389
                uint64_t query_flags,
390
                bool shared_owner,
391
                DnssecResult dnssec_result,
392
                usec_t timestamp,
393
                int ifindex,
394
                int owner_family,
395
                const union in_addr_union *owner_address,
396
                usec_t stale_retention_usec) {
397

398
        assert(c);
1✔
399
        assert(i);
1✔
400
        assert(rr);
1✔
401
        assert(owner_address);
1✔
402

403
        i->type = DNS_CACHE_POSITIVE;
1✔
404

405
        if (!i->by_key_prev)
1✔
406
                /* We are the first item in the list, we need to
407
                 * update the key used in the hashmap */
408

409
                assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0);
1✔
410

411
        DNS_RR_REPLACE(i->rr, dns_resource_record_ref(rr));
1✔
412

413
        DNS_RESOURCE_KEY_REPLACE(i->key, dns_resource_key_ref(rr->key));
1✔
414

415
        DNS_ANSWER_REPLACE(i->answer, dns_answer_ref(answer));
1✔
416

417
        DNS_PACKET_REPLACE(i->full_packet, dns_packet_ref(full_packet));
1✔
418

419
        i->until_valid = calculate_until_valid(rr, min_ttl, UINT32_MAX, timestamp, false);
1✔
420
        i->until = calculate_until(i->until_valid, stale_retention_usec);
1✔
421
        i->query_flags = query_flags & CACHEABLE_QUERY_FLAGS;
1✔
422
        i->shared_owner = shared_owner;
1✔
423
        i->dnssec_result = dnssec_result;
1✔
424

425
        i->ifindex = ifindex;
1✔
426

427
        i->owner_family = owner_family;
1✔
428
        i->owner_address = *owner_address;
1✔
429

430
        prioq_reshuffle(c->by_expiry, i, &i->prioq_idx);
1✔
431
}
1✔
432

433
static int dns_cache_put_positive(
209✔
434
                DnsCache *c,
435
                DnsProtocol protocol,
436
                DnsResourceRecord *rr,
437
                DnsAnswer *answer,
438
                DnsPacket *full_packet,
439
                uint64_t query_flags,
440
                bool shared_owner,
441
                DnssecResult dnssec_result,
442
                usec_t timestamp,
443
                int ifindex,
444
                int owner_family,
445
                const union in_addr_union *owner_address,
446
                usec_t stale_retention_usec) {
447

448
        char key_str[DNS_RESOURCE_KEY_STRING_MAX];
209✔
449
        DnsCacheItem *existing;
209✔
450
        uint32_t min_ttl;
209✔
451
        int r;
209✔
452

453
        assert(c);
209✔
454
        assert(rr);
209✔
455
        assert(owner_address);
209✔
456

457
        /* Never cache pseudo RRs */
458
        if (dns_class_is_pseudo(rr->key->class))
209✔
459
                return 0;
209✔
460
        if (dns_type_is_pseudo(rr->key->type))
208✔
461
                return 0;
462

463
        /* Determine the minimal TTL of all RRs in the answer plus the one by the main RR we are supposed to
464
         * cache. Since we cache whole answers to questions we should never return answers where only some
465
         * RRs are still valid, hence find the lowest here */
466
        min_ttl = MIN(dns_answer_min_ttl(answer), rr->ttl);
206✔
467

468
        /* New TTL is 0? Delete this specific entry... */
469
        if (min_ttl <= 0) {
206✔
470
                r = dns_cache_remove_by_rr(c, rr);
3✔
471
                log_debug("%s: %s",
5✔
472
                          r > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry",
473
                          dns_resource_key_to_string(rr->key, key_str, sizeof key_str));
474
                return 0;
3✔
475
        }
476

477
        /* Entry exists already? Update TTL, timestamp and owner */
478
        existing = dns_cache_get(c, rr);
203✔
479
        if (existing) {
203✔
480
                dns_cache_item_update_positive(
1✔
481
                                c,
482
                                existing,
483
                                rr,
484
                                answer,
485
                                full_packet,
486
                                min_ttl,
487
                                query_flags,
488
                                shared_owner,
489
                                dnssec_result,
490
                                timestamp,
491
                                ifindex,
492
                                owner_family,
493
                                owner_address,
494
                                stale_retention_usec);
495
                return 0;
1✔
496
        }
497

498
        /* Do not cache mDNS goodbye packet. */
499
        if (protocol == DNS_PROTOCOL_MDNS && rr->ttl <= 1)
202✔
500
                return 0;
501

502
        /* Otherwise, add the new RR */
503
        r = dns_cache_init(c);
202✔
504
        if (r < 0)
202✔
505
                return r;
506

507
        dns_cache_make_space(c, 1);
202✔
508

509
        _cleanup_(dns_cache_item_freep) DnsCacheItem *i = new(DnsCacheItem, 1);
404✔
510
        if (!i)
202✔
511
                return -ENOMEM;
512

513
        /* If StaleRetentionSec is greater than zero, the 'until' property is set to a duration
514
         * of StaleRetentionSec from the time of TTL expiry.
515
         * If StaleRetentionSec is zero, both the 'until' and 'until_valid' are set to the TTL duration,
516
         * leading to the eviction of the record once the TTL expires. */
517
        usec_t until_valid = calculate_until_valid(rr, min_ttl, UINT32_MAX, timestamp, false);
202✔
518
        *i = (DnsCacheItem) {
404✔
519
                .type = DNS_CACHE_POSITIVE,
520
                .key = dns_resource_key_ref(rr->key),
202✔
521
                .rr = dns_resource_record_ref(rr),
202✔
522
                .answer = dns_answer_ref(answer),
202✔
523
                .full_packet = dns_packet_ref(full_packet),
202✔
524
                .until = calculate_until(until_valid, stale_retention_usec),
202✔
525
                .until_valid = until_valid,
526
                .query_flags = query_flags & CACHEABLE_QUERY_FLAGS,
202✔
527
                .shared_owner = shared_owner,
528
                .dnssec_result = dnssec_result,
529
                .ifindex = ifindex,
530
                .owner_family = owner_family,
531
                .owner_address = *owner_address,
202✔
532
                .prioq_idx = PRIOQ_IDX_NULL,
533
        };
534

535
        r = dns_cache_link_item(c, i);
202✔
536
        if (r < 0)
202✔
537
                return r;
538

539
        log_debug("Added positive %s %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s",
625✔
540
                  FLAGS_SET(i->query_flags, SD_RESOLVED_AUTHENTICATED) ? "authenticated" : "unauthenticated",
541
                  FLAGS_SET(i->query_flags, SD_RESOLVED_CONFIDENTIAL) ? "confidential" : "non-confidential",
542
                  i->shared_owner ? " shared" : "",
543
                  dns_resource_key_to_string(i->key, key_str, sizeof key_str),
544
                  (i->until - timestamp) / USEC_PER_SEC,
545
                  i->ifindex == 0 ? "*" : FORMAT_IFNAME(i->ifindex),
546
                  af_to_name_short(i->owner_family),
547
                  IN_ADDR_TO_STRING(i->owner_family, &i->owner_address));
548

549
        TAKE_PTR(i);
202✔
550
        return 0;
202✔
551
}
552
/* https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml */
553
/* https://www.iana.org/assignments/locally-served-dns-zones/locally-served-dns-zones.xhtml#transport-independent */
554
static bool dns_special_use_domain_invalid_answer(DnsResourceKey *key, int rcode) {
31✔
555
        /* Sometimes we know a domain exists, even if broken nameservers say otherwise. Make sure not to
556
         * cache any answers we know are wrong. */
557

558
        /* RFC9462 § 6.4: resolvers SHOULD respond to queries of any type other than SVCB for
559
         * _dns.resolver.arpa. with NODATA and queries of any type for any domain name under resolver.arpa
560
         * with NODATA. */
561
        if (dns_name_endswith(dns_resource_key_name(key), "resolver.arpa") > 0 && rcode == DNS_RCODE_NXDOMAIN)
31✔
562
                return true;
×
563

564
        return false;
565
}
566

567
static int dns_cache_put_negative(
33✔
568
                DnsCache *c,
569
                DnsResourceKey *key,
570
                int rcode,
571
                DnsAnswer *answer,
572
                DnsPacket *full_packet,
573
                uint64_t query_flags,
574
                DnssecResult dnssec_result,
575
                uint32_t nsec_ttl,
576
                usec_t timestamp,
577
                DnsResourceRecord *soa,
578
                int owner_family,
579
                const union in_addr_union *owner_address) {
580

581
        _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
33✔
582
        char key_str[DNS_RESOURCE_KEY_STRING_MAX];
33✔
583
        int r;
33✔
584

585
        assert(c);
33✔
586
        assert(key);
33✔
587
        assert(owner_address);
33✔
588

589
        /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly
590
         * important to filter out as we use this as a pseudo-type for
591
         * NXDOMAIN entries */
592
        if (dns_class_is_pseudo(key->class))
33✔
593
                return 0;
594
        if (dns_type_is_pseudo(key->type))
33✔
595
                return 0;
596
        if (dns_special_use_domain_invalid_answer(key, rcode))
31✔
597
                return 0;
598

599
        if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
31✔
600
                if (!soa)
22✔
601
                        return 0;
602

603
                /* For negative replies, check if we have a TTL of a SOA */
604
                if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) {
22✔
605
                        log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s",
×
606
                                  dns_resource_key_to_string(key, key_str, sizeof key_str));
607
                        return 0;
×
608
                }
609
        } else if (rcode != DNS_RCODE_SERVFAIL)
9✔
610
                return 0;
611

612
        r = dns_cache_init(c);
31✔
613
        if (r < 0)
31✔
614
                return r;
615

616
        dns_cache_make_space(c, 1);
31✔
617

618
        i = new(DnsCacheItem, 1);
31✔
619
        if (!i)
31✔
620
                return -ENOMEM;
621

622
        *i = (DnsCacheItem) {
62✔
623
                .type =
624
                        rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA :
31✔
625
                        rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE,
626
                .query_flags = query_flags & CACHEABLE_QUERY_FLAGS,
31✔
627
                .dnssec_result = dnssec_result,
628
                .owner_family = owner_family,
629
                .owner_address = *owner_address,
31✔
630
                .prioq_idx = PRIOQ_IDX_NULL,
631
                .rcode = rcode,
632
                .answer = dns_answer_ref(answer),
31✔
633
                .full_packet = dns_packet_ref(full_packet),
31✔
634
        };
635

636
        /* Determine how long to cache this entry. In case we have some RRs in the answer use the lowest TTL
637
         * of any of them. Typically that's the SOA's TTL, which is OK, but could possibly be lower because
638
         * of some other RR. Let's better take the lowest option here than a needlessly high one */
639
        i->until = i->until_valid =
62✔
640
                i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC :
31✔
641
                calculate_until_valid(soa, dns_answer_min_ttl(answer), nsec_ttl, timestamp, true);
22✔
642

643
        if (i->type == DNS_CACHE_NXDOMAIN) {
31✔
644
                /* NXDOMAIN entries should apply equally to all types, so we use ANY as
645
                 * a pseudo type for this purpose here. */
646
                i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key));
8✔
647
                if (!i->key)
8✔
648
                        return -ENOMEM;
649

650
                /* Make sure to remove any previous entry for this
651
                 * specific ANY key. (For non-ANY keys the cache data
652
                 * is already cleared by the caller.) Note that we
653
                 * don't bother removing positive or NODATA cache
654
                 * items in this case, because it would either be slow
655
                 * or require explicit indexing by name */
656
                dns_cache_remove_by_key(c, key);
8✔
657
        } else
658
                i->key = dns_resource_key_ref(key);
23✔
659

660
        r = dns_cache_link_item(c, i);
31✔
661
        if (r < 0)
31✔
662
                return r;
663

664
        log_debug("Added %s cache entry for %s "USEC_FMT"s",
31✔
665
                  dns_cache_item_type_to_string(i),
666
                  dns_resource_key_to_string(i->key, key_str, sizeof key_str),
667
                  (i->until - timestamp) / USEC_PER_SEC);
668

669
        i = NULL;
31✔
670
        return 0;
31✔
671
}
672

673
static void dns_cache_remove_previous(
163✔
674
                DnsCache *c,
675
                DnsResourceKey *key,
676
                DnsAnswer *answer) {
677

678
        DnsResourceRecord *rr;
163✔
679
        DnsAnswerFlags flags;
163✔
680

681
        assert(c);
163✔
682

683
        /* First, if we were passed a key (i.e. on LLMNR/DNS, but
684
         * not on mDNS), delete all matching old RRs, so that we only
685
         * keep complete by_key in place. */
686
        if (key)
163✔
687
                dns_cache_remove_by_key(c, key);
150✔
688

689
        /* Second, flush all entries matching the answer, unless this
690
         * is an RR that is explicitly marked to be "shared" between
691
         * peers (i.e. mDNS RRs without the flush-cache bit set). */
692
        DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
389✔
693
                if ((flags & DNS_ANSWER_CACHEABLE) == 0)
226✔
694
                        continue;
13✔
695

696
                if (flags & DNS_ANSWER_SHARED_OWNER)
213✔
697
                        continue;
11✔
698

699
                dns_cache_remove_by_key(c, rr->key);
202✔
700
        }
701
}
163✔
702

703
static bool rr_eligible(DnsResourceRecord *rr) {
213✔
704
        assert(rr);
213✔
705

706
        /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
707
         * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
708
         * existence from any cached NSEC/NSEC3, but that should be fine. */
709

710
        switch (rr->key->type) {
213✔
711

712
        case DNS_TYPE_NSEC:
17✔
713
                return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
27✔
714
                        bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
5✔
715

716
        case DNS_TYPE_NSEC3:
8✔
717
                return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
14✔
718
                        bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
4✔
719

720
        default:
721
                return true;
722
        }
723
}
724

725
int dns_cache_put(
163✔
726
                DnsCache *c,
727
                DnsCacheMode cache_mode,
728
                DnsProtocol protocol,
729
                DnsResourceKey *key,
730
                int rcode,
731
                DnsAnswer *answer,
732
                DnsPacket *full_packet,
733
                uint64_t query_flags,
734
                DnssecResult dnssec_result,
735
                uint32_t nsec_ttl,
736
                int owner_family,
737
                const union in_addr_union *owner_address,
738
                usec_t stale_retention_usec) {
739

740
        DnsResourceRecord *soa = NULL;
163✔
741
        bool weird_rcode = false;
163✔
742
        DnsAnswerItem *item;
163✔
743
        DnsAnswerFlags flags;
163✔
744
        unsigned cache_keys;
163✔
745
        usec_t timestamp;
163✔
746
        int r;
163✔
747

748
        assert(c);
163✔
749
        assert(owner_address);
163✔
750

751
        dns_cache_remove_previous(c, key, answer);
163✔
752

753
        /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective
754
         * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL
755
         * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a
756
         * short time.) */
757

758
        if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) {
163✔
759
                if (dns_answer_isempty(answer)) {
153✔
760
                        if (key) {
4✔
761
                                char key_str[DNS_RESOURCE_KEY_STRING_MAX];
4✔
762

763
                                log_debug("Not caching negative entry without a SOA record: %s",
4✔
764
                                          dns_resource_key_to_string(key, key_str, sizeof key_str));
765
                        }
766

767
                        return 0;
4✔
768
                }
769

770
        } else {
771
                /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be
772
                 * beneficial. */
773
                if (rcode != DNS_RCODE_SERVFAIL)
10✔
774
                        return 0;
775

776
                weird_rcode = true;
777
        }
778

779
        cache_keys = dns_answer_size(answer);
158✔
780
        if (key)
158✔
781
                cache_keys++;
145✔
782

783
        /* Make some space for our new entries */
784
        dns_cache_make_space(c, cache_keys);
158✔
785

786
        timestamp = now(CLOCK_BOOTTIME);
158✔
787

788
        /* Second, add in positive entries for all contained RRs */
789
        DNS_ANSWER_FOREACH_ITEM(item, answer) {
382✔
790
                int primary = false;
226✔
791

792
                if (!FLAGS_SET(item->flags, DNS_ANSWER_CACHEABLE) ||
439✔
793
                    !rr_eligible(item->rr))
213✔
794
                        continue;
15✔
795

796
                if (key) {
211✔
797
                        /* We store the auxiliary RRs and packet data in the cache only if they were in
798
                         * direct response to the original query. If we cache an RR we also received, and
799
                         * that is just auxiliary information we can't use the data, hence don't. */
800

801
                        primary = dns_resource_key_match_rr(key, item->rr, NULL);
193✔
802
                        if (primary < 0)
193✔
803
                                return primary;
2✔
804
                        if (primary == 0) {
192✔
805
                                primary = dns_resource_key_match_cname_or_dname(key, item->rr->key, NULL);
71✔
806
                                if (primary < 0)
71✔
807
                                        return primary;
808
                        }
809
                }
810

811
                if (!primary) {
70✔
812
                        DnsCacheItem *first;
83✔
813

814
                        /* Do not replace existing cache items for primary lookups with non-primary
815
                         * data. After all the primary lookup data is a lot more useful. */
816
                        first = hashmap_get(c->by_key, item->rr->key);
83✔
817
                        if (first && DNS_CACHE_ITEM_IS_PRIMARY(first))
83✔
818
                                return 0;
819
                }
820

821
                r = dns_cache_put_positive(
209✔
822
                                c,
823
                                protocol,
824
                                item->rr,
825
                                primary ? answer : NULL,
826
                                primary ? full_packet : NULL,
827
                                ((item->flags & DNS_ANSWER_AUTHENTICATED) ? SD_RESOLVED_AUTHENTICATED : 0) |
209✔
828
                                (query_flags & SD_RESOLVED_CONFIDENTIAL),
209✔
829
                                item->flags & DNS_ANSWER_SHARED_OWNER,
209✔
830
                                dnssec_result,
831
                                timestamp,
832
                                item->ifindex,
833
                                owner_family,
834
                                owner_address,
835
                                stale_retention_usec);
836
                if (r < 0)
209✔
837
                        goto fail;
×
838
        }
839

840
        if (!key) /* mDNS doesn't know negative caching, really */
156✔
841
                return 0;
842

843
        /* Third, add in negative entries if the key has no RR */
844
        r = dns_answer_match_key(answer, key, NULL);
143✔
845
        if (r < 0)
143✔
846
                goto fail;
×
847
        if (r > 0)
143✔
848
                return 0;
849

850
        /* But not if it has a matching CNAME/DNAME (the negative caching will be done on the canonical name,
851
         * not on the alias) */
852
        r = dns_answer_find_cname_or_dname(answer, key, NULL, NULL);
41✔
853
        if (r < 0)
41✔
854
                goto fail;
×
855
        if (r > 0)
41✔
856
                return 0;
857

858
        /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to
859
         * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so
860
         * regardless of a SOA. */
861
        r = dns_answer_find_soa(answer, key, &soa, &flags);
36✔
862
        if (r < 0)
36✔
863
                goto fail;
×
864
        if (r == 0 && !weird_rcode)
36✔
865
                return 0;
866
        if (r > 0) {
33✔
867
                /* Refuse using the SOA data if it is unsigned, but the key is signed */
868
                if (FLAGS_SET(query_flags, SD_RESOLVED_AUTHENTICATED) &&
24✔
869
                    (flags & DNS_ANSWER_AUTHENTICATED) == 0)
21✔
870
                        return 0;
871
        }
872

873
        if (cache_mode == DNS_CACHE_MODE_NO_NEGATIVE) {
33✔
874
                char key_str[DNS_RESOURCE_KEY_STRING_MAX];
×
875
                log_debug("Not caching negative entry for: %s, cache mode set to no-negative",
×
876
                          dns_resource_key_to_string(key, key_str, sizeof key_str));
877
                return 0;
×
878
        }
879

880
        r = dns_cache_put_negative(
33✔
881
                        c,
882
                        key,
883
                        rcode,
884
                        answer,
885
                        full_packet,
886
                        query_flags,
887
                        dnssec_result,
888
                        nsec_ttl,
889
                        timestamp,
890
                        soa,
891
                        owner_family,
892
                        owner_address);
893
        if (r < 0)
33✔
894
                goto fail;
×
895

896
        return 0;
897

898
fail:
×
899
        /* Adding all RRs failed. Let's clean up what we already
900
         * added, just in case */
901

902
        if (key)
×
903
                dns_cache_remove_by_key(c, key);
×
904

905
        DNS_ANSWER_FOREACH_ITEM(item, answer) {
×
906
                if ((item->flags & DNS_ANSWER_CACHEABLE) == 0)
×
907
                        continue;
×
908

909
                dns_cache_remove_by_key(c, item->rr->key);
×
910
        }
911

912
        return r;
×
913
}
914

915
static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, DnsResourceKey *k) {
9,935✔
916
        DnsCacheItem *i;
9,935✔
917
        const char *n;
9,935✔
918
        int r;
9,935✔
919

920
        assert(c);
9,935✔
921
        assert(k);
9,935✔
922

923
        /* If we hit some OOM error, or suchlike, we don't care too
924
         * much, after all this is just a cache */
925

926
        i = hashmap_get(c->by_key, k);
9,935✔
927
        if (i)
9,935✔
928
                return i;
9,935✔
929

930
        n = dns_resource_key_name(k);
9,761✔
931

932
        /* Check if we have an NXDOMAIN cache item for the name, notice that we use
933
         * the pseudo-type ANY for NXDOMAIN cache items. */
934
        i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_ANY, n));
9,761✔
935
        if (i && i->type == DNS_CACHE_NXDOMAIN)
9,761✔
936
                return i;
937

938
        if (dns_type_may_redirect(k->type)) {
9,758✔
939
                /* Check if we have a CNAME record instead */
940
                i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_CNAME, n));
9,758✔
941
                if (i && i->type != DNS_CACHE_NODATA)
9,758✔
942
                        return i;
9,758✔
943

944
                /* OK, let's look for cached DNAME records. */
945
                for (;;) {
48,515✔
946
                        if (isempty(n))
58,273✔
947
                                return NULL;
9,758✔
948

949
                        i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_DNAME, n));
48,524✔
950
                        if (i && i->type != DNS_CACHE_NODATA)
48,524✔
951
                                return i;
952

953
                        /* Jump one label ahead */
954
                        r = dns_name_parent(&n);
48,524✔
955
                        if (r <= 0)
48,524✔
956
                                return NULL;
957
                }
958
        }
959

960
        if (k->type != DNS_TYPE_NSEC) {
×
961
                /* Check if we have an NSEC record instead for the name. */
962
                i = hashmap_get(c->by_key, &DNS_RESOURCE_KEY_CONST(k->class, DNS_TYPE_NSEC, n));
×
963
                if (i)
×
964
                        return i;
×
965
        }
966

967
        return NULL;
968
}
969

970
static int answer_add_clamp_ttl(
259✔
971
                DnsAnswer **answer,
972
                DnsResourceRecord *rr,
973
                int ifindex,
974
                DnsAnswerFlags answer_flags,
975
                DnsResourceRecord *rrsig,
976
                uint64_t query_flags,
977
                usec_t until,
978
                usec_t current) {
979

980
        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *patched = NULL, *patched_rrsig = NULL;
259✔
981
        int r;
259✔
982

983
        assert(answer);
259✔
984
        assert(rr);
259✔
985

986
        if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) {
259✔
987
                uint32_t left_ttl;
130✔
988

989
                assert(current > 0);
130✔
990

991
                /* Let's determine how much time is left for this cache entry. Note that we round down, but
992
                 * clamp this to be 1s at minimum, since we usually want records to remain cached better too
993
                 * short a time than too long a time, but otoh don't want to return 0 ever, since that has
994
                 * special semantics in various contexts — in particular in mDNS */
995

996
                left_ttl = MAX(1U, LESS_BY(until, current) / USEC_PER_SEC);
130✔
997

998
                patched = dns_resource_record_ref(rr);
130✔
999

1000
                r = dns_resource_record_clamp_ttl(&patched, left_ttl);
130✔
1001
                if (r < 0)
130✔
1002
                        return r;
1003

1004
                rr = patched;
130✔
1005

1006
                if (rrsig) {
130✔
1007
                        patched_rrsig = dns_resource_record_ref(rrsig);
127✔
1008
                        r = dns_resource_record_clamp_ttl(&patched_rrsig, left_ttl);
127✔
1009
                        if (r < 0)
127✔
1010
                                return r;
1011

1012
                        rrsig = patched_rrsig;
127✔
1013
                }
1014
        }
1015

1016
        r = dns_answer_add_extend(answer, rr, ifindex, answer_flags, rrsig);
259✔
1017
        if (r < 0)
259✔
1018
                return r;
×
1019

1020
        return 0;
1021
}
1022

1023
int dns_cache_lookup(
9,936✔
1024
                DnsCache *c,
1025
                DnsResourceKey *key,
1026
                uint64_t query_flags,
1027
                int *ret_rcode,
1028
                DnsAnswer **ret_answer,
1029
                DnsPacket **ret_full_packet,
1030
                uint64_t *ret_query_flags,
1031
                DnssecResult *ret_dnssec_result) {
1032

1033
        _cleanup_(dns_packet_unrefp) DnsPacket *full_packet = NULL;
9,936✔
1034
        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
9,936✔
1035
        char key_str[DNS_RESOURCE_KEY_STRING_MAX];
9,936✔
1036
        unsigned n = 0;
9,936✔
1037
        int r;
9,936✔
1038
        bool nxdomain = false;
9,936✔
1039
        DnsCacheItem *first, *nsec = NULL;
9,936✔
1040
        bool have_authenticated = false, have_non_authenticated = false, have_confidential = false, have_non_confidential = false;
9,936✔
1041
        usec_t current = 0;
9,936✔
1042
        int found_rcode = -1;
9,936✔
1043
        DnssecResult dnssec_result = -1;
9,936✔
1044
        int have_dnssec_result = -1;
9,936✔
1045

1046
        assert(c);
9,936✔
1047
        assert(key);
9,936✔
1048

1049
        if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
9,936✔
1050
                /* If we have ANY lookups we don't use the cache, so that the caller refreshes via the
1051
                 * network. */
1052

1053
                log_debug("Ignoring cache for ANY lookup: %s",
1✔
1054
                          dns_resource_key_to_string(key, key_str, sizeof key_str));
1055
                goto miss;
1✔
1056
        }
1057

1058
        first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key);
9,935✔
1059
        if (!first) {
9,935✔
1060
                /* If one question cannot be answered we need to refresh */
1061

1062
                log_debug("Cache miss for %s",
9,758✔
1063
                          dns_resource_key_to_string(key, key_str, sizeof key_str));
1064
                goto miss;
9,758✔
1065
        }
1066

1067
        if ((query_flags & (SD_RESOLVED_CLAMP_TTL | SD_RESOLVED_NO_STALE)) != 0) {
177✔
1068
                /* 'current' is always passed to answer_add_clamp_ttl(), but is only used conditionally.
1069
                 * We'll do the same assert there to make sure that it was initialized properly.
1070
                 * 'current' is also used below when SD_RESOLVED_NO_STALE is set. */
1071
                current = now(CLOCK_BOOTTIME);
171✔
1072
                assert(current > 0);
171✔
1073
        }
1074

1075
        LIST_FOREACH(by_key, j, first) {
416✔
1076
                /* If the caller doesn't allow us to answer questions from cache data learned from
1077
                 * "side-effect", skip this entry. */
1078
                if (FLAGS_SET(query_flags, SD_RESOLVED_REQUIRE_PRIMARY) &&
242✔
1079
                    !DNS_CACHE_ITEM_IS_PRIMARY(j)) {
93✔
1080
                        log_debug("Primary answer was requested for cache lookup for %s, which we don't have.",
×
1081
                                  dns_resource_key_to_string(key, key_str, sizeof key_str));
1082

1083
                        goto miss;
×
1084
                }
1085

1086
                /* Skip the next part if ttl is expired and requested with no stale flag. */
1087
                if (FLAGS_SET(query_flags, SD_RESOLVED_NO_STALE) && j->until_valid < current) {
242✔
1088
                        log_debug("Requested with no stale and TTL expired for %s",
3✔
1089
                                                dns_resource_key_to_string(key, key_str, sizeof key_str));
1090

1091
                        goto miss;
3✔
1092
                }
1093

1094
                if (j->type == DNS_CACHE_NXDOMAIN)
239✔
1095
                        nxdomain = true;
1096
                else if (j->type == DNS_CACHE_RCODE)
236✔
1097
                        found_rcode = j->rcode;
8✔
1098
                else if (j->rr) {
228✔
1099
                        if (j->rr->key->type == DNS_TYPE_NSEC)
222✔
1100
                                nsec = j;
×
1101

1102
                        n++;
222✔
1103
                }
1104

1105
                if (FLAGS_SET(j->query_flags, SD_RESOLVED_AUTHENTICATED))
239✔
1106
                        have_authenticated = true;
1107
                else
1108
                        have_non_authenticated = true;
22✔
1109

1110
                if (FLAGS_SET(j->query_flags, SD_RESOLVED_CONFIDENTIAL))
239✔
1111
                        have_confidential = true;
1112
                else
1113
                        have_non_confidential = true;
230✔
1114

1115
                if (j->dnssec_result < 0) {
239✔
1116
                        have_dnssec_result = false; /* an entry without dnssec result? then invalidate things for good */
1117
                        dnssec_result = _DNSSEC_RESULT_INVALID;
1118
                } else if (have_dnssec_result < 0) {
239✔
1119
                        have_dnssec_result = true; /* So far no result seen, let's pick this one up */
1120
                        dnssec_result = j->dnssec_result;
1121
                } else if (have_dnssec_result > 0 && j->dnssec_result != dnssec_result) {
65✔
1122
                        have_dnssec_result = false; /* conflicting result seen? then invalidate for good */
×
1123
                        dnssec_result = _DNSSEC_RESULT_INVALID;
×
1124
                }
1125

1126
                /* If the question is being resolved using stale data, the clamp TTL will be set to CACHE_STALE_TTL_MAX_USEC. */
1127
                usec_t until = FLAGS_SET(query_flags, SD_RESOLVED_NO_STALE) ? j->until_valid
478✔
1128
                                                                            : usec_add(current, CACHE_STALE_TTL_MAX_USEC);
239✔
1129

1130
                /* Append the answer RRs to our answer. Ideally we have the answer object, which we
1131
                 * preferably use. But if the cached entry was generated as "side-effect" of a reply,
1132
                 * i.e. from validated auxiliary records rather than from the main reply, then we use the
1133
                 * individual RRs only instead. */
1134
                if (j->answer) {
239✔
1135

1136
                        /* Minor optimization, if the full answer object of this and the previous RR is the
1137
                         * same, don't bother adding it again. Typically we store a full RRset here, hence
1138
                         * that should be the case. */
1139
                        if (!j->by_key_prev || j->answer != j->by_key_prev->answer) {
224✔
1140
                                DnsAnswerItem *item;
160✔
1141

1142
                                DNS_ANSWER_FOREACH_ITEM(item, j->answer) {
404✔
1143
                                        r = answer_add_clamp_ttl(
244✔
1144
                                                        &answer,
1145
                                                        item->rr,
1146
                                                        item->ifindex,
1147
                                                        item->flags,
1148
                                                        item->rrsig,
1149
                                                        query_flags,
1150
                                                        until,
1151
                                                        current);
1152
                                        if (r < 0)
244✔
1153
                                                return r;
×
1154
                                }
1155
                        }
1156

1157
                } else if (j->rr) {
15✔
1158
                        r = answer_add_clamp_ttl(
15✔
1159
                                        &answer,
1160
                                        j->rr,
1161
                                        j->ifindex,
1162
                                        FLAGS_SET(j->query_flags, SD_RESOLVED_AUTHENTICATED) ? DNS_ANSWER_AUTHENTICATED : 0,
1163
                                        NULL,
1164
                                        query_flags,
1165
                                        until,
1166
                                        current);
1167
                        if (r < 0)
15✔
1168
                                return r;
1169
                }
1170

1171
                /* We'll return any packet we have for this. Typically all cache entries for the same key
1172
                 * should come from the same packet anyway, hence it doesn't really matter which packet we
1173
                 * return here, they should all resolve to the same anyway. */
1174
                if (!full_packet && j->full_packet)
239✔
1175
                        full_packet = dns_packet_ref(j->full_packet);
153✔
1176
        }
1177

1178
        if (found_rcode >= 0) {
174✔
1179
                log_debug("RCODE %s cache hit for %s",
8✔
1180
                          FORMAT_DNS_RCODE(found_rcode),
1181
                          dns_resource_key_to_string(key, key_str, sizeof(key_str)));
1182

1183
                if (ret_rcode)
8✔
1184
                        *ret_rcode = found_rcode;
8✔
1185
                if (ret_answer)
8✔
1186
                        *ret_answer = TAKE_PTR(answer);
8✔
1187
                if (ret_full_packet)
8✔
1188
                        *ret_full_packet = TAKE_PTR(full_packet);
8✔
1189
                if (ret_query_flags)
8✔
1190
                        *ret_query_flags = 0;
8✔
1191
                if (ret_dnssec_result)
8✔
1192
                        *ret_dnssec_result = dnssec_result;
8✔
1193

1194
                c->n_hit++;
8✔
1195
                return 1;
8✔
1196
        }
1197

1198
        if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
166✔
1199
                /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC
1200
                 * RRs from the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
1201

1202
                log_debug("NSEC NODATA cache hit for %s",
×
1203
                          dns_resource_key_to_string(key, key_str, sizeof key_str));
1204

1205
                /* We only found an NSEC record that matches our name.  If it says the type doesn't exist
1206
                 * report NODATA. Otherwise report a cache miss. */
1207

1208
                if (ret_rcode)
×
1209
                        *ret_rcode = DNS_RCODE_SUCCESS;
×
1210
                if (ret_answer)
×
1211
                        *ret_answer = TAKE_PTR(answer);
×
1212
                if (ret_full_packet)
×
1213
                        *ret_full_packet = TAKE_PTR(full_packet);
×
1214
                if (ret_query_flags)
×
1215
                        *ret_query_flags = nsec->query_flags;
×
1216
                if (ret_dnssec_result)
×
1217
                        *ret_dnssec_result = nsec->dnssec_result;
×
1218

1219
                if (!bitmap_isset(nsec->rr->nsec.types, key->type) &&
×
1220
                    !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) &&
×
1221
                    !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) {
×
1222
                        c->n_hit++;
×
1223
                        return 1;
×
1224
                }
1225

1226
                c->n_miss++;
×
1227
                return 0;
×
1228
        }
1229

1230
        log_debug("%s cache hit for %s",
172✔
1231
                  n > 0    ? "Positive" :
1232
                  nxdomain ? "NXDOMAIN" : "NODATA",
1233
                  dns_resource_key_to_string(key, key_str, sizeof key_str));
1234

1235
        if (n <= 0) {
166✔
1236
                c->n_hit++;
9✔
1237

1238
                if (ret_rcode)
9✔
1239
                        *ret_rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS;
15✔
1240
                if (ret_answer)
9✔
1241
                        *ret_answer = TAKE_PTR(answer);
9✔
1242
                if (ret_full_packet)
9✔
1243
                        *ret_full_packet = TAKE_PTR(full_packet);
9✔
1244
                if (ret_query_flags)
9✔
1245
                        *ret_query_flags =
18✔
1246
                                ((have_authenticated && !have_non_authenticated) ? SD_RESOLVED_AUTHENTICATED : 0) |
18✔
1247
                                ((have_confidential && !have_non_confidential) ? SD_RESOLVED_CONFIDENTIAL : 0);
9✔
1248
                if (ret_dnssec_result)
9✔
1249
                        *ret_dnssec_result = dnssec_result;
8✔
1250

1251
                return 1;
9✔
1252
        }
1253

1254
        c->n_hit++;
157✔
1255

1256
        if (ret_rcode)
157✔
1257
                *ret_rcode = DNS_RCODE_SUCCESS;
157✔
1258
        if (ret_answer)
157✔
1259
                *ret_answer = TAKE_PTR(answer);
157✔
1260
        if (ret_full_packet)
157✔
1261
                *ret_full_packet = TAKE_PTR(full_packet);
157✔
1262
        if (ret_query_flags)
157✔
1263
                *ret_query_flags =
314✔
1264
                        ((have_authenticated && !have_non_authenticated) ? SD_RESOLVED_AUTHENTICATED : 0) |
314✔
1265
                        ((have_confidential && !have_non_confidential) ? SD_RESOLVED_CONFIDENTIAL : 0);
157✔
1266
        if (ret_dnssec_result)
157✔
1267
                *ret_dnssec_result = dnssec_result;
151✔
1268

1269
        return n;
157✔
1270

1271
miss:
9,762✔
1272
        if (ret_rcode)
9,762✔
1273
                *ret_rcode = DNS_RCODE_SUCCESS;
9,762✔
1274
        if (ret_answer)
9,762✔
1275
                *ret_answer = NULL;
9,762✔
1276
        if (ret_full_packet)
9,762✔
1277
                *ret_full_packet = NULL;
9,762✔
1278
        if (ret_query_flags)
9,762✔
1279
                *ret_query_flags = 0;
9,762✔
1280
        if (ret_dnssec_result)
9,762✔
1281
                *ret_dnssec_result = _DNSSEC_RESULT_INVALID;
9,760✔
1282

1283
        c->n_miss++;
9,762✔
1284
        return 0;
9,762✔
1285
}
1286

1287
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) {
3✔
1288
        DnsCacheItem *first;
3✔
1289
        bool same_owner = true;
3✔
1290

1291
        assert(cache);
3✔
1292
        assert(rr);
3✔
1293

1294
        dns_cache_prune(cache);
3✔
1295

1296
        /* See if there's a cache entry for the same key. If there
1297
         * isn't there's no conflict */
1298
        first = hashmap_get(cache->by_key, rr->key);
3✔
1299
        if (!first)
3✔
1300
                return 0;
1301

1302
        /* See if the RR key is owned by the same owner, if so, there
1303
         * isn't a conflict either */
1304
        LIST_FOREACH(by_key, i, first) {
3✔
1305
                if (i->owner_family != owner_family ||
4✔
1306
                    !in_addr_equal(owner_family, &i->owner_address, owner_address)) {
2✔
1307
                        same_owner = false;
1✔
1308
                        break;
1✔
1309
                }
1310
        }
1311
        if (same_owner)
1✔
1312
                return 0;
1313

1314
        /* See if there's the exact same RR in the cache. If yes, then
1315
         * there's no conflict. */
1316
        if (dns_cache_get(cache, rr))
1✔
1317
                return 0;
×
1318

1319
        /* There's a conflict */
1320
        return 1;
1321
}
1322

1323
int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p, usec_t ts, unsigned max_rr) {
2✔
1324
        unsigned ancount = 0;
2✔
1325
        DnsCacheItem *i;
2✔
1326
        int r;
2✔
1327

1328
        assert(cache);
2✔
1329
        assert(p);
2✔
1330
        assert(p->protocol == DNS_PROTOCOL_MDNS);
2✔
1331

1332
        HASHMAP_FOREACH(i, cache->by_key)
8✔
1333
                LIST_FOREACH(by_key, j, i) {
12✔
1334
                        if (!j->rr)
6✔
1335
                                continue;
×
1336

1337
                        if (!j->shared_owner)
6✔
1338
                                continue;
3✔
1339

1340
                        /* Ignore cached goodby packet. See on_mdns_packet() and RFC 6762 section 10.1. */
1341
                        if (j->rr->ttl <= 1)
3✔
1342
                                continue;
×
1343

1344
                        /* RFC6762 7.1: Don't append records with less than half the TTL remaining
1345
                         * as known answers. */
1346
                        if (usec_sub_unsigned(j->until, ts) < j->rr->ttl * USEC_PER_SEC / 2)
6✔
1347
                                continue;
×
1348

1349
                        if (max_rr > 0 && ancount >= max_rr) {
3✔
1350
                                DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
2✔
1351
                                ancount = 0;
1✔
1352

1353
                                r = dns_packet_new_query(&p->more, p->protocol, 0, true);
1✔
1354
                                if (r < 0)
1✔
1355
                                        return r;
×
1356

1357
                                p = p->more;
1✔
1358

1359
                                max_rr = UINT_MAX;
1✔
1360
                        }
1361

1362
                        r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
3✔
1363
                        if (r == -EMSGSIZE) {
3✔
1364
                                if (max_rr == 0)
×
1365
                                        /* If max_rr == 0, do not allocate more packets. */
1366
                                        goto finalize;
×
1367

1368
                                /* If we're unable to stuff all known answers into the given packet, allocate
1369
                                 * a new one, push the RR into that one and link it to the current one. */
1370

1371
                                DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
×
1372
                                ancount = 0;
×
1373

1374
                                r = dns_packet_new_query(&p->more, p->protocol, 0, true);
×
1375
                                if (r < 0)
×
1376
                                        return r;
1377

1378
                                /* continue with new packet */
1379
                                p = p->more;
×
1380
                                r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
×
1381
                        }
1382

1383
                        if (r < 0)
3✔
1384
                                return r;
1385

1386
                        ancount++;
3✔
1387
                }
1388

1389
finalize:
2✔
1390
        DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
4✔
1391

1392
        return 0;
2✔
1393
}
1394

1395
void dns_cache_dump(DnsCache *cache, FILE *f) {
2✔
1396
        DnsCacheItem *i;
2✔
1397

1398
        if (!cache)
2✔
1399
                return;
×
1400

1401
        if (!f)
2✔
1402
                f = stdout;
×
1403

1404
        HASHMAP_FOREACH(i, cache->by_key)
5✔
1405
                LIST_FOREACH(by_key, j, i) {
6✔
1406

1407
                        fputc('\t', f);
3✔
1408

1409
                        if (j->rr) {
3✔
1410
                                const char *t;
3✔
1411
                                t = dns_resource_record_to_string(j->rr);
3✔
1412
                                if (!t) {
3✔
1413
                                        log_oom();
×
1414
                                        continue;
×
1415
                                }
1416

1417
                                fputs(t, f);
3✔
1418
                                fputc('\n', f);
3✔
1419
                        } else {
1420
                                char key_str[DNS_RESOURCE_KEY_STRING_MAX];
×
1421

1422
                                fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f);
×
1423
                                fputs(" -- ", f);
×
1424
                                fputs(dns_cache_item_type_to_string(j), f);
×
1425
                                fputc('\n', f);
×
1426
                        }
1427
                }
1428
}
1429

1430
int dns_cache_dump_to_json(DnsCache *cache, sd_json_variant **ret) {
7✔
1431
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *c = NULL;
7✔
1432
        DnsCacheItem *i;
7✔
1433
        int r;
7✔
1434

1435
        assert(cache);
7✔
1436
        assert(ret);
7✔
1437

1438
        HASHMAP_FOREACH(i, cache->by_key) {
77✔
1439
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *d = NULL, *k = NULL;
140✔
1440

1441
                r = dns_resource_key_to_json(i->key, &k);
70✔
1442
                if (r < 0)
70✔
1443
                        return r;
1444

1445
                if (i->rr) {
70✔
1446
                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *l = NULL;
40✔
1447

1448
                        LIST_FOREACH(by_key, j, i) {
86✔
1449
                                _cleanup_(sd_json_variant_unrefp) sd_json_variant *rj = NULL;
46✔
1450

1451
                                assert(j->rr);
46✔
1452

1453
                                r = dns_resource_record_to_json(j->rr, &rj);
46✔
1454
                                if (r < 0)
46✔
1455
                                        return r;
1456

1457
                                r = dns_resource_record_to_wire_format(j->rr, /* canonical= */ false); /* don't use DNSSEC canonical format, since it removes casing, but we want that for DNS_SD compat */
46✔
1458
                                if (r < 0)
46✔
1459
                                        return r;
1460

1461
                                r = sd_json_variant_append_arraybo(
46✔
1462
                                                &l,
1463
                                                SD_JSON_BUILD_PAIR_VARIANT("rr", rj),
1464
                                                SD_JSON_BUILD_PAIR_BASE64("raw", j->rr->wire_format, j->rr->wire_format_size));
1465
                                if (r < 0)
46✔
1466
                                        return r;
1467
                        }
1468

1469
                        if (!l) {
40✔
1470
                                r = sd_json_variant_new_array(&l, NULL, 0);
×
1471
                                if (r < 0)
×
1472
                                        return r;
1473
                        }
1474

1475
                        r = sd_json_buildo(
40✔
1476
                                        &d,
1477
                                        SD_JSON_BUILD_PAIR_VARIANT("key", k),
1478
                                        SD_JSON_BUILD_PAIR_VARIANT("rrs", l),
1479
                                        SD_JSON_BUILD_PAIR_UNSIGNED("until", i->until));
1480
                } else if (i->type == DNS_CACHE_NODATA) {
30✔
1481
                        r = sd_json_buildo(
3✔
1482
                                        &d,
1483
                                        SD_JSON_BUILD_PAIR_VARIANT("key", k),
1484
                                        SD_JSON_BUILD_PAIR_EMPTY_ARRAY("rrs"),
1485
                                        SD_JSON_BUILD_PAIR_UNSIGNED("until", i->until));
1486
                } else
1487
                        r = sd_json_buildo(
27✔
1488
                                        &d,
1489
                                        SD_JSON_BUILD_PAIR_VARIANT("key", k),
1490
                                        SD_JSON_BUILD_PAIR_STRING("type", dns_cache_item_type_to_string(i)),
1491
                                        SD_JSON_BUILD_PAIR_UNSIGNED("until", i->until));
1492
                if (r < 0)
70✔
1493
                        return r;
1494

1495
                r = sd_json_variant_append_array(&c, d);
70✔
1496
                if (r < 0)
70✔
1497
                        return r;
1498
        }
1499

1500
        if (!c)
7✔
1501
                return sd_json_variant_new_array(ret, NULL, 0);
3✔
1502

1503
        *ret = TAKE_PTR(c);
4✔
1504
        return 0;
4✔
1505
}
1506

1507
bool dns_cache_is_empty(DnsCache *cache) {
26✔
1508
        if (!cache)
26✔
1509
                return true;
1510

1511
        return hashmap_isempty(cache->by_key);
26✔
1512
}
1513

1514
unsigned dns_cache_size(DnsCache *cache) {
27✔
1515
        if (!cache)
27✔
1516
                return 0;
1517

1518
        return hashmap_size(cache->by_key);
27✔
1519
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc