chiark / gitweb /
resolved: don't accept messages with ANY RRs
[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         if (rr->key->class == DNS_CLASS_ANY)
169                 return -EINVAL;
170         if (rr->key->type == DNS_TYPE_ANY)
171                 return -EINVAL;
172
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
195 int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret) {
196         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
197         int r;
198         unsigned i, n = 0;
199         bool has_other_rrs = false;
200
201         assert(z);
202         assert(q);
203         assert(ret);
204
205         if (q->n_keys <= 0) {
206                 *ret = NULL;
207                 return 0;
208         }
209
210         for (i = 0; i < q->n_keys; i++) {
211                 DnsZoneItem *j;
212
213                 j = hashmap_get(z->by_key, q->keys[i]);
214                 if (!j) {
215                         if (hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])))
216                                 has_other_rrs = true;
217
218                         continue;
219                 }
220
221                 LIST_FOREACH(by_name, j, j)
222                         n++;
223         }
224
225         if (n <= 0) {
226                 *ret = NULL;
227                 return has_other_rrs;
228         }
229
230         answer = dns_answer_new(n);
231         if (!answer)
232                 return -ENOMEM;
233
234         for (i = 0; i < q->n_keys; i++) {
235                 DnsZoneItem *j;
236
237                 j = hashmap_get(z->by_key, q->keys[i]);
238                 LIST_FOREACH(by_key, j, j) {
239                         r = dns_answer_add(answer, j->rr);
240                         if (r < 0)
241                                 return r;
242                 }
243         }
244
245         *ret = answer;
246         answer = NULL;
247
248         return 1;
249 }