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

systemd / systemd / 19397511130

15 Nov 2025 09:47PM UTC coverage: 72.518% (+0.1%) from 72.37%
19397511130

push

github

web-flow
sd-event: several follow-ups for recent change (#39743)

3 of 4 new or added lines in 2 files covered. (75.0%)

1869 existing lines in 55 files now uncovered.

308519 of 425439 relevant lines covered (72.52%)

1258617.71 hits per line

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

90.69
/src/shared/dns-answer.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdio.h>
4

5
#include "sd-json.h"
6

7
#include "alloc-util.h"
8
#include "dns-answer.h"
9
#include "dns-domain.h"
10
#include "dns-rr.h"
11
#include "log.h"
12
#include "random-util.h"
13
#include "siphash24.h"
14
#include "string-util.h"
15

16
static DnsAnswerItem *dns_answer_item_free(DnsAnswerItem *item) {
16,314✔
17
        if (!item)
16,314✔
18
                return NULL;
19

20
        dns_resource_record_unref(item->rr);
16,314✔
21
        dns_resource_record_unref(item->rrsig);
16,314✔
22

23
        return mfree(item);
16,314✔
24
}
25

26
DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(DnsAnswerItem, dns_answer_item, dns_answer_item_free);
16,654✔
27
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswerItem*, dns_answer_item_unref);
16,314✔
28

29
static void dns_answer_item_hash_func(const DnsAnswerItem *a, struct siphash *state) {
42,510✔
30
        assert(a);
42,510✔
31
        assert(state);
42,510✔
32

33
        siphash24_compress_typesafe(a->ifindex, state);
42,510✔
34

35
        dns_resource_record_hash_func(a->rr, state);
42,510✔
36
}
42,510✔
37

38
static int dns_answer_item_compare_func(const DnsAnswerItem *a, const DnsAnswerItem *b) {
5,177✔
39
        int r;
5,177✔
40

41
        assert(a);
5,177✔
42
        assert(b);
5,177✔
43

44
        r = CMP(a->ifindex, b->ifindex);
5,177✔
45
        if (r != 0)
5,150✔
46
                return r;
29✔
47

48
        return dns_resource_record_compare_func(a->rr, b->rr);
5,148✔
49
}
50

51
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
16,278✔
52
        dns_answer_item_hash_ops,
53
        DnsAnswerItem,
54
        dns_answer_item_hash_func,
55
        dns_answer_item_compare_func,
56
        dns_answer_item_unref);
57

58
static int dns_answer_reserve_internal(DnsAnswer *a, size_t n) {
10,029✔
59
        size_t m;
10,029✔
60

61
        assert(a);
10,029✔
62
        assert(a->items);
10,029✔
63

64
        m = ordered_set_size(a->items);
10,029✔
65
        assert(m <= UINT16_MAX); /* We can only place 64K RRs in an answer at max */
10,029✔
66

67
        n = saturate_add(m, n, UINT16_MAX);
10,029✔
68

69
        /* Higher multipliers give slightly higher efficiency through hash collisions, but the gains
70
         * quickly drop off after 2. */
71
        return ordered_set_reserve(a->items, n * 2);
10,029✔
72
}
73

74
DnsAnswer *dns_answer_new(size_t n) {
1,928✔
75
        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
1,928✔
76
        _cleanup_(dns_answer_unrefp) DnsAnswer *a = NULL;
1,928✔
77

78
        if (n > UINT16_MAX)
1,928✔
UNCOV
79
                n = UINT16_MAX;
×
80

81
        s = ordered_set_new(&dns_answer_item_hash_ops);
1,928✔
82
        if (!s)
1,928✔
83
                return NULL;
84

85
        a = new(DnsAnswer, 1);
1,928✔
86
        if (!a)
1,928✔
87
                return NULL;
88

89
        *a = (DnsAnswer) {
1,928✔
90
                .n_ref = 1,
91
                .items = TAKE_PTR(s),
1,928✔
92
        };
93

94
        if (dns_answer_reserve_internal(a, n) < 0)
1,928✔
95
                return NULL;
96

97
        return TAKE_PTR(a);
1,928✔
98
}
99

100
static DnsAnswer *dns_answer_free(DnsAnswer *a) {
1,928✔
101
        assert(a);
1,928✔
102

103
        ordered_set_free(a->items);
1,928✔
104
        return mfree(a);
1,928✔
105
}
106

107
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsAnswer, dns_answer, dns_answer_free);
62,762✔
108

109
static int dns_answer_add_raw(
16,314✔
110
                DnsAnswer *a,
111
                DnsResourceRecord *rr,
112
                int ifindex,
113
                DnsAnswerFlags flags,
114
                DnsResourceRecord *rrsig,
115
                usec_t until) {
116

117
        _cleanup_(dns_answer_item_unrefp) DnsAnswerItem *item = NULL;
16,314✔
118
        int r;
16,314✔
119

120
        assert(rr);
16,314✔
121

122
        if (!a)
16,314✔
123
                return -ENOSPC;
124

125
        if (dns_answer_size(a) >= UINT16_MAX)
16,314✔
126
                return -ENOSPC;
127

128
        item = new(DnsAnswerItem, 1);
16,314✔
129
        if (!item)
16,314✔
130
                return -ENOMEM;
131

132
        *item = (DnsAnswerItem) {
32,628✔
133
                .n_ref = 1,
134
                .rr = dns_resource_record_ref(rr),
16,314✔
135
                .ifindex = ifindex,
136
                .flags = flags,
137
                .until = until,
138
                .rrsig = dns_resource_record_ref(rrsig),
16,314✔
139
        };
140

141
        r = ordered_set_put(a->items, item);
16,314✔
142
        if (r < 0)
16,314✔
143
                return r;
144

145
        TAKE_PTR(item);
16,314✔
146
        return 1;
16,314✔
147
}
148

149
static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
157✔
150
        DnsAnswerItem *item;
157✔
151
        int r;
157✔
152

153
        DNS_ANSWER_FOREACH_ITEM(item, source) {
388✔
154
                r = dns_answer_add_raw(
231✔
155
                                a,
156
                                item->rr,
157
                                item->ifindex,
158
                                item->flags,
159
                                item->rrsig,
160
                                /* until= */ USEC_INFINITY);
161
                if (r < 0)
231✔
UNCOV
162
                        return r;
×
163
        }
164

165
        return 0;
157✔
166
}
167

168
int dns_answer_add_full(
16,128✔
169
                DnsAnswer *a,
170
                DnsResourceRecord *rr,
171
                int ifindex,
172
                DnsAnswerFlags flags,
173
                DnsResourceRecord *rrsig,
174
                usec_t until) {
175

176
        DnsAnswerItem tmp, *exist;
16,128✔
177

178
        assert(rr);
16,128✔
179

180
        if (!a)
16,128✔
181
                return -ENOSPC;
16,128✔
182
        if (a->n_ref > 1)
16,128✔
183
                return -EBUSY;
184

185
        tmp = (DnsAnswerItem) {
16,128✔
186
                .rr = rr,
187
                .ifindex = ifindex,
188
        };
189

190
        exist = ordered_set_get(a->items, &tmp);
16,128✔
191
        if (exist) {
16,128✔
192
                /* There's already an RR of the same RRset in place! Let's see if the TTLs more or
193
                 * less match. RFC 2181, Section 5.2 suggests clients should reject RRsets
194
                 * containing RRs with differing TTLs. We are more tolerant of this situation except
195
                 * if one RR has a zero TTL and the other a nonzero TTL. In mDNS, zero TTLs are
196
                 * special, so we must error in that case. */
197
                if ((rr->ttl == 0) != (exist->rr->ttl == 0)) {
45✔
UNCOV
198
                        if ((exist->flags | flags) & DNS_ANSWER_REFUSE_TTL_NO_MATCH)
×
UNCOV
199
                                return log_debug_errno(
×
200
                                                SYNTHETIC_ERRNO(EINVAL),
201
                                                "Refusing to merge RRs with zero TTL and non-zero TTL: %s vs. %s",
202
                                                dns_resource_record_to_string(rr),
203
                                                dns_resource_record_to_string(exist->rr));
204

UNCOV
205
                        log_debug("Merging RRs with zero TTL and non-zero TTL (not RFC 2181/5.2 compliant): %s vs. %s",
×
206
                                  dns_resource_record_to_string(rr),
207
                                  dns_resource_record_to_string(exist->rr));
208
                }
209

210
                /* Entry already exists, keep the entry with the higher TTL. */
211
                if (rr->ttl > exist->rr->ttl) {
45✔
UNCOV
212
                        DNS_RR_REPLACE(exist->rr, dns_resource_record_ref(rr));
×
213

214
                        /* Update RRSIG and RR at the same time */
UNCOV
215
                        if (rrsig)
×
UNCOV
216
                                DNS_RR_REPLACE(exist->rrsig, dns_resource_record_ref(rrsig));
×
217
                }
218

219
                exist->flags |= flags;
45✔
220

221
                if (rr->key->type == DNS_TYPE_RRSIG) {
45✔
222
                        /* If the rr is RRSIG, then move the rr to the end. */
UNCOV
223
                        assert_se(ordered_set_remove(a->items, exist) == exist);
×
UNCOV
224
                        assert_se(ordered_set_put(a->items, exist) == 1);
×
225
                }
226
                return 0;
45✔
227
        }
228

229
        return dns_answer_add_raw(a, rr, ifindex, flags, rrsig, until);
16,083✔
230
}
231

232
static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
105✔
233
        DnsAnswerItem *item;
105✔
234
        int r;
105✔
235

236
        DNS_ANSWER_FOREACH_ITEM(item, b) {
260✔
237
                r = dns_answer_add(a, item->rr, item->ifindex, item->flags, item->rrsig);
155✔
238
                if (r < 0)
155✔
UNCOV
239
                        return r;
×
240
        }
241

242
        return 0;
105✔
243
}
244

245
int dns_answer_add_extend_full(
8,744✔
246
                DnsAnswer **a,
247
                DnsResourceRecord *rr,
248
                int ifindex,
249
                DnsAnswerFlags flags,
250
                DnsResourceRecord *rrsig,
251
                usec_t until) {
252

253
        int r;
8,744✔
254

255
        assert(a);
8,744✔
256
        assert(rr);
8,744✔
257

258
        r = dns_answer_reserve_or_clone(a, 1);
8,744✔
259
        if (r < 0)
8,744✔
260
                return r;
261

262
        return dns_answer_add_full(*a, rr, ifindex, flags, rrsig, until);
8,744✔
263
}
264

265
int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
11✔
UNCOV
266
        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
×
267

268
        soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
11✔
269
        if (!soa)
11✔
270
                return -ENOMEM;
271

272
        soa->ttl = ttl;
11✔
273

274
        soa->soa.mname = strdup(name);
11✔
275
        if (!soa->soa.mname)
11✔
276
                return -ENOMEM;
277

278
        soa->soa.rname = strjoin("root.", name);
11✔
279
        if (!soa->soa.rname)
11✔
280
                return -ENOMEM;
281

282
        soa->soa.serial = 1;
11✔
283
        soa->soa.refresh = 1;
11✔
284
        soa->soa.retry = 1;
11✔
285
        soa->soa.expire = 1;
11✔
286
        soa->soa.minimum = ttl;
11✔
287

288
        return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED, NULL);
11✔
289
}
290

291
int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
1,090✔
292
        DnsAnswerFlags flags = 0, i_flags;
1,090✔
293
        DnsResourceRecord *i;
1,090✔
294
        bool found = false;
1,090✔
295
        int r;
1,090✔
296

297
        assert(key);
1,090✔
298

299
        DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
9,934✔
300
                r = dns_resource_key_match_rr(key, i, NULL);
9,312✔
301
                if (r < 0)
9,312✔
302
                        return r;
468✔
303
                if (r == 0)
9,311✔
304
                        continue;
8,688✔
305

306
                if (!ret_flags)
623✔
307
                        return 1;
308

309
                if (found)
156✔
310
                        flags &= i_flags;
32✔
311
                else {
312
                        flags = i_flags;
313
                        found = true;
314
                }
315
        }
316

317
        if (ret_flags)
622✔
318
                *ret_flags = flags;
151✔
319

320
        return found;
622✔
321
}
322

323
bool dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
24✔
324
        DnsResourceRecord *i;
24✔
325

326
        DNS_ANSWER_FOREACH(i, a)
48✔
327
                if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
45✔
328
                        return true;
21✔
329

330
        return false;
3✔
331
}
332

333
int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
1✔
334
        DnsResourceRecord *rr;
1✔
335
        int r;
1✔
336

337
        /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
338

339
        DNS_ANSWER_FOREACH(rr, answer) {
2✔
340
                const char *p;
1✔
341

342
                if (rr->key->type != DNS_TYPE_NSEC3)
1✔
343
                        continue;
1✔
344

345
                p = dns_resource_key_name(rr->key);
×
346
                r = dns_name_parent(&p);
×
347
                if (r < 0)
×
348
                        return r;
×
UNCOV
349
                if (r == 0)
×
350
                        continue;
×
351

UNCOV
352
                r = dns_name_equal(p, zone);
×
UNCOV
353
                if (r != 0)
×
354
                        return r;
355
        }
356

357
        return false;
1✔
358
}
359

360
bool dns_answer_contains(DnsAnswer *answer, DnsResourceRecord *rr) {
6,634✔
361
        DnsResourceRecord *i;
6,634✔
362

363
        DNS_ANSWER_FOREACH(i, answer)
118,277✔
364
                if (dns_resource_record_equal(i, rr))
118,233✔
365
                        return true;
6,590✔
366

367
        return false;
44✔
368
}
369

370
int dns_answer_find_soa(
50✔
371
                DnsAnswer *a,
372
                const DnsResourceKey *key,
373
                DnsResourceRecord **ret,
374
                DnsAnswerFlags *ret_flags) {
375

376
        DnsResourceRecord *rr, *soa = NULL;
50✔
377
        DnsAnswerFlags rr_flags, soa_flags = 0;
50✔
378
        int r;
50✔
379

380
        assert(key);
50✔
381

382
        /* For a SOA record we can never find a matching SOA record */
383
        if (key->type == DNS_TYPE_SOA)
50✔
384
                goto not_found;
1✔
385

386
        DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
115✔
387
                r = dns_resource_key_match_soa(key, rr->key);
67✔
388
                if (r < 0)
67✔
389
                        return r;
1✔
390
                if (r > 0) {
66✔
391

392
                        if (soa) {
33✔
UNCOV
393
                                r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
×
394
                                if (r < 0)
×
395
                                        return r;
UNCOV
396
                                if (r > 0)
×
UNCOV
397
                                        continue;
×
398
                        }
399

400
                        soa = rr;
401
                        soa_flags = rr_flags;
402
                }
403
        }
404

405
        if (!soa)
48✔
406
                goto not_found;
15✔
407

408
        if (ret)
33✔
409
                *ret = soa;
33✔
410
        if (ret_flags)
33✔
411
                *ret_flags = soa_flags;
33✔
412

413
        return 1;
414

415
not_found:
16✔
416
        if (ret)
16✔
417
                *ret = NULL;
16✔
418
        if (ret_flags)
16✔
419
                *ret_flags = 0;
16✔
420

421
        return 0;
422
}
423

424
int dns_answer_find_cname_or_dname(
108✔
425
                DnsAnswer *a,
426
                const DnsResourceKey *key,
427
                DnsResourceRecord **ret,
428
                DnsAnswerFlags *ret_flags) {
429

430
        DnsResourceRecord *rr;
108✔
431
        DnsAnswerFlags rr_flags;
108✔
432
        int r;
108✔
433

434
        assert(key);
108✔
435

436
        /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
437
        if (!dns_type_may_redirect(key->type))
108✔
438
                return 0;
439

440
        DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
340✔
441
                r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
245✔
442
                if (r < 0)
245✔
443
                        return r;
11✔
444
                if (r > 0) {
245✔
445
                        if (ret)
11✔
UNCOV
446
                                *ret = rr;
×
447
                        if (ret_flags)
11✔
448
                                *ret_flags = rr_flags;
3✔
449
                        return 1;
11✔
450
                }
451
        }
452

453
        if (ret)
95✔
UNCOV
454
                *ret = NULL;
×
455
        if (ret_flags)
95✔
456
                *ret_flags = 0;
24✔
457

458
        return 0;
459
}
460

461
int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
220✔
462
        _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
220✔
463
        int r;
220✔
464

465
        assert(ret);
220✔
466

467
        if (a == b) {
220✔
468
                *ret = dns_answer_ref(a);
2✔
469
                return 0;
2✔
470
        }
471

472
        if (dns_answer_size(a) <= 0) {
325✔
473
                *ret = dns_answer_ref(b);
112✔
474
                return 0;
112✔
475
        }
476

477
        if (dns_answer_size(b) <= 0) {
212✔
478
                *ret = dns_answer_ref(a);
1✔
479
                return 0;
1✔
480
        }
481

482
        k = dns_answer_new(dns_answer_size(a) + dns_answer_size(b));
105✔
483
        if (!k)
105✔
484
                return -ENOMEM;
485

486
        r = dns_answer_add_raw_all(k, a);
105✔
487
        if (r < 0)
105✔
488
                return r;
489

490
        r = dns_answer_add_all(k, b);
105✔
491
        if (r < 0)
105✔
492
                return r;
493

494
        *ret = TAKE_PTR(k);
105✔
495

496
        return 0;
105✔
497
}
498

499
int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
216✔
500
        DnsAnswer *merged;
216✔
501
        int r;
216✔
502

503
        assert(a);
216✔
504

505
        r = dns_answer_merge(*a, b, &merged);
216✔
506
        if (r < 0)
216✔
507
                return r;
216✔
508

509
        DNS_ANSWER_REPLACE(*a, merged);
216✔
510
        return 0;
216✔
511
}
512

513
int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
220✔
514
        DnsAnswerItem *item;
220✔
515
        bool found = false;
220✔
516
        int r;
220✔
517

518
        assert(a);
220✔
519
        assert(key);
220✔
520

521
        /* Remove all entries matching the specified key from *a */
522

523
        DNS_ANSWER_FOREACH_ITEM(item, *a) {
1,068✔
524
                r = dns_resource_key_equal(item->rr->key, key);
628✔
525
                if (r < 0)
628✔
UNCOV
526
                        return r;
×
527
                if (r > 0) {
628✔
528
                        dns_answer_item_unref(ordered_set_remove((*a)->items, item));
203✔
529
                        found = true;
530
                }
531
        }
532

533
        if (!found)
220✔
534
                return 0;
535

536
        if (dns_answer_isempty(*a))
179✔
537
                *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
14✔
538

539
        return 1;
540
}
541

542
int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr) {
6✔
543
        _unused_ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr_ref = dns_resource_record_ref(rr);
12✔
544
        DnsAnswerItem *item;
6✔
545
        bool found = false;
6✔
546
        int r;
6✔
547

548
        assert(a);
6✔
549
        assert(rr);
6✔
550

551
        /* Remove all entries matching the specified RR from *a */
552

553
        DNS_ANSWER_FOREACH_ITEM(item, *a) {
24✔
554
                r = dns_resource_record_equal(item->rr, rr);
12✔
555
                if (r < 0)
12✔
UNCOV
556
                        return r;
×
557
                if (r > 0) {
12✔
558
                        dns_answer_item_unref(ordered_set_remove((*a)->items, item));
3✔
559
                        found = true;
560
                }
561
        }
562

563
        if (!found)
6✔
564
                return 0;
565

566
        if (dns_answer_isempty(*a))
3✔
567
                *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
1✔
568

569
        return 1;
570
}
571

572
int dns_answer_remove_by_answer_keys(DnsAnswer **a, DnsAnswer *b) {
83✔
573
        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *prev = NULL;
83✔
574
        DnsAnswerItem *item;
83✔
575
        int r;
83✔
576

577
        /* Removes all items from '*a' that have a matching key in 'b' */
578

579
        DNS_ANSWER_FOREACH_ITEM(item, b) {
122✔
580

581
                if (prev && dns_resource_key_equal(item->rr->key, prev)) /* Skip this one, we already looked at it */
38✔
UNCOV
582
                        continue;
×
583

584
                r = dns_answer_remove_by_key(a, item->rr->key);
38✔
585
                if (r < 0)
38✔
586
                        return r;
33✔
587
                if (!*a)
38✔
588
                        return 0; /* a is already empty. */
589

590
                /* Let's remember this entry's RR key, to optimize the loop a bit: if we have an RRset with
591
                 * more than one item then we don't need to remove the key multiple times */
592
                DNS_RESOURCE_KEY_REPLACE(prev, dns_resource_key_ref(item->rr->key));
93✔
593
        }
594

595
        return 0;
50✔
596
}
597

598
int dns_answer_copy_by_key(
204✔
599
                DnsAnswer **a,
600
                DnsAnswer *source,
601
                const DnsResourceKey *key,
602
                DnsAnswerFlags or_flags,
603
                DnsResourceRecord *rrsig) {
604

605
        DnsAnswerItem *item;
204✔
606
        int r;
204✔
607

608
        assert(a);
204✔
609
        assert(key);
204✔
610

611
        /* Copy all RRs matching the specified key from source into *a */
612

613
        DNS_ANSWER_FOREACH_ITEM(item, source) {
873✔
614

615
                r = dns_resource_key_equal(item->rr->key, key);
669✔
616
                if (r < 0)
669✔
UNCOV
617
                        return r;
×
618
                if (r == 0)
669✔
619
                        continue;
431✔
620

621
                r = dns_answer_add_extend(a, item->rr, item->ifindex, item->flags|or_flags, rrsig ?: item->rrsig);
238✔
622
                if (r < 0)
238✔
623
                        return r;
624
        }
625

626
        return 0;
204✔
627
}
628

629
int dns_answer_move_by_key(
172✔
630
                DnsAnswer **to,
631
                DnsAnswer **from,
632
                const DnsResourceKey *key,
633
                DnsAnswerFlags or_flags,
634
                DnsResourceRecord *rrsig) {
635

636
        int r;
172✔
637

638
        assert(to);
172✔
639
        assert(from);
172✔
640
        assert(key);
172✔
641

642
        r = dns_answer_copy_by_key(to, *from, key, or_flags, rrsig);
172✔
643
        if (r < 0)
172✔
644
                return r;
645

646
        return dns_answer_remove_by_key(from, key);
172✔
647
}
648

649
void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
9✔
650
        _cleanup_free_ DnsAnswerItem **items = NULL;
9✔
651
        DnsAnswerItem **p, *item;
9✔
652
        size_t n;
9✔
653

654
        n = dns_answer_size(a);
16✔
655
        if (n <= 1)
9✔
656
                return;
657

658
        /* RFC 4795, Section 2.6 suggests we should order entries
659
         * depending on whether the sender is a link-local address. */
660

661
        p = items = new(DnsAnswerItem*, n);
2✔
662
        if (!items)
2✔
UNCOV
663
                return (void) log_oom();
×
664

665
        /* Order preferred address records and other records to the beginning of the array */
666
        DNS_ANSWER_FOREACH_ITEM(item, a)
10✔
667
                if (dns_resource_record_is_link_local_address(item->rr) == prefer_link_local)
6✔
668
                        *p++ = dns_answer_item_ref(item);
3✔
669

670
        /* Order address records that are not preferred to the end of the array */
671
        DNS_ANSWER_FOREACH_ITEM(item, a)
10✔
672
                if (dns_resource_record_is_link_local_address(item->rr) != prefer_link_local)
6✔
673
                        *p++ = dns_answer_item_ref(item);
3✔
674

675
        assert((size_t) (p - items) == n);
2✔
676

677
        ordered_set_clear(a->items);
2✔
678
        for (size_t i = 0; i < n; i++)
8✔
679
                assert_se(ordered_set_put(a->items, items[i]) >= 0);
6✔
680
}
681

682
int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
8,922✔
683
        assert(a);
8,922✔
684

685
        if (n_free <= 0)
8,922✔
686
                return 0;
687

688
        if (!*a) {
8,847✔
689
                DnsAnswer *n;
694✔
690

691
                n = dns_answer_new(n_free);
694✔
692
                if (!n)
694✔
693
                        return -ENOMEM;
694

695
                *a = n;
694✔
696
                return 0;
694✔
697
        }
698

699
        if ((*a)->n_ref > 1)
8,153✔
700
                return -EBUSY;
701

702
        return dns_answer_reserve_internal(*a, n_free);
8,101✔
703
}
704

705
int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
8,819✔
706
        _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
8,819✔
707
        size_t ns;
8,819✔
708
        int r;
8,819✔
709

710
        assert(a);
8,819✔
711

712
        r = dns_answer_reserve(a, n_free);
8,819✔
713
        if (r != -EBUSY)
8,819✔
714
                return r;
715

716
        ns = dns_answer_size(*a);
52✔
717
        assert(ns <= UINT16_MAX); /* Maximum number of RRs we can stick into a DNS packet section */
52✔
718

719
        ns = saturate_add(ns, n_free, UINT16_MAX);
52✔
720

721
        n = dns_answer_new(ns);
52✔
722
        if (!n)
52✔
723
                return -ENOMEM;
724

725
        r = dns_answer_add_raw_all(n, *a);
52✔
726
        if (r < 0)
52✔
727
                return r;
728

729
        DNS_ANSWER_REPLACE(*a, TAKE_PTR(n));
52✔
730
        return 0;
52✔
731
}
732

733
/*
734
 * This function is not used in the code base, but is useful when debugging. Do not delete.
735
 */
736
void dns_answer_dump(DnsAnswer *answer, FILE *f) {
3✔
737
        DnsAnswerItem *item;
3✔
738

739
        if (!f)
3✔
UNCOV
740
                f = stdout;
×
741

742
        DNS_ANSWER_FOREACH_ITEM(item, answer) {
13✔
743
                const char *t;
10✔
744

745
                fputc('\t', f);
10✔
746

747
                t = dns_resource_record_to_string(item->rr);
10✔
748
                if (!t) {
10✔
UNCOV
749
                        log_oom();
×
UNCOV
750
                        continue;
×
751
                }
752

753
                fputs(t, f);
10✔
754
                fputs("\t;", f);
10✔
755
                fprintf(f, " ttl=%" PRIu32, item->rr->ttl);
10✔
756

757
                if (item->ifindex != 0)
10✔
758
                        fprintf(f, " ifindex=%i", item->ifindex);
10✔
759
                if (item->rrsig)
10✔
UNCOV
760
                        fputs(" rrsig", f);
×
761
                if (item->flags & DNS_ANSWER_AUTHENTICATED)
10✔
762
                        fputs(" authenticated", f);
1✔
763
                if (item->flags & DNS_ANSWER_CACHEABLE)
10✔
764
                        fputs(" cacheable", f);
7✔
765
                if (item->flags & DNS_ANSWER_SHARED_OWNER)
10✔
766
                        fputs(" shared-owner", f);
1✔
767
                if (item->flags & DNS_ANSWER_CACHE_FLUSH)
10✔
768
                        fputs(" cache-flush", f);
1✔
769
                if (item->flags & DNS_ANSWER_GOODBYE)
10✔
770
                        fputs(" goodbye", f);
1✔
771
                if (item->flags & DNS_ANSWER_SECTION_ANSWER)
10✔
772
                        fputs(" section-answer", f);
1✔
773
                if (item->flags & DNS_ANSWER_SECTION_AUTHORITY)
10✔
774
                        fputs(" section-authority", f);
1✔
775
                if (item->flags & DNS_ANSWER_SECTION_ADDITIONAL)
10✔
776
                        fputs(" section-additional", f);
1✔
777

778
                fputc('\n', f);
10✔
779
        }
780
}
3✔
781

782
int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
6✔
783
        DnsResourceRecord *rr;
6✔
784
        int r;
6✔
785

786
        assert(cname);
6✔
787

788
        /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
789
         * synthesized from it */
790

791
        if (cname->key->type != DNS_TYPE_CNAME)
6✔
792
                return 0;
793

794
        DNS_ANSWER_FOREACH(rr, a) {
5✔
795
                _cleanup_free_ char *n = NULL;
2✔
796

797
                if (rr->key->type != DNS_TYPE_DNAME)
2✔
798
                        continue;
×
799
                if (rr->key->class != cname->key->class)
2✔
UNCOV
800
                        continue;
×
801

802
                r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n);
2✔
803
                if (r < 0)
2✔
804
                        return r;
805
                if (r == 0)
2✔
806
                        continue;
2✔
807

UNCOV
808
                r = dns_name_equal(n, dns_resource_key_name(cname->key));
×
809
                if (r < 0)
×
810
                        return r;
UNCOV
811
                if (r > 0)
×
812
                        return 1;
813
        }
814

815
        return 0;
3✔
816
}
817

818
void dns_answer_randomize(DnsAnswer *a) {
75✔
819
        _cleanup_free_ DnsAnswerItem **items = NULL;
75✔
820
        DnsAnswerItem **p, *item;
75✔
821
        size_t n;
75✔
822

823
        /* Permutes the answer list randomly (Knuth shuffle) */
824

825
        n = dns_answer_size(a);
75✔
826
        if (n <= 1)
75✔
827
                return;
828

829
        p = items = new(DnsAnswerItem*, n);
75✔
830
        if (!items)
75✔
UNCOV
831
                return (void) log_oom();
×
832

833
        DNS_ANSWER_FOREACH_ITEM(item, a)
239✔
834
                *p++ = dns_answer_item_ref(item);
164✔
835

836
        assert((size_t) (p - items) == n);
75✔
837

838
        for (size_t i = 0; i < n; i++) {
239✔
839
                size_t k;
164✔
840

841
                k = random_u64_range(n);
164✔
842
                if (k == i)
164✔
843
                        continue;
78✔
844

845
                SWAP_TWO(items[i], items[k]);
164✔
846
        }
847

848
        ordered_set_clear(a->items);
75✔
849
        for (size_t i = 0; i < n; i++)
239✔
850
                assert_se(ordered_set_put(a->items, items[i]) >= 0);
164✔
851
}
852

853
uint32_t dns_answer_min_ttl(DnsAnswer *a) {
5,012✔
854
        uint32_t ttl = UINT32_MAX;
5,012✔
855
        DnsResourceRecord *rr;
5,012✔
856

857
        /* Return the smallest TTL of all RRs in this answer */
858

859
        DNS_ANSWER_FOREACH(rr, a) {
5,283✔
860
                /* Don't consider OPT (where the TTL field is used for other purposes than an actual TTL) */
861

862
                if (dns_type_is_pseudo(rr->key->type) ||
542✔
863
                    dns_class_is_pseudo(rr->key->class))
271✔
UNCOV
864
                        continue;
×
865

866
                ttl = MIN(ttl, rr->ttl);
271✔
867
        }
868

869
        return ttl;
5,012✔
870
}
871

872
int dns_answer_to_json(DnsAnswer *answer, sd_json_variant **ret) {
24✔
873
        int r;
24✔
874

875
        assert(ret);
24✔
876

877
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *ja = NULL;
24✔
878
        DnsResourceRecord *rr;
24✔
879
        DNS_ANSWER_FOREACH(rr, answer) {
532✔
880
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
508✔
881

882
                r = dns_resource_record_to_json(rr, &v);
508✔
883
                if (r < 0)
508✔
884
                        return r;
885

886
                r = dns_resource_record_to_wire_format(rr, /* canonical= */ false);
508✔
887
                if (r < 0)
508✔
888
                        return r;
889

890
                r = sd_json_variant_append_arraybo(
508✔
891
                                &ja,
892
                                SD_JSON_BUILD_PAIR_VARIANT("rr", v),
893
                                SD_JSON_BUILD_PAIR_BASE64("raw", rr->wire_format, rr->wire_format_size));
894
                if (r < 0)
508✔
895
                        return r;
896
        }
897

898
        if (!ja)
24✔
UNCOV
899
                return sd_json_variant_new_array(ret, /* array=*/ NULL, /* n= */ 0);
×
900

901
        *ret = TAKE_PTR(ja);
24✔
902
        return 0;
24✔
903
}
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