]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | /* |
2 | * iterator/iter_priv.c - iterative resolver private address and domain store | |
3 | * | |
4 | * Copyright (c) 2008, NLnet Labs. All rights reserved. | |
5 | * | |
6 | * This software is open source. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * Redistributions of source code must retain the above copyright notice, | |
13 | * this list of conditions and the following disclaimer. | |
14 | * | |
15 | * Redistributions in binary form must reproduce the above copyright notice, | |
16 | * this list of conditions and the following disclaimer in the documentation | |
17 | * and/or other materials provided with the distribution. | |
18 | * | |
19 | * Neither the name of the NLNET LABS nor the names of its contributors may | |
20 | * be used to endorse or promote products derived from this software without | |
21 | * specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
27 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |
29 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
30 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
34 | */ | |
35 | ||
36 | /** | |
37 | * \file | |
38 | * | |
39 | * This file contains functions to assist the iterator module. | |
40 | * Keep track of the private addresses and lookup fast. | |
41 | */ | |
42 | ||
43 | #include "config.h" | |
44 | #include "iterator/iter_priv.h" | |
45 | #include "util/regional.h" | |
46 | #include "util/log.h" | |
47 | #include "util/config_file.h" | |
48 | #include "util/data/dname.h" | |
49 | #include "util/data/msgparse.h" | |
50 | #include "util/net_help.h" | |
51 | #include "util/storage/dnstree.h" | |
52 | #include "ldns/str2wire.h" | |
53 | #include "ldns/sbuffer.h" | |
54 | ||
55 | struct iter_priv* priv_create(void) | |
56 | { | |
57 | struct iter_priv* priv = (struct iter_priv*)calloc(1, sizeof(*priv)); | |
58 | if(!priv) | |
59 | return NULL; | |
60 | priv->region = regional_create(); | |
61 | if(!priv->region) { | |
62 | priv_delete(priv); | |
63 | return NULL; | |
64 | } | |
65 | addr_tree_init(&priv->a); | |
66 | name_tree_init(&priv->n); | |
67 | return priv; | |
68 | } | |
69 | ||
70 | void priv_delete(struct iter_priv* priv) | |
71 | { | |
72 | if(!priv) return; | |
73 | regional_destroy(priv->region); | |
74 | free(priv); | |
75 | } | |
76 | ||
77 | /** Read private-addr declarations from config */ | |
78 | static int read_addrs(struct iter_priv* priv, struct config_file* cfg) | |
79 | { | |
80 | /* parse addresses, report errors, insert into tree */ | |
81 | struct config_strlist* p; | |
82 | struct addr_tree_node* n; | |
83 | struct sockaddr_storage addr; | |
84 | int net; | |
85 | socklen_t addrlen; | |
86 | ||
87 | for(p = cfg->private_address; p; p = p->next) { | |
88 | log_assert(p->str); | |
89 | if(!netblockstrtoaddr(p->str, UNBOUND_DNS_PORT, &addr, | |
90 | &addrlen, &net)) { | |
91 | log_err("cannot parse private-address: %s", p->str); | |
92 | return 0; | |
93 | } | |
94 | n = (struct addr_tree_node*)regional_alloc(priv->region, | |
95 | sizeof(*n)); | |
96 | if(!n) { | |
97 | log_err("out of memory"); | |
98 | return 0; | |
99 | } | |
100 | if(!addr_tree_insert(&priv->a, n, &addr, addrlen, net)) { | |
101 | verbose(VERB_QUERY, "ignoring duplicate " | |
102 | "private-address: %s", p->str); | |
103 | } | |
104 | } | |
105 | return 1; | |
106 | } | |
107 | ||
108 | /** Read private-domain declarations from config */ | |
109 | static int read_names(struct iter_priv* priv, struct config_file* cfg) | |
110 | { | |
111 | /* parse names, report errors, insert into tree */ | |
112 | struct config_strlist* p; | |
113 | struct name_tree_node* n; | |
114 | uint8_t* nm, *nmr; | |
115 | size_t nm_len; | |
116 | int nm_labs; | |
117 | ||
118 | for(p = cfg->private_domain; p; p = p->next) { | |
119 | log_assert(p->str); | |
120 | nm = sldns_str2wire_dname(p->str, &nm_len); | |
121 | if(!nm) { | |
122 | log_err("cannot parse private-domain: %s", p->str); | |
123 | return 0; | |
124 | } | |
125 | nm_labs = dname_count_size_labels(nm, &nm_len); | |
126 | nmr = (uint8_t*)regional_alloc_init(priv->region, nm, nm_len); | |
127 | free(nm); | |
128 | if(!nmr) { | |
129 | log_err("out of memory"); | |
130 | return 0; | |
131 | } | |
132 | n = (struct name_tree_node*)regional_alloc(priv->region, | |
133 | sizeof(*n)); | |
134 | if(!n) { | |
135 | log_err("out of memory"); | |
136 | return 0; | |
137 | } | |
138 | if(!name_tree_insert(&priv->n, n, nmr, nm_len, nm_labs, | |
139 | LDNS_RR_CLASS_IN)) { | |
140 | verbose(VERB_QUERY, "ignoring duplicate " | |
141 | "private-domain: %s", p->str); | |
142 | } | |
143 | } | |
144 | return 1; | |
145 | } | |
146 | ||
147 | int priv_apply_cfg(struct iter_priv* priv, struct config_file* cfg) | |
148 | { | |
149 | /* empty the current contents */ | |
150 | regional_free_all(priv->region); | |
151 | addr_tree_init(&priv->a); | |
152 | name_tree_init(&priv->n); | |
153 | ||
154 | /* read new contents */ | |
155 | if(!read_addrs(priv, cfg)) | |
156 | return 0; | |
157 | if(!read_names(priv, cfg)) | |
158 | return 0; | |
159 | ||
160 | /* prepare for lookups */ | |
161 | addr_tree_init_parents(&priv->a); | |
162 | name_tree_init_parents(&priv->n); | |
163 | return 1; | |
164 | } | |
165 | ||
166 | /** | |
167 | * See if an address is blocked. | |
168 | * @param priv: structure for address storage. | |
169 | * @param addr: address to check | |
170 | * @param addrlen: length of addr. | |
171 | * @return: true if the address must not be queried. false if unlisted. | |
172 | */ | |
173 | static int | |
174 | priv_lookup_addr(struct iter_priv* priv, struct sockaddr_storage* addr, | |
175 | socklen_t addrlen) | |
176 | { | |
177 | return addr_tree_lookup(&priv->a, addr, addrlen) != NULL; | |
178 | } | |
179 | ||
180 | /** | |
181 | * See if a name is whitelisted. | |
182 | * @param priv: structure for address storage. | |
183 | * @param pkt: the packet (for compression ptrs). | |
184 | * @param name: name to check. | |
185 | * @param name_len: uncompressed length of the name to check. | |
186 | * @param dclass: class to check. | |
187 | * @return: true if the name is OK. false if unlisted. | |
188 | */ | |
189 | static int | |
190 | priv_lookup_name(struct iter_priv* priv, sldns_buffer* pkt, | |
191 | uint8_t* name, size_t name_len, uint16_t dclass) | |
192 | { | |
193 | size_t len; | |
194 | uint8_t decomp[256]; | |
195 | int labs; | |
196 | if(name_len >= sizeof(decomp)) | |
197 | return 0; | |
198 | dname_pkt_copy(pkt, decomp, name); | |
199 | labs = dname_count_size_labels(decomp, &len); | |
200 | log_assert(name_len == len); | |
201 | return name_tree_lookup(&priv->n, decomp, len, labs, dclass) != NULL; | |
202 | } | |
203 | ||
204 | size_t priv_get_mem(struct iter_priv* priv) | |
205 | { | |
206 | if(!priv) return 0; | |
207 | return sizeof(*priv) + regional_get_mem(priv->region); | |
208 | } | |
209 | ||
210 | /** remove RR from msgparse RRset, return true if rrset is entirely bad */ | |
211 | static int | |
212 | remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, | |
213 | struct rr_parse* prev, struct rr_parse** rr, struct sockaddr_storage* addr, socklen_t addrlen) | |
214 | { | |
215 | if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) { | |
216 | uint8_t buf[LDNS_MAX_DOMAINLEN+1]; | |
217 | dname_pkt_copy(pkt, buf, rrset->dname); | |
218 | log_name_addr(VERB_QUERY, str, buf, addr, addrlen); | |
219 | } | |
220 | if(prev) | |
221 | prev->next = (*rr)->next; | |
222 | else rrset->rr_first = (*rr)->next; | |
223 | if(rrset->rr_last == *rr) | |
224 | rrset->rr_last = prev; | |
225 | rrset->rr_count --; | |
226 | rrset->size -= (*rr)->size; | |
227 | /* rr struct still exists, but is unlinked, so that in the for loop | |
228 | * the rr->next works fine to continue. */ | |
229 | return rrset->rr_count == 0; | |
230 | } | |
231 | ||
232 | int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, | |
233 | struct rrset_parse* rrset) | |
234 | { | |
235 | if(priv->a.count == 0) | |
236 | return 0; /* there are no blocked addresses */ | |
237 | ||
238 | /* see if it is a private name, that is allowed to have any */ | |
239 | if(priv_lookup_name(priv, pkt, rrset->dname, rrset->dname_len, | |
240 | ntohs(rrset->rrset_class))) { | |
241 | return 0; | |
242 | } else { | |
243 | /* so its a public name, check the address */ | |
244 | socklen_t len; | |
245 | struct rr_parse* rr, *prev = NULL; | |
246 | if(rrset->type == LDNS_RR_TYPE_A) { | |
247 | struct sockaddr_storage addr; | |
248 | struct sockaddr_in sa; | |
249 | ||
250 | len = (socklen_t)sizeof(sa); | |
251 | memset(&sa, 0, len); | |
252 | sa.sin_family = AF_INET; | |
253 | sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT); | |
254 | for(rr = rrset->rr_first; rr; rr = rr->next) { | |
255 | if(sldns_read_uint16(rr->ttl_data+4) | |
256 | != INET_SIZE) { | |
257 | prev = rr; | |
258 | continue; | |
259 | } | |
260 | memmove(&sa.sin_addr, rr->ttl_data+4+2, | |
261 | INET_SIZE); | |
262 | memmove(&addr, &sa, len); | |
263 | if(priv_lookup_addr(priv, &addr, len)) { | |
264 | if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) | |
265 | return 1; | |
266 | continue; | |
267 | } | |
268 | prev = rr; | |
269 | } | |
270 | } else if(rrset->type == LDNS_RR_TYPE_AAAA) { | |
271 | struct sockaddr_storage addr; | |
272 | struct sockaddr_in6 sa; | |
273 | len = (socklen_t)sizeof(sa); | |
274 | memset(&sa, 0, len); | |
275 | sa.sin6_family = AF_INET6; | |
276 | sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT); | |
277 | for(rr = rrset->rr_first; rr; rr = rr->next) { | |
278 | if(sldns_read_uint16(rr->ttl_data+4) | |
279 | != INET6_SIZE) { | |
280 | prev = rr; | |
281 | continue; | |
282 | } | |
283 | memmove(&sa.sin6_addr, rr->ttl_data+4+2, | |
284 | INET6_SIZE); | |
285 | memmove(&addr, &sa, len); | |
286 | if(priv_lookup_addr(priv, &addr, len)) { | |
287 | if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) | |
288 | return 1; | |
289 | continue; | |
290 | } | |
291 | prev = rr; | |
292 | } | |
293 | } | |
294 | } | |
295 | return 0; | |
296 | } |