]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-cache.c
resolved: fix bus signatures to follow family as int change
[thirdparty/systemd.git] / src / resolve / resolved-dns-cache.c
CommitLineData
322345fd
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include "resolved-dns-cache.h"
23
24#define CACHE_MAX 1024
25#define CACHE_TTL_MAX_USEC (10 * USEC_PER_MINUTE)
26
27static void dns_cache_item_free(DnsCacheItem *i) {
28 if (!i)
29 return;
30
31 dns_resource_record_unref(i->rr);
32 free(i);
33}
34
35DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free);
36
37static void dns_cache_item_remove_and_free(DnsCache *c, DnsCacheItem *i) {
38 DnsCacheItem *first;
39
40 assert(c);
41
42 if (!i)
43 return;
44
45 first = hashmap_get(c->rrsets, &i->rr->key);
46 LIST_REMOVE(rrsets, first, i);
47
48 if (first)
49 assert_se(hashmap_replace(c->rrsets, &first->rr->key, first) >= 0);
50 else
51 hashmap_remove(c->rrsets, &i->rr->key);
52
53 prioq_remove(c->expire, i, &i->expire_prioq_idx);
54
55 dns_cache_item_free(i);
56}
57
58void dns_cache_flush(DnsCache *c) {
59 DnsCacheItem *i;
60
61 assert(c);
62
63 while ((i = hashmap_first(c->rrsets)))
64 dns_cache_item_remove_and_free(c, i);
65
66 assert(hashmap_size(c->rrsets) == 0);
67 assert(prioq_size(c->expire) == 0);
68
69 hashmap_free(c->rrsets);
70 c->rrsets = NULL;
71
72 prioq_free(c->expire);
73 c->expire = NULL;
74}
75
76void dns_cache_remove(DnsCache *c, DnsResourceKey *key) {
77 DnsCacheItem *i;
78
79 assert(c);
80 assert(key);
81
82 while ((i = hashmap_get(c->rrsets, &key)))
83 dns_cache_item_remove_and_free(c, i);
84}
85
86static void dns_cache_make_space(DnsCache *c, unsigned add) {
87 assert(c);
88
89 if (add <= 0)
90 return;
91
92 /* Makes space for n new entries. Note that we actually allow
93 * the cache to grow beyond CACHE_MAX, but only when we shall
94 * add more RRs to the cache than CACHE_MAX at once. In that
95 * case the cache will be emptied completely otherwise. */
96
97 for (;;) {
98 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
99 DnsCacheItem *i;
100
101 if (prioq_size(c->expire) <= 0)
102 break;
103
104 if (prioq_size(c->expire) + add < CACHE_MAX)
105 break;
106
107 i = prioq_peek(c->expire);
108 rr = dns_resource_record_ref(i->rr);
109 dns_cache_remove(c, &rr->key);
110 }
111}
112
113void dns_cache_prune(DnsCache *c) {
114 usec_t t = 0;
115
116 assert(c);
117
118 /* Remove all entries that are past their TTL */
119
120 for (;;) {
121 DnsCacheItem *i;
122 usec_t ttl;
123
124 i = prioq_peek(c->expire);
125 if (!i)
126 break;
127
128 ttl = i->rr->ttl * USEC_PER_SEC;
129 if (ttl > CACHE_TTL_MAX_USEC)
130 ttl = CACHE_TTL_MAX_USEC;
131
132 if (t <= 0)
133 t = now(CLOCK_MONOTONIC);
134
135 if (i->timestamp + ttl > t)
136 break;
137
138 dns_cache_remove(c, &i->rr->key);
139 }
140}
141
142static int dns_cache_item_prioq_compare_func(const void *a, const void *b) {
143 usec_t t, z;
144 const DnsCacheItem *x = a, *y = b;
145
146 t = x->timestamp + x->rr->ttl * USEC_PER_SEC;
147 z = y->timestamp + y->rr->ttl * USEC_PER_SEC;
148
149 if (t < z)
150 return -1;
151 if (t > z)
152 return 1;
153 return 0;
154}
155
156static void dns_cache_item_update(DnsCache *c, DnsCacheItem *i, DnsResourceRecord *rr, usec_t timestamp) {
157 assert(c);
158 assert(i);
159 assert(rr);
160
161 if (!i->rrsets_prev) {
162 /* We are the first item in the list, we need to
163 * update the key used in the hashmap */
164
165 assert_se(hashmap_replace(c->rrsets, &rr->key, i) >= 0);
166 }
167
168 dns_resource_record_unref(i->rr);
169 i->rr = dns_resource_record_ref(rr);
170
171 i->timestamp = timestamp;
172
173 prioq_reshuffle(c->expire, i, &i->expire_prioq_idx);
174}
175
176int dns_cache_put(DnsCache *c, DnsResourceRecord *rr, usec_t timestamp) {
177 _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
178 DnsCacheItem *first = NULL, *existing;
179 int r;
180
181 assert(c);
182 assert(rr);
183
184 /* New TTL is 0? Delete the entry... */
185 if (rr->ttl <= 0) {
186 dns_cache_remove(c, &rr->key);
187 return 0;
188 }
189
190 /* Entry exists already? Update TTL and timestamp */
191 existing = dns_cache_get(c, rr);
192 if (existing) {
193 dns_cache_item_update(c, existing, rr, timestamp);
194 return 0;
195 }
196
197 /* Otherwise, add the new RR */
198 r = prioq_ensure_allocated(&c->expire, dns_cache_item_prioq_compare_func);
199 if (r < 0)
200 return r;
201
202 r = hashmap_ensure_allocated(&c->rrsets, dns_resource_key_hash_func, dns_resource_key_compare_func);
203 if (r < 0)
204 return r;
205
206 dns_cache_make_space(c, 1);
207
208 i = new0(DnsCacheItem, 1);
209 if (!i)
210 return -ENOMEM;
211
212 i->rr = dns_resource_record_ref(rr);
213 i->timestamp = timestamp;
214 i->expire_prioq_idx = PRIOQ_IDX_NULL;
215
216 r = prioq_put(c->expire, i, &i->expire_prioq_idx);
217 if (r < 0)
218 return r;
219
220 first = hashmap_get(c->rrsets, &i->rr->key);
221 if (first) {
222 LIST_PREPEND(rrsets, first, i);
223 assert_se(hashmap_replace(c->rrsets, &first->rr->key, first) >= 0);
224 } else {
225 r = hashmap_put(c->rrsets, &i->rr->key, i);
226 if (r < 0) {
227 prioq_remove(c->expire, i, &i->expire_prioq_idx);
228 return r;
229 }
230 }
231
232 i = NULL;
233
234 return 0;
235}
236
237int dns_cache_put_rrs(DnsCache *c, DnsResourceRecord **rrs, unsigned n_rrs, usec_t timestamp) {
238 unsigned i, added = 0;
239 int r;
240
241 assert(c);
242
243 if (n_rrs <= 0)
244 return 0;
245
246 assert(rrs);
247
248 /* First iteration, delete all matching old RRs, so that we
249 * only keep complete rrsets in place. */
250 for (i = 0; i < n_rrs; i++)
251 dns_cache_remove(c, &rrs[i]->key);
252
253 dns_cache_make_space(c, n_rrs);
254
255 /* Second iteration, add in new RRs */
256 for (added = 0; added < n_rrs; added++) {
257 if (timestamp <= 0)
258 timestamp = now(CLOCK_MONOTONIC);
259
260 r = dns_cache_put(c, rrs[added], timestamp);
261 if (r < 0)
262 goto fail;
263
264 }
265
266 return 0;
267
268fail:
269 /* Adding all RRs failed. Let's clean up what we already
270 * added, just in case */
271
272 for (i = 0; i < added; i++)
273 dns_cache_remove(c, &rrs[i]->key);
274
275 return r;
276}
277
278DnsCacheItem* dns_cache_lookup(DnsCache *c, DnsResourceKey *key) {
279 assert(c);
280 assert(key);
281
282 return hashmap_get(c->rrsets, key);
283}
284
285DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) {
286 DnsCacheItem *i;
287
288 assert(c);
289 assert(rr);
290
291 LIST_FOREACH(rrsets, i, hashmap_get(c->rrsets, &rr->key))
292 if (dns_resource_record_equal(i->rr, rr))
293 return i;
294
295 return NULL;
296}
297
298int dns_cache_lookup_many(DnsCache *c, DnsResourceKey *keys, unsigned n_keys, DnsResourceRecord ***rrs) {
299 DnsResourceRecord **p = NULL;
300 size_t allocated = 0, used = 0;
301 unsigned i;
302 int r;
303
304 assert(c);
305 assert(rrs);
306
307 if (n_keys <= 0) {
308 *rrs = NULL;
309 return 0;
310 }
311
312 assert(keys);
313
314 for (i = 0; i < n_keys; i++) {
315 DnsCacheItem *j;
316
317 j = dns_cache_lookup(c, &keys[i]);
318 if (!j) {
319 *rrs = NULL;
320 r = 0;
321 goto fail;
322 }
323
324 LIST_FOREACH(rrsets, j, j) {
325
326 if (!GREEDY_REALLOC(p, allocated, used+1)) {
327 r = -ENOMEM;
328 goto fail;
329 }
330
331 p[used++] = dns_resource_record_ref(j->rr);
332 }
333 }
334
335 *rrs = p;
336 return (int) used;
337
338fail:
339 dns_resource_record_freev(p, used);
340 return r;
341}