chiark / gitweb /
resolved: only cache answer RRs, never additional or authoritative RRs of responses
[elogind.git] / src / resolve / resolved-dns-zone.c
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 "list.h"
23
24 #include "resolved-dns-zone.h"
25 #include "resolved-dns-domain.h"
26 #include "resolved-dns-packet.h"
27
28 /* Never allow more than 1K entries */
29 #define ZONE_MAX 1024
30
31 typedef struct DnsZoneItem DnsZoneItem;
32
33 struct DnsZoneItem {
34         DnsResourceRecord *rr;
35         bool verified;
36         LIST_FIELDS(DnsZoneItem, by_key);
37         LIST_FIELDS(DnsZoneItem, by_name);
38 };
39
40 static void dns_zone_item_free(DnsZoneItem *i) {
41         if (!i)
42                 return;
43
44         dns_resource_record_unref(i->rr);
45         free(i);
46 }
47
48 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free);
49
50 static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) {
51         DnsZoneItem *first;
52
53         assert(z);
54
55         if (!i)
56                 return;
57
58         first = hashmap_get(z->by_key, i->rr->key);
59         LIST_REMOVE(by_key, first, i);
60         if (first)
61                 assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0);
62         else
63                 hashmap_remove(z->by_key, i->rr->key);
64
65         first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key));
66         LIST_REMOVE(by_name, first, i);
67         if (first)
68                 assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0);
69         else
70                 hashmap_remove(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key));
71
72         dns_zone_item_free(i);
73 }
74
75 void dns_zone_flush(DnsZone *z) {
76         DnsZoneItem *i;
77
78         assert(z);
79
80         while ((i = hashmap_first(z->by_key)))
81                 dns_zone_item_remove_and_free(z, i);
82
83         assert(hashmap_size(z->by_key) == 0);
84         assert(hashmap_size(z->by_name) == 0);
85
86         hashmap_free(z->by_key);
87         z->by_key = NULL;
88
89         hashmap_free(z->by_name);
90         z->by_name = NULL;
91 }
92
93 static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) {
94         DnsZoneItem *i;
95
96         assert(z);
97         assert(rr);
98
99         LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key))
100                 if (dns_resource_record_equal(i->rr, rr))
101                         return i;
102
103         return NULL;
104 }
105
106 void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) {
107         DnsZoneItem *i;
108
109         assert(z);
110         assert(rr);
111
112         i = dns_zone_get(z, rr);
113         if (i)
114                 dns_zone_item_remove_and_free(z, i);
115 }
116
117 static int dns_zone_init(DnsZone *z) {
118         int r;
119
120         assert(z);
121
122         r = hashmap_ensure_allocated(&z->by_key, dns_resource_key_hash_func, dns_resource_key_compare_func);
123         if (r < 0)
124                 return r;
125
126         r = hashmap_ensure_allocated(&z->by_name, dns_name_hash_func, dns_name_compare_func);
127         if (r < 0)
128                 return r;
129
130         return 0;
131 }
132
133 static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) {
134         DnsZoneItem *first;
135         int r;
136
137         first = hashmap_get(z->by_key, i->rr->key);
138         if (first) {
139                 LIST_PREPEND(by_key, first, i);
140                 assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0);
141         } else {
142                 r = hashmap_put(z->by_key, i->rr->key, i);
143                 if (r < 0)
144                         return r;
145         }
146
147         first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key));
148         if (first) {
149                 LIST_PREPEND(by_name, first, i);
150                 assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0);
151         } else {
152                 r = hashmap_put(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key), i);
153                 if (r < 0)
154                         return r;
155         }
156
157         return 0;
158 }
159
160 int dns_zone_put(DnsZone *z, DnsResourceRecord *rr) {
161         _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL;
162         DnsZoneItem *existing;
163         int r;
164
165         assert(z);
166         assert(rr);
167
168         existing = dns_zone_get(z, rr);
169         if (existing)
170                 return 0;
171
172         r = dns_zone_init(z);
173         if (r < 0)
174                 return r;
175
176         i = new0(DnsZoneItem, 1);
177         if (!i)
178                 return -ENOMEM;
179
180         i->rr = dns_resource_record_ref(rr);
181
182         r = dns_zone_link_item(z, i);
183         if (r < 0)
184                 return r;
185
186         i = NULL;
187         return 0;
188 }
189
190 int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
191         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
192         int r;
193         unsigned i, n = 0;
194         bool has_other_rrs = false;
195
196         assert(z);
197         assert(q);
198         assert(ret);
199
200         if (q->n_keys <= 0) {
201                 *ret = NULL;
202                 return 0;
203         }
204
205         for (i = 0; i < q->n_keys; i++) {
206                 DnsZoneItem *j;
207
208                 j = hashmap_get(z->by_key, q->keys[i]);
209                 if (!j) {
210                         if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])))
211                                 has_other_rrs = true;
212
213                         continue;
214                 }
215
216                 LIST_FOREACH(by_name, j, j)
217                         n++;
218         }
219
220         if (n <= 0) {
221                 *ret = NULL;
222                 return has_other_rrs;
223         }
224
225         answer = dns_answer_new(n);
226         if (!answer)
227                 return -ENOMEM;
228
229         for (i = 0; i < q->n_keys; i++) {
230                 DnsZoneItem *j;
231
232                 j = hashmap_get(z->by_key, q->keys[i]);
233                 LIST_FOREACH(by_key, j, j) {
234                         r = dns_answer_add(answer, j->rr);
235                         if (r < 0)
236                                 return r;
237                 }
238         }
239
240         *ret = answer;
241         answer = NULL;
242
243         return 1;
244 }