/src/unbound/respip/respip.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * respip/respip.c - filtering response IP module |
3 | | */ |
4 | | |
5 | | /** |
6 | | * \file |
7 | | * |
8 | | * This file contains a module that inspects a result of recursive resolution |
9 | | * to see if any IP address record should trigger a special action. |
10 | | * If applicable these actions can modify the original response. |
11 | | */ |
12 | | #include "config.h" |
13 | | |
14 | | #include "services/localzone.h" |
15 | | #include "services/authzone.h" |
16 | | #include "services/cache/dns.h" |
17 | | #include "sldns/str2wire.h" |
18 | | #include "util/config_file.h" |
19 | | #include "util/fptr_wlist.h" |
20 | | #include "util/module.h" |
21 | | #include "util/net_help.h" |
22 | | #include "util/regional.h" |
23 | | #include "util/data/msgreply.h" |
24 | | #include "util/storage/dnstree.h" |
25 | | #include "respip/respip.h" |
26 | | #include "services/view.h" |
27 | | #include "sldns/rrdef.h" |
28 | | #include "util/data/dname.h" |
29 | | |
30 | | |
31 | | /** Subset of resp_addr.node, used for inform-variant logging */ |
32 | | struct respip_addr_info { |
33 | | struct sockaddr_storage addr; |
34 | | socklen_t addrlen; |
35 | | int net; |
36 | | }; |
37 | | |
38 | | /** Query state regarding the response-ip module. */ |
39 | | enum respip_state { |
40 | | /** |
41 | | * The general state. Unless CNAME chasing takes place, all processing |
42 | | * is completed in this state without any other asynchronous event. |
43 | | */ |
44 | | RESPIP_INIT = 0, |
45 | | |
46 | | /** |
47 | | * A subquery for CNAME chasing is completed. |
48 | | */ |
49 | | RESPIP_SUBQUERY_FINISHED |
50 | | }; |
51 | | |
52 | | /** Per query state for the response-ip module. */ |
53 | | struct respip_qstate { |
54 | | enum respip_state state; |
55 | | }; |
56 | | |
57 | | struct respip_set* |
58 | | respip_set_create(void) |
59 | 0 | { |
60 | 0 | struct respip_set* set = calloc(1, sizeof(*set)); |
61 | 0 | if(!set) |
62 | 0 | return NULL; |
63 | 0 | set->region = regional_create(); |
64 | 0 | if(!set->region) { |
65 | 0 | free(set); |
66 | 0 | return NULL; |
67 | 0 | } |
68 | 0 | addr_tree_init(&set->ip_tree); |
69 | 0 | lock_rw_init(&set->lock); |
70 | 0 | return set; |
71 | 0 | } |
72 | | |
73 | | /** helper traverse to delete resp_addr nodes */ |
74 | | static void |
75 | | resp_addr_del(rbnode_type* n, void* ATTR_UNUSED(arg)) |
76 | 0 | { |
77 | 0 | struct resp_addr* r = (struct resp_addr*)n->key; |
78 | 0 | lock_rw_destroy(&r->lock); |
79 | | #ifdef THREADS_DISABLED |
80 | | (void)r; |
81 | | #endif |
82 | 0 | } |
83 | | |
84 | | void |
85 | | respip_set_delete(struct respip_set* set) |
86 | 0 | { |
87 | 0 | if(!set) |
88 | 0 | return; |
89 | 0 | lock_rw_destroy(&set->lock); |
90 | 0 | traverse_postorder(&set->ip_tree, resp_addr_del, NULL); |
91 | 0 | regional_destroy(set->region); |
92 | 0 | free(set); |
93 | 0 | } |
94 | | |
95 | | struct rbtree_type* |
96 | | respip_set_get_tree(struct respip_set* set) |
97 | 0 | { |
98 | 0 | if(!set) |
99 | 0 | return NULL; |
100 | 0 | return &set->ip_tree; |
101 | 0 | } |
102 | | |
103 | | struct resp_addr* |
104 | | respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr, |
105 | | socklen_t addrlen, int net, int create, const char* ipstr) |
106 | 0 | { |
107 | 0 | struct resp_addr* node; |
108 | 0 | log_assert(set); |
109 | 0 | node = (struct resp_addr*)addr_tree_find(&set->ip_tree, addr, addrlen, net); |
110 | 0 | if(!node && create) { |
111 | 0 | node = regional_alloc_zero(set->region, sizeof(*node)); |
112 | 0 | if(!node) { |
113 | 0 | log_err("out of memory"); |
114 | 0 | return NULL; |
115 | 0 | } |
116 | 0 | lock_rw_init(&node->lock); |
117 | 0 | node->action = respip_none; |
118 | 0 | if(!addr_tree_insert(&set->ip_tree, &node->node, addr, |
119 | 0 | addrlen, net)) { |
120 | | /* We know we didn't find it, so this should be |
121 | | * impossible. */ |
122 | 0 | log_warn("unexpected: duplicate address: %s", ipstr); |
123 | 0 | } |
124 | 0 | } |
125 | 0 | return node; |
126 | 0 | } |
127 | | |
128 | | void |
129 | | respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node) |
130 | 0 | { |
131 | 0 | struct resp_addr* prev; |
132 | 0 | log_assert(set); |
133 | 0 | prev = (struct resp_addr*)rbtree_previous((struct rbnode_type*)node); |
134 | 0 | lock_rw_destroy(&node->lock); |
135 | 0 | (void)rbtree_delete(&set->ip_tree, node); |
136 | | /* no free'ing, all allocated in region */ |
137 | 0 | if(!prev) |
138 | 0 | addr_tree_init_parents((rbtree_type*)set); |
139 | 0 | else |
140 | 0 | addr_tree_init_parents_node(&prev->node); |
141 | 0 | } |
142 | | |
143 | | /** returns the node in the address tree for the specified netblock string; |
144 | | * non-existent node will be created if 'create' is true */ |
145 | | static struct resp_addr* |
146 | | respip_find_or_create(struct respip_set* set, const char* ipstr, int create) |
147 | 0 | { |
148 | 0 | struct sockaddr_storage addr; |
149 | 0 | int net; |
150 | 0 | socklen_t addrlen; |
151 | 0 | log_assert(set); |
152 | |
|
153 | 0 | if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) { |
154 | 0 | log_err("cannot parse netblock: '%s'", ipstr); |
155 | 0 | return NULL; |
156 | 0 | } |
157 | 0 | return respip_sockaddr_find_or_create(set, &addr, addrlen, net, create, |
158 | 0 | ipstr); |
159 | 0 | } |
160 | | |
161 | | static int |
162 | | respip_tag_cfg(struct respip_set* set, const char* ipstr, |
163 | | const uint8_t* taglist, size_t taglen) |
164 | 0 | { |
165 | 0 | struct resp_addr* node; |
166 | 0 | log_assert(set); |
167 | |
|
168 | 0 | if(!(node=respip_find_or_create(set, ipstr, 1))) |
169 | 0 | return 0; |
170 | 0 | if(node->taglist) { |
171 | 0 | log_warn("duplicate response-address-tag for '%s', overridden.", |
172 | 0 | ipstr); |
173 | 0 | } |
174 | 0 | node->taglist = regional_alloc_init(set->region, taglist, taglen); |
175 | 0 | if(!node->taglist) { |
176 | 0 | log_err("out of memory"); |
177 | 0 | return 0; |
178 | 0 | } |
179 | 0 | node->taglen = taglen; |
180 | 0 | return 1; |
181 | 0 | } |
182 | | |
183 | | /** set action for the node specified by the netblock string */ |
184 | | static int |
185 | | respip_action_cfg(struct respip_set* set, const char* ipstr, |
186 | | const char* actnstr) |
187 | 0 | { |
188 | 0 | struct resp_addr* node; |
189 | 0 | enum respip_action action; |
190 | 0 | log_assert(set); |
191 | |
|
192 | 0 | if(!(node=respip_find_or_create(set, ipstr, 1))) |
193 | 0 | return 0; |
194 | 0 | if(node->action != respip_none) { |
195 | 0 | verbose(VERB_QUERY, "duplicate response-ip action for '%s', overridden.", |
196 | 0 | ipstr); |
197 | 0 | } |
198 | 0 | if(strcmp(actnstr, "deny") == 0) |
199 | 0 | action = respip_deny; |
200 | 0 | else if(strcmp(actnstr, "redirect") == 0) |
201 | 0 | action = respip_redirect; |
202 | 0 | else if(strcmp(actnstr, "inform") == 0) |
203 | 0 | action = respip_inform; |
204 | 0 | else if(strcmp(actnstr, "inform_deny") == 0) |
205 | 0 | action = respip_inform_deny; |
206 | 0 | else if(strcmp(actnstr, "inform_redirect") == 0) |
207 | 0 | action = respip_inform_redirect; |
208 | 0 | else if(strcmp(actnstr, "always_transparent") == 0) |
209 | 0 | action = respip_always_transparent; |
210 | 0 | else if(strcmp(actnstr, "always_refuse") == 0) |
211 | 0 | action = respip_always_refuse; |
212 | 0 | else if(strcmp(actnstr, "always_nxdomain") == 0) |
213 | 0 | action = respip_always_nxdomain; |
214 | 0 | else if(strcmp(actnstr, "always_nodata") == 0) |
215 | 0 | action = respip_always_nodata; |
216 | 0 | else if(strcmp(actnstr, "always_deny") == 0) |
217 | 0 | action = respip_always_deny; |
218 | 0 | else { |
219 | 0 | log_err("unknown response-ip action %s", actnstr); |
220 | 0 | return 0; |
221 | 0 | } |
222 | 0 | node->action = action; |
223 | 0 | return 1; |
224 | 0 | } |
225 | | |
226 | | /** allocate and initialize an rrset structure; this function is based |
227 | | * on new_local_rrset() from the localzone.c module */ |
228 | | static struct ub_packed_rrset_key* |
229 | | new_rrset(struct regional* region, uint16_t rrtype, uint16_t rrclass) |
230 | 0 | { |
231 | 0 | struct packed_rrset_data* pd; |
232 | 0 | struct ub_packed_rrset_key* rrset = regional_alloc_zero( |
233 | 0 | region, sizeof(*rrset)); |
234 | 0 | if(!rrset) { |
235 | 0 | log_err("out of memory"); |
236 | 0 | return NULL; |
237 | 0 | } |
238 | 0 | rrset->entry.key = rrset; |
239 | 0 | pd = regional_alloc_zero(region, sizeof(*pd)); |
240 | 0 | if(!pd) { |
241 | 0 | log_err("out of memory"); |
242 | 0 | return NULL; |
243 | 0 | } |
244 | 0 | pd->trust = rrset_trust_prim_noglue; |
245 | 0 | pd->security = sec_status_insecure; |
246 | 0 | rrset->entry.data = pd; |
247 | 0 | rrset->rk.dname = regional_alloc_zero(region, 1); |
248 | 0 | if(!rrset->rk.dname) { |
249 | 0 | log_err("out of memory"); |
250 | 0 | return NULL; |
251 | 0 | } |
252 | 0 | rrset->rk.dname_len = 1; |
253 | 0 | rrset->rk.type = htons(rrtype); |
254 | 0 | rrset->rk.rrset_class = htons(rrclass); |
255 | 0 | return rrset; |
256 | 0 | } |
257 | | |
258 | | /** enter local data as resource records into a response-ip node */ |
259 | | |
260 | | int |
261 | | respip_enter_rr(struct regional* region, struct resp_addr* raddr, |
262 | | uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata, |
263 | | size_t rdata_len, const char* rrstr, const char* netblockstr) |
264 | 0 | { |
265 | 0 | struct packed_rrset_data* pd; |
266 | 0 | struct sockaddr* sa; |
267 | 0 | sa = (struct sockaddr*)&raddr->node.addr; |
268 | 0 | if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) { |
269 | 0 | log_err("CNAME response-ip data (%s) can not co-exist with other " |
270 | 0 | "response-ip data for netblock %s", rrstr, netblockstr); |
271 | 0 | return 0; |
272 | 0 | } else if (raddr->data && |
273 | 0 | raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) { |
274 | 0 | log_err("response-ip data (%s) can not be added; CNAME response-ip " |
275 | 0 | "data already in place for netblock %s", rrstr, netblockstr); |
276 | 0 | return 0; |
277 | 0 | } else if((rrtype != LDNS_RR_TYPE_CNAME) && |
278 | 0 | ((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) || |
279 | 0 | (sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) { |
280 | 0 | log_err("response-ip data %s record type does not correspond " |
281 | 0 | "to netblock %s address family", rrstr, netblockstr); |
282 | 0 | return 0; |
283 | 0 | } |
284 | | |
285 | 0 | if(!raddr->data) { |
286 | 0 | raddr->data = new_rrset(region, rrtype, rrclass); |
287 | 0 | if(!raddr->data) |
288 | 0 | return 0; |
289 | 0 | } |
290 | 0 | pd = raddr->data->entry.data; |
291 | 0 | return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr); |
292 | 0 | } |
293 | | |
294 | | static int |
295 | | respip_enter_rrstr(struct regional* region, struct resp_addr* raddr, |
296 | | const char* rrstr, const char* netblock) |
297 | 0 | { |
298 | 0 | uint8_t* nm; |
299 | 0 | uint16_t rrtype = 0, rrclass = 0; |
300 | 0 | time_t ttl = 0; |
301 | 0 | uint8_t rr[LDNS_RR_BUF_SIZE]; |
302 | 0 | uint8_t* rdata = NULL; |
303 | 0 | size_t rdata_len = 0; |
304 | 0 | char buf[65536]; |
305 | 0 | char bufshort[64]; |
306 | 0 | int ret; |
307 | 0 | if(raddr->action != respip_redirect |
308 | 0 | && raddr->action != respip_inform_redirect) { |
309 | 0 | log_err("cannot parse response-ip-data %s: response-ip " |
310 | 0 | "action for %s is not redirect", rrstr, netblock); |
311 | 0 | return 0; |
312 | 0 | } |
313 | 0 | ret = snprintf(buf, sizeof(buf), ". %s", rrstr); |
314 | 0 | if(ret < 0 || ret >= (int)sizeof(buf)) { |
315 | 0 | strlcpy(bufshort, rrstr, sizeof(bufshort)); |
316 | 0 | log_err("bad response-ip-data: %s...", bufshort); |
317 | 0 | return 0; |
318 | 0 | } |
319 | 0 | if(!rrstr_get_rr_content(buf, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr), |
320 | 0 | &rdata, &rdata_len)) { |
321 | 0 | log_err("bad response-ip-data: %s", rrstr); |
322 | 0 | return 0; |
323 | 0 | } |
324 | 0 | free(nm); |
325 | 0 | return respip_enter_rr(region, raddr, rrtype, rrclass, ttl, rdata, |
326 | 0 | rdata_len, rrstr, netblock); |
327 | 0 | } |
328 | | |
329 | | static int |
330 | | respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr) |
331 | 0 | { |
332 | 0 | struct resp_addr* node; |
333 | 0 | log_assert(set); |
334 | |
|
335 | 0 | node=respip_find_or_create(set, ipstr, 0); |
336 | 0 | if(!node || node->action == respip_none) { |
337 | 0 | log_err("cannot parse response-ip-data %s: " |
338 | 0 | "response-ip node for %s not found", rrstr, ipstr); |
339 | 0 | return 0; |
340 | 0 | } |
341 | 0 | return respip_enter_rrstr(set->region, node, rrstr, ipstr); |
342 | 0 | } |
343 | | |
344 | | static int |
345 | | respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags, |
346 | | struct config_strbytelist* respip_tags, |
347 | | struct config_str2list* respip_actions, |
348 | | struct config_str2list* respip_data) |
349 | 0 | { |
350 | 0 | struct config_strbytelist* p; |
351 | 0 | struct config_str2list* pa; |
352 | 0 | struct config_str2list* pd; |
353 | 0 | log_assert(set); |
354 | |
|
355 | 0 | set->tagname = tagname; |
356 | 0 | set->num_tags = num_tags; |
357 | |
|
358 | 0 | p = respip_tags; |
359 | 0 | while(p) { |
360 | 0 | struct config_strbytelist* np = p->next; |
361 | |
|
362 | 0 | log_assert(p->str && p->str2); |
363 | 0 | if(!respip_tag_cfg(set, p->str, p->str2, p->str2len)) { |
364 | 0 | config_del_strbytelist(p); |
365 | 0 | return 0; |
366 | 0 | } |
367 | 0 | free(p->str); |
368 | 0 | free(p->str2); |
369 | 0 | free(p); |
370 | 0 | p = np; |
371 | 0 | } |
372 | | |
373 | 0 | pa = respip_actions; |
374 | 0 | while(pa) { |
375 | 0 | struct config_str2list* np = pa->next; |
376 | 0 | log_assert(pa->str && pa->str2); |
377 | 0 | if(!respip_action_cfg(set, pa->str, pa->str2)) { |
378 | 0 | config_deldblstrlist(pa); |
379 | 0 | return 0; |
380 | 0 | } |
381 | 0 | free(pa->str); |
382 | 0 | free(pa->str2); |
383 | 0 | free(pa); |
384 | 0 | pa = np; |
385 | 0 | } |
386 | | |
387 | 0 | pd = respip_data; |
388 | 0 | while(pd) { |
389 | 0 | struct config_str2list* np = pd->next; |
390 | 0 | log_assert(pd->str && pd->str2); |
391 | 0 | if(!respip_data_cfg(set, pd->str, pd->str2)) { |
392 | 0 | config_deldblstrlist(pd); |
393 | 0 | return 0; |
394 | 0 | } |
395 | 0 | free(pd->str); |
396 | 0 | free(pd->str2); |
397 | 0 | free(pd); |
398 | 0 | pd = np; |
399 | 0 | } |
400 | 0 | addr_tree_init_parents(&set->ip_tree); |
401 | |
|
402 | 0 | return 1; |
403 | 0 | } |
404 | | |
405 | | int |
406 | | respip_global_apply_cfg(struct respip_set* set, struct config_file* cfg) |
407 | 0 | { |
408 | 0 | int ret = respip_set_apply_cfg(set, cfg->tagname, cfg->num_tags, |
409 | 0 | cfg->respip_tags, cfg->respip_actions, cfg->respip_data); |
410 | 0 | cfg->respip_data = NULL; |
411 | 0 | cfg->respip_actions = NULL; |
412 | 0 | cfg->respip_tags = NULL; |
413 | 0 | return ret; |
414 | 0 | } |
415 | | |
416 | | /** Iterate through raw view data and apply the view-specific respip |
417 | | * configuration; at this point we should have already seen all the views, |
418 | | * so if any of the views that respip data refer to does not exist, that's |
419 | | * an error. This additional iteration through view configuration data |
420 | | * is expected to not have significant performance impact (or rather, its |
421 | | * performance impact is not expected to be prohibitive in the configuration |
422 | | * processing phase). |
423 | | */ |
424 | | int |
425 | | respip_views_apply_cfg(struct views* vs, struct config_file* cfg, |
426 | | int* have_view_respip_cfg) |
427 | 0 | { |
428 | 0 | struct config_view* cv; |
429 | 0 | struct view* v; |
430 | 0 | int ret; |
431 | |
|
432 | 0 | for(cv = cfg->views; cv; cv = cv->next) { |
433 | | |
434 | | /** if no respip config for this view then there's |
435 | | * nothing to do; note that even though respip data must go |
436 | | * with respip action, we're checking for both here because |
437 | | * we want to catch the case where the respip action is missing |
438 | | * while the data is present */ |
439 | 0 | if(!cv->respip_actions && !cv->respip_data) |
440 | 0 | continue; |
441 | | |
442 | 0 | if(!(v = views_find_view(vs, cv->name, 1))) { |
443 | 0 | log_err("view '%s' unexpectedly missing", cv->name); |
444 | 0 | return 0; |
445 | 0 | } |
446 | 0 | if(!v->respip_set) { |
447 | 0 | v->respip_set = respip_set_create(); |
448 | 0 | if(!v->respip_set) { |
449 | 0 | log_err("out of memory"); |
450 | 0 | lock_rw_unlock(&v->lock); |
451 | 0 | return 0; |
452 | 0 | } |
453 | 0 | } |
454 | 0 | ret = respip_set_apply_cfg(v->respip_set, NULL, 0, NULL, |
455 | 0 | cv->respip_actions, cv->respip_data); |
456 | 0 | lock_rw_unlock(&v->lock); |
457 | 0 | if(!ret) { |
458 | 0 | log_err("Error while applying respip configuration " |
459 | 0 | "for view '%s'", cv->name); |
460 | 0 | return 0; |
461 | 0 | } |
462 | 0 | *have_view_respip_cfg = (*have_view_respip_cfg || |
463 | 0 | v->respip_set->ip_tree.count); |
464 | 0 | cv->respip_actions = NULL; |
465 | 0 | cv->respip_data = NULL; |
466 | 0 | } |
467 | 0 | return 1; |
468 | 0 | } |
469 | | |
470 | | /** |
471 | | * make a deep copy of 'key' in 'region'. |
472 | | * This is largely derived from packed_rrset_copy_region() and |
473 | | * packed_rrset_ptr_fixup(), but differs in the following points: |
474 | | * |
475 | | * - It doesn't assume all data in 'key' are in a contiguous memory region. |
476 | | * Although that would be the case in most cases, 'key' can be passed from |
477 | | * a lower-level module and it might not build the rrset to meet the |
478 | | * assumption. In fact, an rrset specified as response-ip-data or generated |
479 | | * in local_data_find_tag_datas() breaks the assumption. So it would be |
480 | | * safer not to naively rely on the assumption. On the other hand, this |
481 | | * function ensures the copied rrset data are in a contiguous region so |
482 | | * that it won't cause a disruption even if an upper layer module naively |
483 | | * assumes the memory layout. |
484 | | * - It doesn't copy RRSIGs (if any) in 'key'. The rrset will be used in |
485 | | * a reply that was already faked, so it doesn't make much sense to provide |
486 | | * partial sigs even if they are valid themselves. |
487 | | * - It doesn't adjust TTLs as it basically has to be a verbatim copy of 'key' |
488 | | * just allocated in 'region' (the assumption is necessary TTL adjustment |
489 | | * has been already done in 'key'). |
490 | | * |
491 | | * This function returns the copied rrset key on success, and NULL on memory |
492 | | * allocation failure. |
493 | | */ |
494 | | struct ub_packed_rrset_key* |
495 | | respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region) |
496 | 0 | { |
497 | 0 | struct ub_packed_rrset_key* ck = regional_alloc(region, |
498 | 0 | sizeof(struct ub_packed_rrset_key)); |
499 | 0 | struct packed_rrset_data* d; |
500 | 0 | struct packed_rrset_data* data = key->entry.data; |
501 | 0 | size_t dsize, i; |
502 | 0 | uint8_t* nextrdata; |
503 | | |
504 | | /* derived from packed_rrset_copy_region(), but don't use |
505 | | * packed_rrset_sizeof() and do exclude RRSIGs */ |
506 | 0 | if(!ck) |
507 | 0 | return NULL; |
508 | 0 | ck->id = key->id; |
509 | 0 | memset(&ck->entry, 0, sizeof(ck->entry)); |
510 | 0 | ck->entry.hash = key->entry.hash; |
511 | 0 | ck->entry.key = ck; |
512 | 0 | ck->rk = key->rk; |
513 | 0 | if(key->rk.dname) { |
514 | 0 | ck->rk.dname = regional_alloc_init(region, key->rk.dname, |
515 | 0 | key->rk.dname_len); |
516 | 0 | if(!ck->rk.dname) |
517 | 0 | return NULL; |
518 | 0 | ck->rk.dname_len = key->rk.dname_len; |
519 | 0 | } else { |
520 | 0 | ck->rk.dname = NULL; |
521 | 0 | ck->rk.dname_len = 0; |
522 | 0 | } |
523 | | |
524 | 0 | if((unsigned)data->count >= 0xffff00U) |
525 | 0 | return NULL; /* guard against integer overflow in dsize */ |
526 | 0 | dsize = sizeof(struct packed_rrset_data) + data->count * |
527 | 0 | (sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t)); |
528 | 0 | for(i=0; i<data->count; i++) { |
529 | 0 | if((unsigned)dsize >= 0x0fffffffU || |
530 | 0 | (unsigned)data->rr_len[i] >= 0x0fffffffU) |
531 | 0 | return NULL; /* guard against integer overflow */ |
532 | 0 | dsize += data->rr_len[i]; |
533 | 0 | } |
534 | 0 | d = regional_alloc_zero(region, dsize); |
535 | 0 | if(!d) |
536 | 0 | return NULL; |
537 | 0 | *d = *data; |
538 | 0 | d->rrsig_count = 0; |
539 | 0 | ck->entry.data = d; |
540 | | |
541 | | /* derived from packed_rrset_ptr_fixup() with copying the data */ |
542 | 0 | d->rr_len = (size_t*)((uint8_t*)d + sizeof(struct packed_rrset_data)); |
543 | 0 | d->rr_data = (uint8_t**)&(d->rr_len[d->count]); |
544 | 0 | d->rr_ttl = (time_t*)&(d->rr_data[d->count]); |
545 | 0 | nextrdata = (uint8_t*)&(d->rr_ttl[d->count]); |
546 | 0 | for(i=0; i<d->count; i++) { |
547 | 0 | d->rr_len[i] = data->rr_len[i]; |
548 | 0 | d->rr_ttl[i] = data->rr_ttl[i]; |
549 | 0 | d->rr_data[i] = nextrdata; |
550 | 0 | memcpy(d->rr_data[i], data->rr_data[i], data->rr_len[i]); |
551 | 0 | nextrdata += d->rr_len[i]; |
552 | 0 | } |
553 | |
|
554 | 0 | return ck; |
555 | 0 | } |
556 | | |
557 | | int |
558 | | respip_init(struct module_env* env, int id) |
559 | 0 | { |
560 | 0 | (void)env; |
561 | 0 | (void)id; |
562 | 0 | return 1; |
563 | 0 | } |
564 | | |
565 | | void |
566 | | respip_deinit(struct module_env* env, int id) |
567 | 0 | { |
568 | 0 | (void)env; |
569 | 0 | (void)id; |
570 | 0 | } |
571 | | |
572 | | /** Convert a packed AAAA or A RRset to sockaddr. */ |
573 | | static int |
574 | | rdata2sockaddr(const struct packed_rrset_data* rd, uint16_t rtype, size_t i, |
575 | | struct sockaddr_storage* ss, socklen_t* addrlenp) |
576 | 0 | { |
577 | | /* unbound can accept and cache odd-length AAAA/A records, so we have |
578 | | * to validate the length. */ |
579 | 0 | if(rtype == LDNS_RR_TYPE_A && rd->rr_len[i] == 6) { |
580 | 0 | struct sockaddr_in* sa4 = (struct sockaddr_in*)ss; |
581 | |
|
582 | 0 | memset(sa4, 0, sizeof(*sa4)); |
583 | 0 | sa4->sin_family = AF_INET; |
584 | 0 | memcpy(&sa4->sin_addr, rd->rr_data[i] + 2, |
585 | 0 | sizeof(sa4->sin_addr)); |
586 | 0 | *addrlenp = sizeof(*sa4); |
587 | 0 | return 1; |
588 | 0 | } else if(rtype == LDNS_RR_TYPE_AAAA && rd->rr_len[i] == 18) { |
589 | 0 | struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ss; |
590 | |
|
591 | 0 | memset(sa6, 0, sizeof(*sa6)); |
592 | 0 | sa6->sin6_family = AF_INET6; |
593 | 0 | memcpy(&sa6->sin6_addr, rd->rr_data[i] + 2, |
594 | 0 | sizeof(sa6->sin6_addr)); |
595 | 0 | *addrlenp = sizeof(*sa6); |
596 | 0 | return 1; |
597 | 0 | } |
598 | 0 | return 0; |
599 | 0 | } |
600 | | |
601 | | /** |
602 | | * Search the given 'iptree' for response address information that matches |
603 | | * any of the IP addresses in an AAAA or A in the answer section of the |
604 | | * response (stored in 'rep'). If found, a pointer to the matched resp_addr |
605 | | * structure will be returned, and '*rrset_id' is set to the index in |
606 | | * rep->rrsets for the RRset that contains the matching IP address record |
607 | | * (the index is normally 0, but can be larger than that if this is a CNAME |
608 | | * chain or type-ANY response). |
609 | | * Returns resp_addr holding read lock. |
610 | | */ |
611 | | static struct resp_addr* |
612 | | respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs, |
613 | | size_t* rrset_id, size_t* rr_id) |
614 | 0 | { |
615 | 0 | size_t i; |
616 | 0 | struct resp_addr* ra; |
617 | 0 | struct sockaddr_storage ss; |
618 | 0 | socklen_t addrlen; |
619 | 0 | log_assert(rs); |
620 | |
|
621 | 0 | lock_rw_rdlock(&rs->lock); |
622 | 0 | for(i=0; i<rep->an_numrrsets; i++) { |
623 | 0 | size_t j; |
624 | 0 | const struct packed_rrset_data* rd; |
625 | 0 | uint16_t rtype = ntohs(rep->rrsets[i]->rk.type); |
626 | |
|
627 | 0 | if(rtype != LDNS_RR_TYPE_A && rtype != LDNS_RR_TYPE_AAAA) |
628 | 0 | continue; |
629 | 0 | rd = rep->rrsets[i]->entry.data; |
630 | 0 | for(j = 0; j < rd->count; j++) { |
631 | 0 | if(!rdata2sockaddr(rd, rtype, j, &ss, &addrlen)) |
632 | 0 | continue; |
633 | 0 | ra = (struct resp_addr*)addr_tree_lookup(&rs->ip_tree, |
634 | 0 | &ss, addrlen); |
635 | 0 | if(ra) { |
636 | 0 | *rrset_id = i; |
637 | 0 | *rr_id = j; |
638 | 0 | lock_rw_rdlock(&ra->lock); |
639 | 0 | lock_rw_unlock(&rs->lock); |
640 | 0 | return ra; |
641 | 0 | } |
642 | 0 | } |
643 | 0 | } |
644 | 0 | lock_rw_unlock(&rs->lock); |
645 | 0 | return NULL; |
646 | 0 | } |
647 | | |
648 | | /** |
649 | | * See if response-ip or tag data should override the original answer rrset |
650 | | * (which is rep->rrsets[rrset_id]) and if so override it. |
651 | | * This is (mostly) equivalent to localzone.c:local_data_answer() but for |
652 | | * response-ip actions. |
653 | | * Note that this function distinguishes error conditions from "success but |
654 | | * not overridden". This is because we want to avoid accidentally applying |
655 | | * the "no data" action in case of error. |
656 | | * @param action: action to apply |
657 | | * @param data: RRset to use for override |
658 | | * @param qtype: original query type |
659 | | * @param rep: original reply message |
660 | | * @param rrset_id: the rrset ID in 'rep' to which the action should apply |
661 | | * @param new_repp: see respip_rewrite_reply |
662 | | * @param tag: if >= 0 the tag ID used to determine the action and data |
663 | | * @param tag_datas: data corresponding to 'tag'. |
664 | | * @param tag_datas_size: size of 'tag_datas' |
665 | | * @param tagname: array of tag names, used for logging |
666 | | * @param num_tags: size of 'tagname', used for logging |
667 | | * @param redirect_rrsetp: ptr to redirect record |
668 | | * @param region: region for building new reply |
669 | | * @return 1 if overridden, 0 if not overridden, -1 on error. |
670 | | */ |
671 | | static int |
672 | | respip_data_answer(enum respip_action action, |
673 | | struct ub_packed_rrset_key* data, |
674 | | uint16_t qtype, const struct reply_info* rep, |
675 | | size_t rrset_id, struct reply_info** new_repp, int tag, |
676 | | struct config_strlist** tag_datas, size_t tag_datas_size, |
677 | | char* const* tagname, int num_tags, |
678 | | struct ub_packed_rrset_key** redirect_rrsetp, struct regional* region) |
679 | 0 | { |
680 | 0 | struct ub_packed_rrset_key* rp = data; |
681 | 0 | struct reply_info* new_rep; |
682 | 0 | *redirect_rrsetp = NULL; |
683 | |
|
684 | 0 | if(action == respip_redirect && tag != -1 && |
685 | 0 | (size_t)tag<tag_datas_size && tag_datas[tag]) { |
686 | 0 | struct query_info dataqinfo; |
687 | 0 | struct ub_packed_rrset_key r; |
688 | | |
689 | | /* Extract parameters of the original answer rrset that can be |
690 | | * rewritten below, in the form of query_info. Note that these |
691 | | * can be different from the info of the original query if the |
692 | | * rrset is a CNAME target.*/ |
693 | 0 | memset(&dataqinfo, 0, sizeof(dataqinfo)); |
694 | 0 | dataqinfo.qname = rep->rrsets[rrset_id]->rk.dname; |
695 | 0 | dataqinfo.qname_len = rep->rrsets[rrset_id]->rk.dname_len; |
696 | 0 | dataqinfo.qtype = ntohs(rep->rrsets[rrset_id]->rk.type); |
697 | 0 | dataqinfo.qclass = ntohs(rep->rrsets[rrset_id]->rk.rrset_class); |
698 | |
|
699 | 0 | memset(&r, 0, sizeof(r)); |
700 | 0 | if(local_data_find_tag_datas(&dataqinfo, tag_datas[tag], &r, |
701 | 0 | region)) { |
702 | 0 | verbose(VERB_ALGO, |
703 | 0 | "response-ip redirect with tag data [%d] %s", |
704 | 0 | tag, (tag<num_tags?tagname[tag]:"null")); |
705 | | /* use copy_rrset() to 'normalize' memory layout */ |
706 | 0 | rp = respip_copy_rrset(&r, region); |
707 | 0 | if(!rp) |
708 | 0 | return -1; |
709 | 0 | } |
710 | 0 | } |
711 | 0 | if(!rp) |
712 | 0 | return 0; |
713 | | |
714 | | /* If we are using response-ip-data, we need to make a copy of rrset |
715 | | * to replace the rrset's dname. Note that, unlike local data, we |
716 | | * rename the dname for other actions than redirect. This is because |
717 | | * response-ip-data isn't associated to any specific name. */ |
718 | 0 | if(rp == data) { |
719 | 0 | rp = respip_copy_rrset(rp, region); |
720 | 0 | if(!rp) |
721 | 0 | return -1; |
722 | 0 | rp->rk.dname = rep->rrsets[rrset_id]->rk.dname; |
723 | 0 | rp->rk.dname_len = rep->rrsets[rrset_id]->rk.dname_len; |
724 | 0 | } |
725 | | |
726 | | /* Build a new reply with redirect rrset. We keep any preceding CNAMEs |
727 | | * and replace the address rrset that triggers the action. If it's |
728 | | * type ANY query, however, no other answer records should be kept |
729 | | * (note that it can't be a CNAME chain in this case due to |
730 | | * sanitizing). */ |
731 | 0 | if(qtype == LDNS_RR_TYPE_ANY) |
732 | 0 | rrset_id = 0; |
733 | 0 | new_rep = make_new_reply_info(rep, region, rrset_id + 1, rrset_id); |
734 | 0 | if(!new_rep) |
735 | 0 | return -1; |
736 | 0 | rp->rk.flags |= PACKED_RRSET_FIXEDTTL; /* avoid adjusting TTL */ |
737 | 0 | new_rep->rrsets[rrset_id] = rp; |
738 | |
|
739 | 0 | *redirect_rrsetp = rp; |
740 | 0 | *new_repp = new_rep; |
741 | 0 | return 1; |
742 | 0 | } |
743 | | |
744 | | /** |
745 | | * apply response ip action in case where no action data is provided. |
746 | | * this is similar to localzone.c:lz_zone_answer() but simplified due to |
747 | | * the characteristics of response ip: |
748 | | * - 'deny' variants will be handled at the caller side |
749 | | * - no specific processing for 'transparent' variants: unlike local zones, |
750 | | * there is no such a case of 'no data but name existing'. so all variants |
751 | | * just mean 'transparent if no data'. |
752 | | * @param qtype: query type |
753 | | * @param action: found action |
754 | | * @param rep: |
755 | | * @param new_repp |
756 | | * @param rrset_id |
757 | | * @param region: region for building new reply |
758 | | * @return 1 on success, 0 on error. |
759 | | */ |
760 | | static int |
761 | | respip_nodata_answer(uint16_t qtype, enum respip_action action, |
762 | | const struct reply_info *rep, size_t rrset_id, |
763 | | struct reply_info** new_repp, struct regional* region) |
764 | 0 | { |
765 | 0 | struct reply_info* new_rep; |
766 | |
|
767 | 0 | if(action == respip_refuse || action == respip_always_refuse) { |
768 | 0 | new_rep = make_new_reply_info(rep, region, 0, 0); |
769 | 0 | if(!new_rep) |
770 | 0 | return 0; |
771 | 0 | FLAGS_SET_RCODE(new_rep->flags, LDNS_RCODE_REFUSED); |
772 | 0 | *new_repp = new_rep; |
773 | 0 | return 1; |
774 | 0 | } else if(action == respip_static || action == respip_redirect || |
775 | 0 | action == respip_always_nxdomain || |
776 | 0 | action == respip_always_nodata || |
777 | 0 | action == respip_inform_redirect) { |
778 | | /* Since we don't know about other types of the owner name, |
779 | | * we generally return NOERROR/NODATA unless an NXDOMAIN action |
780 | | * is explicitly specified. */ |
781 | 0 | int rcode = (action == respip_always_nxdomain)? |
782 | 0 | LDNS_RCODE_NXDOMAIN:LDNS_RCODE_NOERROR; |
783 | | /* We should empty the answer section except for any preceding |
784 | | * CNAMEs (in that case rrset_id > 0). Type-ANY case is |
785 | | * special as noted in respip_data_answer(). */ |
786 | 0 | if(qtype == LDNS_RR_TYPE_ANY) |
787 | 0 | rrset_id = 0; |
788 | 0 | new_rep = make_new_reply_info(rep, region, rrset_id, rrset_id); |
789 | 0 | if(!new_rep) |
790 | 0 | return 0; |
791 | 0 | FLAGS_SET_RCODE(new_rep->flags, rcode); |
792 | 0 | *new_repp = new_rep; |
793 | 0 | return 1; |
794 | 0 | } |
795 | | |
796 | 0 | return 1; |
797 | 0 | } |
798 | | |
799 | | /** Populate action info structure with the results of response-ip action |
800 | | * processing, iff as the result of response-ip processing we are actually |
801 | | * taking some action. Only action is set if action_only is true. |
802 | | * Returns true on success, false on failure. |
803 | | */ |
804 | | static int |
805 | | populate_action_info(struct respip_action_info* actinfo, |
806 | | enum respip_action action, const struct resp_addr* raddr, |
807 | | const struct ub_packed_rrset_key* ATTR_UNUSED(rrset), |
808 | | int ATTR_UNUSED(tag), const struct respip_set* ATTR_UNUSED(ipset), |
809 | | int ATTR_UNUSED(action_only), struct regional* region, int rpz_used, |
810 | | int rpz_log, char* log_name, int rpz_cname_override) |
811 | 0 | { |
812 | 0 | if(action == respip_none || !raddr) |
813 | 0 | return 1; |
814 | 0 | actinfo->action = action; |
815 | 0 | actinfo->rpz_used = rpz_used; |
816 | 0 | actinfo->rpz_log = rpz_log; |
817 | 0 | actinfo->log_name = log_name; |
818 | 0 | actinfo->rpz_cname_override = rpz_cname_override; |
819 | | |
820 | | /* for inform variants, make a copy of the matched address block for |
821 | | * later logging. We make a copy to proactively avoid disruption if |
822 | | * and when we allow a dynamic update to the respip tree. */ |
823 | 0 | if(action == respip_inform || action == respip_inform_deny || |
824 | 0 | rpz_used) { |
825 | 0 | struct respip_addr_info* a = |
826 | 0 | regional_alloc_zero(region, sizeof(*a)); |
827 | 0 | if(!a) { |
828 | 0 | log_err("out of memory"); |
829 | 0 | return 0; |
830 | 0 | } |
831 | 0 | a->addr = raddr->node.addr; |
832 | 0 | a->addrlen = raddr->node.addrlen; |
833 | 0 | a->net = raddr->node.net; |
834 | 0 | actinfo->addrinfo = a; |
835 | 0 | } |
836 | | |
837 | 0 | return 1; |
838 | 0 | } |
839 | | |
840 | | static int |
841 | | respip_use_rpz(struct resp_addr* raddr, struct rpz* r, |
842 | | enum respip_action* action, |
843 | | struct ub_packed_rrset_key** data, int* rpz_log, char** log_name, |
844 | | int* rpz_cname_override, struct regional* region, int* is_rpz, |
845 | | int* rpz_passthru) |
846 | 0 | { |
847 | 0 | if(rpz_passthru && *rpz_passthru) |
848 | 0 | return 0; |
849 | 0 | if(r->action_override == RPZ_DISABLED_ACTION) { |
850 | 0 | *is_rpz = 0; |
851 | 0 | return 1; |
852 | 0 | } |
853 | 0 | else if(r->action_override == RPZ_NO_OVERRIDE_ACTION) |
854 | 0 | *action = raddr->action; |
855 | 0 | else |
856 | 0 | *action = rpz_action_to_respip_action(r->action_override); |
857 | 0 | if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION && |
858 | 0 | r->cname_override) { |
859 | 0 | *data = r->cname_override; |
860 | 0 | *rpz_cname_override = 1; |
861 | 0 | } |
862 | 0 | if(*action == respip_always_transparent /* RPZ_PASSTHRU_ACTION */ |
863 | 0 | && rpz_passthru) |
864 | 0 | *rpz_passthru = 1; |
865 | 0 | *rpz_log = r->log; |
866 | 0 | if(r->log_name) |
867 | 0 | if(!(*log_name = regional_strdup(region, r->log_name))) |
868 | 0 | return 0; |
869 | 0 | *is_rpz = 1; |
870 | 0 | return 1; |
871 | 0 | } |
872 | | |
873 | | int |
874 | | respip_rewrite_reply(const struct query_info* qinfo, |
875 | | const struct respip_client_info* cinfo, const struct reply_info* rep, |
876 | | struct reply_info** new_repp, struct respip_action_info* actinfo, |
877 | | struct ub_packed_rrset_key** alias_rrset, int search_only, |
878 | | struct regional* region, struct auth_zones* az, int* rpz_passthru, |
879 | | struct views* views, struct respip_set* ipset) |
880 | 0 | { |
881 | 0 | const uint8_t* ctaglist; |
882 | 0 | size_t ctaglen; |
883 | 0 | const uint8_t* tag_actions; |
884 | 0 | size_t tag_actions_size; |
885 | 0 | struct config_strlist** tag_datas; |
886 | 0 | size_t tag_datas_size; |
887 | 0 | struct view* view = NULL; |
888 | 0 | size_t rrset_id = 0, rr_id = 0; |
889 | 0 | enum respip_action action = respip_none; |
890 | 0 | int tag = -1; |
891 | 0 | struct resp_addr* raddr = NULL; |
892 | 0 | int ret = 1; |
893 | 0 | struct ub_packed_rrset_key* redirect_rrset = NULL; |
894 | 0 | struct rpz* r; |
895 | 0 | struct auth_zone* a = NULL; |
896 | 0 | struct ub_packed_rrset_key* data = NULL; |
897 | 0 | int rpz_used = 0; |
898 | 0 | int rpz_log = 0; |
899 | 0 | int rpz_cname_override = 0; |
900 | 0 | char* log_name = NULL; |
901 | |
|
902 | 0 | if(!cinfo) |
903 | 0 | goto done; |
904 | 0 | ctaglist = cinfo->taglist; |
905 | 0 | ctaglen = cinfo->taglen; |
906 | 0 | tag_actions = cinfo->tag_actions; |
907 | 0 | tag_actions_size = cinfo->tag_actions_size; |
908 | 0 | tag_datas = cinfo->tag_datas; |
909 | 0 | tag_datas_size = cinfo->tag_datas_size; |
910 | 0 | if(cinfo->view) { |
911 | 0 | view = cinfo->view; |
912 | 0 | lock_rw_rdlock(&view->lock); |
913 | 0 | } else if(cinfo->view_name) { |
914 | 0 | view = views_find_view(views, cinfo->view_name, 0); |
915 | 0 | if(!view) { |
916 | | /* If the view no longer exists, the rewrite can not |
917 | | * be processed further. */ |
918 | 0 | verbose(VERB_ALGO, "respip: failed because view %s no " |
919 | 0 | "longer exists", cinfo->view_name); |
920 | 0 | return 0; |
921 | 0 | } |
922 | | /* The view is rdlocked by views_find_view. */ |
923 | 0 | } |
924 | | |
925 | 0 | log_assert(ipset); |
926 | | |
927 | | /** Try to use response-ip config from the view first; use |
928 | | * global response-ip config if we don't have the view or we don't |
929 | | * have the matching per-view config (and the view allows the use |
930 | | * of global data in this case). |
931 | | * Note that we lock the view even if we only use view members that |
932 | | * currently don't change after creation. This is for safety for |
933 | | * future possible changes as the view documentation seems to expect |
934 | | * any of its member can change in the view's lifetime. |
935 | | * Note also that we assume 'view' is valid in this function, which |
936 | | * should be safe (see unbound bug #1191) */ |
937 | 0 | if(view) { |
938 | 0 | if(view->respip_set) { |
939 | 0 | if((raddr = respip_addr_lookup(rep, |
940 | 0 | view->respip_set, &rrset_id, &rr_id))) { |
941 | | /** for per-view respip directives the action |
942 | | * can only be direct (i.e. not tag-based) */ |
943 | 0 | action = raddr->action; |
944 | 0 | } |
945 | 0 | } |
946 | 0 | if(!raddr && !view->isfirst) |
947 | 0 | goto done; |
948 | 0 | if(!raddr && view->isfirst) { |
949 | 0 | lock_rw_unlock(&view->lock); |
950 | 0 | view = NULL; |
951 | 0 | } |
952 | 0 | } |
953 | 0 | if(!raddr && (raddr = respip_addr_lookup(rep, ipset, |
954 | 0 | &rrset_id, &rr_id))) { |
955 | 0 | action = (enum respip_action)local_data_find_tag_action( |
956 | 0 | raddr->taglist, raddr->taglen, ctaglist, ctaglen, |
957 | 0 | tag_actions, tag_actions_size, |
958 | 0 | (enum localzone_type)raddr->action, &tag, |
959 | 0 | ipset->tagname, ipset->num_tags); |
960 | 0 | } |
961 | 0 | lock_rw_rdlock(&az->rpz_lock); |
962 | 0 | for(a = az->rpz_first; a && !raddr && !(rpz_passthru && *rpz_passthru); a = a->rpz_az_next) { |
963 | 0 | lock_rw_rdlock(&a->lock); |
964 | 0 | r = a->rpz; |
965 | 0 | if(!r->taglist || taglist_intersect(r->taglist, |
966 | 0 | r->taglistlen, ctaglist, ctaglen)) { |
967 | 0 | if((raddr = respip_addr_lookup(rep, |
968 | 0 | r->respip_set, &rrset_id, &rr_id))) { |
969 | 0 | if(!respip_use_rpz(raddr, r, &action, &data, |
970 | 0 | &rpz_log, &log_name, &rpz_cname_override, |
971 | 0 | region, &rpz_used, rpz_passthru)) { |
972 | 0 | log_err("out of memory"); |
973 | 0 | lock_rw_unlock(&raddr->lock); |
974 | 0 | lock_rw_unlock(&a->lock); |
975 | 0 | lock_rw_unlock(&az->rpz_lock); |
976 | 0 | return 0; |
977 | 0 | } |
978 | 0 | if(rpz_used) { |
979 | 0 | if(verbosity >= VERB_ALGO) { |
980 | 0 | struct sockaddr_storage ss; |
981 | 0 | socklen_t ss_len = 0; |
982 | 0 | char nm[256], ip[256]; |
983 | 0 | char qn[LDNS_MAX_DOMAINLEN]; |
984 | 0 | if(!rdata2sockaddr(rep->rrsets[rrset_id]->entry.data, ntohs(rep->rrsets[rrset_id]->rk.type), rr_id, &ss, &ss_len)) |
985 | 0 | snprintf(ip, sizeof(ip), "invalidRRdata"); |
986 | 0 | else |
987 | 0 | addr_to_str(&ss, ss_len, ip, sizeof(ip)); |
988 | 0 | dname_str(qinfo->qname, qn); |
989 | 0 | addr_to_str(&raddr->node.addr, |
990 | 0 | raddr->node.addrlen, |
991 | 0 | nm, sizeof(nm)); |
992 | 0 | verbose(VERB_ALGO, "respip: rpz: response-ip trigger %s/%d on %s %s with action %s", nm, raddr->node.net, qn, ip, rpz_action_to_string(respip_action_to_rpz_action(action))); |
993 | 0 | } |
994 | | /* break to make sure 'a' stays pointed |
995 | | * to used auth_zone, and keeps lock */ |
996 | 0 | break; |
997 | 0 | } |
998 | 0 | lock_rw_unlock(&raddr->lock); |
999 | 0 | raddr = NULL; |
1000 | 0 | actinfo->rpz_disabled++; |
1001 | 0 | } |
1002 | 0 | } |
1003 | 0 | lock_rw_unlock(&a->lock); |
1004 | 0 | } |
1005 | 0 | lock_rw_unlock(&az->rpz_lock); |
1006 | 0 | if(raddr && !search_only) { |
1007 | 0 | int result = 0; |
1008 | | |
1009 | | /* first, see if we have response-ip or tag action for the |
1010 | | * action except for 'always' variants. */ |
1011 | 0 | if(action != respip_always_refuse |
1012 | 0 | && action != respip_always_transparent |
1013 | 0 | && action != respip_always_nxdomain |
1014 | 0 | && action != respip_always_nodata |
1015 | 0 | && action != respip_always_deny |
1016 | 0 | && (result = respip_data_answer(action, |
1017 | 0 | (data) ? data : raddr->data, qinfo->qtype, rep, |
1018 | 0 | rrset_id, new_repp, tag, tag_datas, tag_datas_size, |
1019 | 0 | ipset->tagname, ipset->num_tags, &redirect_rrset, |
1020 | 0 | region)) < 0) { |
1021 | 0 | ret = 0; |
1022 | 0 | goto done; |
1023 | 0 | } |
1024 | | |
1025 | | /* if no action data applied, take action specific to the |
1026 | | * action without data. */ |
1027 | 0 | if(!result && !respip_nodata_answer(qinfo->qtype, action, rep, |
1028 | 0 | rrset_id, new_repp, region)) { |
1029 | 0 | ret = 0; |
1030 | 0 | goto done; |
1031 | 0 | } |
1032 | 0 | } |
1033 | 0 | done: |
1034 | 0 | if(view) { |
1035 | 0 | lock_rw_unlock(&view->lock); |
1036 | 0 | } |
1037 | 0 | if(ret) { |
1038 | | /* If we're redirecting the original answer to a |
1039 | | * CNAME, record the CNAME rrset so the caller can take |
1040 | | * the appropriate action. Note that we don't check the |
1041 | | * action type; it should normally be 'redirect', but it |
1042 | | * can be of other type when a data-dependent tag action |
1043 | | * uses redirect response-ip data. |
1044 | | */ |
1045 | 0 | if(redirect_rrset && |
1046 | 0 | redirect_rrset->rk.type == ntohs(LDNS_RR_TYPE_CNAME) && |
1047 | 0 | qinfo->qtype != LDNS_RR_TYPE_ANY) |
1048 | 0 | *alias_rrset = redirect_rrset; |
1049 | | /* on success, populate respip result structure */ |
1050 | 0 | ret = populate_action_info(actinfo, action, raddr, |
1051 | 0 | redirect_rrset, tag, ipset, search_only, region, |
1052 | 0 | rpz_used, rpz_log, log_name, rpz_cname_override); |
1053 | 0 | } |
1054 | 0 | if(raddr) { |
1055 | 0 | lock_rw_unlock(&raddr->lock); |
1056 | 0 | } |
1057 | 0 | if(rpz_used) { |
1058 | 0 | lock_rw_unlock(&a->lock); |
1059 | 0 | } |
1060 | 0 | return ret; |
1061 | 0 | } |
1062 | | |
1063 | | static int |
1064 | | generate_cname_request(struct module_qstate* qstate, |
1065 | | struct ub_packed_rrset_key* alias_rrset) |
1066 | 0 | { |
1067 | 0 | struct module_qstate* subq = NULL; |
1068 | 0 | struct query_info subqi; |
1069 | |
|
1070 | 0 | memset(&subqi, 0, sizeof(subqi)); |
1071 | 0 | get_cname_target(alias_rrset, &subqi.qname, &subqi.qname_len); |
1072 | 0 | if(!subqi.qname) |
1073 | 0 | return 0; /* unexpected: not a valid CNAME RDATA */ |
1074 | 0 | subqi.qtype = qstate->qinfo.qtype; |
1075 | 0 | subqi.qclass = qstate->qinfo.qclass; |
1076 | 0 | fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); |
1077 | 0 | return (*qstate->env->attach_sub)(qstate, &subqi, BIT_RD, 0, 0, &subq); |
1078 | 0 | } |
1079 | | |
1080 | | void |
1081 | | respip_operate(struct module_qstate* qstate, enum module_ev event, int id, |
1082 | | struct outbound_entry* outbound) |
1083 | 0 | { |
1084 | 0 | struct respip_qstate* rq = (struct respip_qstate*)qstate->minfo[id]; |
1085 | |
|
1086 | 0 | log_query_info(VERB_QUERY, "respip operate: query", &qstate->qinfo); |
1087 | 0 | (void)outbound; |
1088 | |
|
1089 | 0 | if(event == module_event_new || event == module_event_pass) { |
1090 | 0 | if(!rq) { |
1091 | 0 | rq = regional_alloc_zero(qstate->region, sizeof(*rq)); |
1092 | 0 | if(!rq) |
1093 | 0 | goto servfail; |
1094 | 0 | rq->state = RESPIP_INIT; |
1095 | 0 | qstate->minfo[id] = rq; |
1096 | 0 | } |
1097 | 0 | if(rq->state == RESPIP_SUBQUERY_FINISHED) { |
1098 | 0 | qstate->ext_state[id] = module_finished; |
1099 | 0 | return; |
1100 | 0 | } |
1101 | 0 | verbose(VERB_ALGO, "respip: pass to next module"); |
1102 | 0 | qstate->ext_state[id] = module_wait_module; |
1103 | 0 | } else if(event == module_event_moddone) { |
1104 | | /* If the reply may be subject to response-ip rewriting |
1105 | | * according to the query type, check the actions. If a |
1106 | | * rewrite is necessary, we'll replace the reply in qstate |
1107 | | * with the new one. */ |
1108 | 0 | enum module_ext_state next_state = module_finished; |
1109 | |
|
1110 | 0 | if((qstate->qinfo.qtype == LDNS_RR_TYPE_A || |
1111 | 0 | qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA || |
1112 | 0 | qstate->qinfo.qtype == LDNS_RR_TYPE_ANY) && |
1113 | 0 | qstate->return_msg && qstate->return_msg->rep) { |
1114 | 0 | struct reply_info* new_rep = qstate->return_msg->rep; |
1115 | 0 | struct ub_packed_rrset_key* alias_rrset = NULL; |
1116 | 0 | struct respip_action_info actinfo = {0, 0, 0, 0, NULL, 0, NULL}; |
1117 | 0 | actinfo.action = respip_none; |
1118 | |
|
1119 | 0 | if(!respip_rewrite_reply(&qstate->qinfo, |
1120 | 0 | qstate->client_info, qstate->return_msg->rep, |
1121 | 0 | &new_rep, &actinfo, &alias_rrset, 0, |
1122 | 0 | qstate->region, qstate->env->auth_zones, |
1123 | 0 | &qstate->rpz_passthru, qstate->env->views, |
1124 | 0 | qstate->env->respip_set)) { |
1125 | 0 | goto servfail; |
1126 | 0 | } |
1127 | 0 | if(actinfo.action != respip_none) { |
1128 | | /* save action info for logging on a |
1129 | | * per-front-end-query basis */ |
1130 | 0 | if(!(qstate->respip_action_info = |
1131 | 0 | regional_alloc_init(qstate->region, |
1132 | 0 | &actinfo, sizeof(actinfo)))) |
1133 | 0 | { |
1134 | 0 | log_err("out of memory"); |
1135 | 0 | goto servfail; |
1136 | 0 | } |
1137 | 0 | } else { |
1138 | 0 | qstate->respip_action_info = NULL; |
1139 | 0 | } |
1140 | 0 | if (actinfo.action == respip_always_deny || |
1141 | 0 | (new_rep == qstate->return_msg->rep && |
1142 | 0 | (actinfo.action == respip_deny || |
1143 | 0 | actinfo.action == respip_inform_deny))) { |
1144 | | /* for deny-variant actions (unless response-ip |
1145 | | * data is applied), mark the query state so |
1146 | | * the response will be dropped for all |
1147 | | * clients. */ |
1148 | 0 | qstate->is_drop = 1; |
1149 | 0 | } else if(alias_rrset) { |
1150 | 0 | if(!generate_cname_request(qstate, alias_rrset)) |
1151 | 0 | goto servfail; |
1152 | 0 | next_state = module_wait_subquery; |
1153 | 0 | } |
1154 | 0 | qstate->return_msg->rep = new_rep; |
1155 | 0 | } |
1156 | 0 | qstate->ext_state[id] = next_state; |
1157 | 0 | } else |
1158 | 0 | qstate->ext_state[id] = module_finished; |
1159 | | |
1160 | 0 | return; |
1161 | | |
1162 | 0 | servfail: |
1163 | 0 | qstate->return_rcode = LDNS_RCODE_SERVFAIL; |
1164 | 0 | qstate->return_msg = NULL; |
1165 | 0 | } |
1166 | | |
1167 | | int |
1168 | | respip_merge_cname(struct reply_info* base_rep, |
1169 | | const struct query_info* qinfo, const struct reply_info* tgt_rep, |
1170 | | const struct respip_client_info* cinfo, int must_validate, |
1171 | | struct reply_info** new_repp, struct regional* region, |
1172 | | struct auth_zones* az, struct views* views, |
1173 | | struct respip_set* respip_set) |
1174 | 0 | { |
1175 | 0 | struct reply_info* new_rep; |
1176 | 0 | struct reply_info* tmp_rep = NULL; /* just a placeholder */ |
1177 | 0 | struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */ |
1178 | 0 | uint16_t tgt_rcode; |
1179 | 0 | size_t i, j; |
1180 | 0 | struct respip_action_info actinfo = {0, 0, 0, 0, NULL, 0, NULL}; |
1181 | 0 | actinfo.action = respip_none; |
1182 | | |
1183 | | /* If the query for the CNAME target would result in an unusual rcode, |
1184 | | * we generally translate it as a failure for the base query |
1185 | | * (which would then be translated into SERVFAIL). The only exception |
1186 | | * is NXDOMAIN and YXDOMAIN, which are passed to the end client(s). |
1187 | | * The YXDOMAIN case would be rare but still possible (when |
1188 | | * DNSSEC-validated DNAME has been cached but synthesizing CNAME |
1189 | | * can't be generated due to length limitation) */ |
1190 | 0 | tgt_rcode = FLAGS_GET_RCODE(tgt_rep->flags); |
1191 | 0 | if((tgt_rcode != LDNS_RCODE_NOERROR && |
1192 | 0 | tgt_rcode != LDNS_RCODE_NXDOMAIN && |
1193 | 0 | tgt_rcode != LDNS_RCODE_YXDOMAIN) || |
1194 | 0 | (must_validate && tgt_rep->security <= sec_status_bogus)) { |
1195 | 0 | return 0; |
1196 | 0 | } |
1197 | | |
1198 | | /* see if the target reply would be subject to a response-ip action. */ |
1199 | 0 | if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo, |
1200 | 0 | &alias_rrset, 1, region, az, NULL, views, respip_set)) |
1201 | 0 | return 0; |
1202 | 0 | if(actinfo.action != respip_none) { |
1203 | 0 | log_info("CNAME target of redirect response-ip action would " |
1204 | 0 | "be subject to response-ip action, too; stripped"); |
1205 | 0 | *new_repp = base_rep; |
1206 | 0 | return 1; |
1207 | 0 | } |
1208 | | |
1209 | | /* Append target reply to the base. Since we cannot assume |
1210 | | * tgt_rep->rrsets is valid throughout the lifetime of new_rep |
1211 | | * or it can be safely shared by multiple threads, we need to make a |
1212 | | * deep copy. */ |
1213 | 0 | new_rep = make_new_reply_info(base_rep, region, |
1214 | 0 | base_rep->an_numrrsets + tgt_rep->an_numrrsets, |
1215 | 0 | base_rep->an_numrrsets); |
1216 | 0 | if(!new_rep) |
1217 | 0 | return 0; |
1218 | 0 | for(i=0,j=base_rep->an_numrrsets; i<tgt_rep->an_numrrsets; i++,j++) { |
1219 | 0 | new_rep->rrsets[j] = respip_copy_rrset(tgt_rep->rrsets[i], region); |
1220 | 0 | if(!new_rep->rrsets[j]) |
1221 | 0 | return 0; |
1222 | 0 | } |
1223 | | |
1224 | 0 | FLAGS_SET_RCODE(new_rep->flags, tgt_rcode); |
1225 | 0 | *new_repp = new_rep; |
1226 | 0 | return 1; |
1227 | 0 | } |
1228 | | |
1229 | | void |
1230 | | respip_inform_super(struct module_qstate* qstate, int id, |
1231 | | struct module_qstate* super) |
1232 | 0 | { |
1233 | 0 | struct respip_qstate* rq = (struct respip_qstate*)super->minfo[id]; |
1234 | 0 | struct reply_info* new_rep = NULL; |
1235 | |
|
1236 | 0 | rq->state = RESPIP_SUBQUERY_FINISHED; |
1237 | | |
1238 | | /* respip subquery should have always been created with a valid reply |
1239 | | * in super. */ |
1240 | 0 | log_assert(super->return_msg && super->return_msg->rep); |
1241 | | |
1242 | | /* return_msg can be NULL when, e.g., the sub query resulted in |
1243 | | * SERVFAIL, in which case we regard it as a failure of the original |
1244 | | * query. Other checks are probably redundant, but we check them |
1245 | | * for safety. */ |
1246 | 0 | if(!qstate->return_msg || !qstate->return_msg->rep || |
1247 | 0 | qstate->return_rcode != LDNS_RCODE_NOERROR) |
1248 | 0 | goto fail; |
1249 | | |
1250 | 0 | if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo, |
1251 | 0 | qstate->return_msg->rep, super->client_info, |
1252 | 0 | super->env->need_to_validate, &new_rep, super->region, |
1253 | 0 | qstate->env->auth_zones, qstate->env->views, |
1254 | 0 | qstate->env->respip_set)) |
1255 | 0 | goto fail; |
1256 | 0 | super->return_msg->rep = new_rep; |
1257 | 0 | return; |
1258 | | |
1259 | 0 | fail: |
1260 | 0 | super->return_rcode = LDNS_RCODE_SERVFAIL; |
1261 | 0 | super->return_msg = NULL; |
1262 | 0 | return; |
1263 | 0 | } |
1264 | | |
1265 | | void |
1266 | | respip_clear(struct module_qstate* qstate, int id) |
1267 | 0 | { |
1268 | 0 | qstate->minfo[id] = NULL; |
1269 | 0 | } |
1270 | | |
1271 | | size_t |
1272 | | respip_get_mem(struct module_env* env, int id) |
1273 | 0 | { |
1274 | 0 | (void)env; |
1275 | 0 | (void)id; |
1276 | 0 | return 0; |
1277 | 0 | } |
1278 | | |
1279 | | /** |
1280 | | * The response-ip function block |
1281 | | */ |
1282 | | static struct module_func_block respip_block = { |
1283 | | "respip", |
1284 | | NULL, NULL, &respip_init, &respip_deinit, &respip_operate, |
1285 | | &respip_inform_super, &respip_clear, &respip_get_mem |
1286 | | }; |
1287 | | |
1288 | | struct module_func_block* |
1289 | | respip_get_funcblock(void) |
1290 | 0 | { |
1291 | 0 | return &respip_block; |
1292 | 0 | } |
1293 | | |
1294 | | enum respip_action |
1295 | | resp_addr_get_action(const struct resp_addr* addr) |
1296 | 0 | { |
1297 | 0 | return addr ? addr->action : respip_none; |
1298 | 0 | } |
1299 | | |
1300 | | struct ub_packed_rrset_key* |
1301 | | resp_addr_get_rrset(struct resp_addr* addr) |
1302 | 0 | { |
1303 | 0 | return addr ? addr->data : NULL; |
1304 | 0 | } |
1305 | | |
1306 | | int |
1307 | | respip_set_is_empty(const struct respip_set* set) |
1308 | 0 | { |
1309 | 0 | return set ? set->ip_tree.count == 0 : 1; |
1310 | 0 | } |
1311 | | |
1312 | | void |
1313 | | respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname, |
1314 | | uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias, |
1315 | | struct sockaddr_storage* addr, socklen_t addrlen) |
1316 | 0 | { |
1317 | 0 | char srcip[128], respip[128], txt[512]; |
1318 | 0 | unsigned port; |
1319 | 0 | struct respip_addr_info* respip_addr = respip_actinfo->addrinfo; |
1320 | 0 | size_t txtlen = 0; |
1321 | 0 | const char* actionstr = NULL; |
1322 | |
|
1323 | 0 | if(local_alias) |
1324 | 0 | qname = local_alias->rrset->rk.dname; |
1325 | 0 | port = (unsigned)((addr->ss_family == AF_INET) ? |
1326 | 0 | ntohs(((struct sockaddr_in*)addr)->sin_port) : |
1327 | 0 | ntohs(((struct sockaddr_in6*)addr)->sin6_port)); |
1328 | 0 | addr_to_str(addr, addrlen, srcip, sizeof(srcip)); |
1329 | 0 | addr_to_str(&respip_addr->addr, respip_addr->addrlen, |
1330 | 0 | respip, sizeof(respip)); |
1331 | 0 | if(respip_actinfo->rpz_log) { |
1332 | 0 | txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, "%s", |
1333 | 0 | "rpz: applied "); |
1334 | 0 | if(respip_actinfo->rpz_cname_override) |
1335 | 0 | actionstr = rpz_action_to_string( |
1336 | 0 | RPZ_CNAME_OVERRIDE_ACTION); |
1337 | 0 | else |
1338 | 0 | actionstr = rpz_action_to_string( |
1339 | 0 | respip_action_to_rpz_action( |
1340 | 0 | respip_actinfo->action)); |
1341 | 0 | } |
1342 | 0 | if(respip_actinfo->log_name) { |
1343 | 0 | txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, |
1344 | 0 | "[%s] ", respip_actinfo->log_name); |
1345 | 0 | } |
1346 | 0 | snprintf(txt+txtlen, sizeof(txt)-txtlen, |
1347 | 0 | "%s/%d %s %s@%u", respip, respip_addr->net, |
1348 | 0 | (actionstr) ? actionstr : "inform", srcip, port); |
1349 | 0 | log_nametypeclass(NO_VERBOSE, txt, qname, qtype, qclass); |
1350 | 0 | } |
1351 | | |
1352 | | size_t respip_set_get_mem(struct respip_set* set) |
1353 | 0 | { |
1354 | 0 | size_t m; |
1355 | 0 | if(!set) return 0; |
1356 | 0 | m = sizeof(*set); |
1357 | 0 | lock_rw_rdlock(&set->lock); |
1358 | 0 | m += regional_get_mem(set->region); |
1359 | 0 | lock_rw_unlock(&set->lock); |
1360 | 0 | return m; |
1361 | 0 | } |
1362 | | |
1363 | | void |
1364 | | respip_set_swap_tree(struct respip_set* respip_set, |
1365 | | struct respip_set* data) |
1366 | 0 | { |
1367 | 0 | rbnode_type* oldroot = respip_set->ip_tree.root; |
1368 | 0 | size_t oldcount = respip_set->ip_tree.count; |
1369 | 0 | struct regional* oldregion = respip_set->region; |
1370 | 0 | char* const* oldtagname = respip_set->tagname; |
1371 | 0 | int oldnum_tags = respip_set->num_tags; |
1372 | 0 | respip_set->ip_tree.root = data->ip_tree.root; |
1373 | 0 | respip_set->ip_tree.count = data->ip_tree.count; |
1374 | 0 | respip_set->region = data->region; |
1375 | 0 | respip_set->tagname = data->tagname; |
1376 | 0 | respip_set->num_tags = data->num_tags; |
1377 | 0 | data->ip_tree.root = oldroot; |
1378 | 0 | data->ip_tree.count = oldcount; |
1379 | 0 | data->region = oldregion; |
1380 | 0 | data->tagname = oldtagname; |
1381 | 0 | data->num_tags = oldnum_tags; |
1382 | 0 | } |