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