]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-zone.c
resolved: don't attempt to order empty answer array
[thirdparty/systemd.git] / src / resolve / resolved-dns-zone.c
CommitLineData
623a4c97
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 "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
31typedef struct DnsZoneItem DnsZoneItem;
32
33struct DnsZoneItem {
34 DnsResourceRecord *rr;
35 bool verified;
36 LIST_FIELDS(DnsZoneItem, by_key);
37 LIST_FIELDS(DnsZoneItem, by_name);
38};
39
40static 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
48DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free);
49
50static 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
75void 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
93static 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
106void 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
117static 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
133static 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
160int 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
1d3b690f
LP
168 if (rr->key->class == DNS_CLASS_ANY)
169 return -EINVAL;
170 if (rr->key->type == DNS_TYPE_ANY)
171 return -EINVAL;
172
623a4c97
LP
173 existing = dns_zone_get(z, rr);
174 if (existing)
175 return 0;
176
177 r = dns_zone_init(z);
178 if (r < 0)
179 return r;
180
181 i = new0(DnsZoneItem, 1);
182 if (!i)
183 return -ENOMEM;
184
185 i->rr = dns_resource_record_ref(rr);
186
187 r = dns_zone_link_item(z, i);
188 if (r < 0)
189 return r;
190
191 i = NULL;
192 return 0;
193}
194
8bf52d3d
LP
195int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa) {
196 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
197 unsigned i, n_answer = 0, n_soa = 0;
d5323661 198 int r;
623a4c97
LP
199
200 assert(z);
201 assert(q);
8bf52d3d
LP
202 assert(ret_answer);
203 assert(ret_soa);
623a4c97
LP
204
205 if (q->n_keys <= 0) {
8bf52d3d
LP
206 *ret_answer = NULL;
207 *ret_soa = NULL;
623a4c97
LP
208 return 0;
209 }
210
8bf52d3d 211 /* First iteration, count what we have */
623a4c97
LP
212 for (i = 0; i < q->n_keys; i++) {
213 DnsZoneItem *j;
214
d5323661
LP
215 if (q->keys[i]->type == DNS_TYPE_ANY ||
216 q->keys[i]->class == DNS_CLASS_ANY) {
217 int k;
218
219 /* If this is a generic match, then we have to
220 * go through the list by the name and look
221 * for everything manually */
222
223 j = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
224 LIST_FOREACH(by_name, j, j) {
d5323661
LP
225 k = dns_resource_key_match_rr(q->keys[i], j->rr);
226 if (k < 0)
227 return k;
228 if (k == 0)
8bf52d3d
LP
229 n_soa++;
230 else
231 n_answer++;
d5323661
LP
232 }
233
234 } else {
235 j = hashmap_get(z->by_key, q->keys[i]);
8bf52d3d
LP
236 if (j) {
237 LIST_FOREACH(by_key, j, j)
238 n_answer++;
239 } else {
d5323661 240 if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])))
8bf52d3d 241 n_soa ++;
d5323661 242 }
d5323661 243 }
623a4c97
LP
244 }
245
8bf52d3d
LP
246 if (n_answer <= 0 && n_soa <= 0) {
247 *ret_answer = NULL;
248 *ret_soa = NULL;
249 return 0;
623a4c97
LP
250 }
251
8bf52d3d
LP
252 if (n_answer > 0) {
253 answer = dns_answer_new(n_answer);
254 if (!answer)
255 return -ENOMEM;
256 }
623a4c97 257
8bf52d3d
LP
258 if (n_soa > 0) {
259 soa = dns_answer_new(n_soa);
260 if (!soa)
261 return -ENOMEM;
262 }
263
264 /* Second iteration, actually add the RRs to the answers */
623a4c97
LP
265 for (i = 0; i < q->n_keys; i++) {
266 DnsZoneItem *j;
267
d5323661
LP
268 if (q->keys[i]->type == DNS_TYPE_ANY ||
269 q->keys[i]->class == DNS_CLASS_ANY) {
270 int k;
271
272 j = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
273 LIST_FOREACH(by_name, j, j) {
274 k = dns_resource_key_match_rr(q->keys[i], j->rr);
275 if (k < 0)
276 return k;
277 if (k == 0)
8bf52d3d
LP
278 r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]));
279 else
280 r = dns_answer_add(answer, j->rr);
d5323661
LP
281 if (r < 0)
282 return r;
283 }
284 } else {
285
286 j = hashmap_get(z->by_key, q->keys[i]);
8bf52d3d
LP
287 if (j) {
288 LIST_FOREACH(by_key, j, j) {
289 r = dns_answer_add(answer, j->rr);
290 if (r < 0)
291 return r;
292 }
293 } else {
294 if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]))) {
295 r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]));
296 if (r < 0)
297 return r;
298 }
d5323661 299 }
623a4c97
LP
300 }
301 }
302
8bf52d3d 303 *ret_answer = answer;
623a4c97
LP
304 answer = NULL;
305
8bf52d3d
LP
306 *ret_soa = soa;
307 soa = NULL;
308
623a4c97
LP
309 return 1;
310}