xref: /openbsd-src/usr.sbin/unbound/respip/respip.c (revision 98bc733b08604094f4138174a0ee0bb9faaca4bd)
12be9e038Ssthen /*
22be9e038Ssthen  * respip/respip.c - filtering response IP module
32be9e038Ssthen  */
42be9e038Ssthen 
52be9e038Ssthen /**
62be9e038Ssthen  * \file
72be9e038Ssthen  *
82be9e038Ssthen  * This file contains a module that inspects a result of recursive resolution
92be9e038Ssthen  * to see if any IP address record should trigger a special action.
102be9e038Ssthen  * If applicable these actions can modify the original response.
112be9e038Ssthen  */
122be9e038Ssthen #include "config.h"
132be9e038Ssthen 
142be9e038Ssthen #include "services/localzone.h"
15eaf2578eSsthen #include "services/authzone.h"
162be9e038Ssthen #include "services/cache/dns.h"
172be9e038Ssthen #include "sldns/str2wire.h"
182be9e038Ssthen #include "util/config_file.h"
192be9e038Ssthen #include "util/fptr_wlist.h"
202be9e038Ssthen #include "util/module.h"
212be9e038Ssthen #include "util/net_help.h"
222be9e038Ssthen #include "util/regional.h"
232be9e038Ssthen #include "util/data/msgreply.h"
242be9e038Ssthen #include "util/storage/dnstree.h"
252be9e038Ssthen #include "respip/respip.h"
262be9e038Ssthen #include "services/view.h"
272be9e038Ssthen #include "sldns/rrdef.h"
28e21c60efSsthen #include "util/data/dname.h"
292be9e038Ssthen 
302be9e038Ssthen 
312be9e038Ssthen /** Subset of resp_addr.node, used for inform-variant logging */
322be9e038Ssthen struct respip_addr_info {
332be9e038Ssthen 	struct sockaddr_storage addr;
342be9e038Ssthen 	socklen_t addrlen;
352be9e038Ssthen 	int net;
362be9e038Ssthen };
372be9e038Ssthen 
382be9e038Ssthen /** Query state regarding the response-ip module. */
392be9e038Ssthen enum respip_state {
402be9e038Ssthen 	/**
412be9e038Ssthen 	 * The general state.  Unless CNAME chasing takes place, all processing
422be9e038Ssthen 	 * is completed in this state without any other asynchronous event.
432be9e038Ssthen 	 */
442be9e038Ssthen 	RESPIP_INIT = 0,
452be9e038Ssthen 
462be9e038Ssthen 	/**
472be9e038Ssthen 	 * A subquery for CNAME chasing is completed.
482be9e038Ssthen 	 */
492be9e038Ssthen 	RESPIP_SUBQUERY_FINISHED
502be9e038Ssthen };
512be9e038Ssthen 
522be9e038Ssthen /** Per query state for the response-ip module. */
532be9e038Ssthen struct respip_qstate {
542be9e038Ssthen 	enum respip_state state;
552be9e038Ssthen };
562be9e038Ssthen 
572be9e038Ssthen struct respip_set*
582be9e038Ssthen respip_set_create(void)
592be9e038Ssthen {
602be9e038Ssthen 	struct respip_set* set = calloc(1, sizeof(*set));
612be9e038Ssthen 	if(!set)
622be9e038Ssthen 		return NULL;
632be9e038Ssthen 	set->region = regional_create();
642be9e038Ssthen 	if(!set->region) {
652be9e038Ssthen 		free(set);
662be9e038Ssthen 		return NULL;
672be9e038Ssthen 	}
682be9e038Ssthen 	addr_tree_init(&set->ip_tree);
69eaf2578eSsthen 	lock_rw_init(&set->lock);
702be9e038Ssthen 	return set;
712be9e038Ssthen }
722be9e038Ssthen 
73eaf2578eSsthen /** helper traverse to delete resp_addr nodes */
74eaf2578eSsthen static void
75eaf2578eSsthen resp_addr_del(rbnode_type* n, void* ATTR_UNUSED(arg))
76eaf2578eSsthen {
77eaf2578eSsthen 	struct resp_addr* r = (struct resp_addr*)n->key;
78eaf2578eSsthen 	lock_rw_destroy(&r->lock);
79eaf2578eSsthen #ifdef THREADS_DISABLED
80eaf2578eSsthen 	(void)r;
81eaf2578eSsthen #endif
82eaf2578eSsthen }
83eaf2578eSsthen 
842be9e038Ssthen void
852be9e038Ssthen respip_set_delete(struct respip_set* set)
862be9e038Ssthen {
872be9e038Ssthen 	if(!set)
882be9e038Ssthen 		return;
89eaf2578eSsthen 	lock_rw_destroy(&set->lock);
90eaf2578eSsthen 	traverse_postorder(&set->ip_tree, resp_addr_del, NULL);
912be9e038Ssthen 	regional_destroy(set->region);
922be9e038Ssthen 	free(set);
932be9e038Ssthen }
942be9e038Ssthen 
952be9e038Ssthen struct rbtree_type*
962be9e038Ssthen respip_set_get_tree(struct respip_set* set)
972be9e038Ssthen {
982be9e038Ssthen 	if(!set)
992be9e038Ssthen 		return NULL;
1002be9e038Ssthen 	return &set->ip_tree;
1012be9e038Ssthen }
1022be9e038Ssthen 
103eaf2578eSsthen struct resp_addr*
104eaf2578eSsthen respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr,
105eaf2578eSsthen 		socklen_t addrlen, int net, int create, const char* ipstr)
106eaf2578eSsthen {
107eaf2578eSsthen 	struct resp_addr* node;
108eaf2578eSsthen 	node = (struct resp_addr*)addr_tree_find(&set->ip_tree, addr, addrlen, net);
109eaf2578eSsthen 	if(!node && create) {
110eaf2578eSsthen 		node = regional_alloc_zero(set->region, sizeof(*node));
111eaf2578eSsthen 		if(!node) {
112eaf2578eSsthen 			log_err("out of memory");
113eaf2578eSsthen 			return NULL;
114eaf2578eSsthen 		}
115eaf2578eSsthen 		lock_rw_init(&node->lock);
116eaf2578eSsthen 		node->action = respip_none;
117eaf2578eSsthen 		if(!addr_tree_insert(&set->ip_tree, &node->node, addr,
118eaf2578eSsthen 			addrlen, net)) {
119eaf2578eSsthen 			/* We know we didn't find it, so this should be
120eaf2578eSsthen 			 * impossible. */
121eaf2578eSsthen 			log_warn("unexpected: duplicate address: %s", ipstr);
122eaf2578eSsthen 		}
123eaf2578eSsthen 	}
124eaf2578eSsthen 	return node;
125eaf2578eSsthen }
126eaf2578eSsthen 
127eaf2578eSsthen void
128eaf2578eSsthen respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node)
129eaf2578eSsthen {
130eaf2578eSsthen 	struct resp_addr* prev;
131eaf2578eSsthen 	prev = (struct resp_addr*)rbtree_previous((struct rbnode_type*)node);
132eaf2578eSsthen 	lock_rw_destroy(&node->lock);
133191f22c6Ssthen 	(void)rbtree_delete(&set->ip_tree, node);
134eaf2578eSsthen 	/* no free'ing, all allocated in region */
135eaf2578eSsthen 	if(!prev)
136eaf2578eSsthen 		addr_tree_init_parents((rbtree_type*)set);
137eaf2578eSsthen 	else
138eaf2578eSsthen 		addr_tree_init_parents_node(&prev->node);
139eaf2578eSsthen }
140eaf2578eSsthen 
1412be9e038Ssthen /** returns the node in the address tree for the specified netblock string;
1422be9e038Ssthen  * non-existent node will be created if 'create' is true */
1432be9e038Ssthen static struct resp_addr*
1442be9e038Ssthen respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
1452be9e038Ssthen {
1462be9e038Ssthen 	struct sockaddr_storage addr;
1472be9e038Ssthen 	int net;
1482be9e038Ssthen 	socklen_t addrlen;
1492be9e038Ssthen 
1502be9e038Ssthen 	if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) {
1512be9e038Ssthen 		log_err("cannot parse netblock: '%s'", ipstr);
1522be9e038Ssthen 		return NULL;
1532be9e038Ssthen 	}
154eaf2578eSsthen 	return respip_sockaddr_find_or_create(set, &addr, addrlen, net, create,
155eaf2578eSsthen 		ipstr);
1562be9e038Ssthen }
1572be9e038Ssthen 
1582be9e038Ssthen static int
1592be9e038Ssthen respip_tag_cfg(struct respip_set* set, const char* ipstr,
1602be9e038Ssthen 	const uint8_t* taglist, size_t taglen)
1612be9e038Ssthen {
1622be9e038Ssthen 	struct resp_addr* node;
1632be9e038Ssthen 
1642be9e038Ssthen 	if(!(node=respip_find_or_create(set, ipstr, 1)))
1652be9e038Ssthen 		return 0;
1662be9e038Ssthen 	if(node->taglist) {
1672be9e038Ssthen 		log_warn("duplicate response-address-tag for '%s', overridden.",
1682be9e038Ssthen 			ipstr);
1692be9e038Ssthen 	}
1702be9e038Ssthen 	node->taglist = regional_alloc_init(set->region, taglist, taglen);
1712be9e038Ssthen 	if(!node->taglist) {
1722be9e038Ssthen 		log_err("out of memory");
1732be9e038Ssthen 		return 0;
1742be9e038Ssthen 	}
1752be9e038Ssthen 	node->taglen = taglen;
1762be9e038Ssthen 	return 1;
1772be9e038Ssthen }
1782be9e038Ssthen 
1792be9e038Ssthen /** set action for the node specified by the netblock string */
1802be9e038Ssthen static int
1812be9e038Ssthen respip_action_cfg(struct respip_set* set, const char* ipstr,
1822be9e038Ssthen 	const char* actnstr)
1832be9e038Ssthen {
1842be9e038Ssthen 	struct resp_addr* node;
1852be9e038Ssthen 	enum respip_action action;
1862be9e038Ssthen 
1872be9e038Ssthen 	if(!(node=respip_find_or_create(set, ipstr, 1)))
1882be9e038Ssthen 		return 0;
1892be9e038Ssthen 	if(node->action != respip_none) {
190452a1548Ssthen 		verbose(VERB_QUERY, "duplicate response-ip action for '%s', overridden.",
1912be9e038Ssthen 			ipstr);
1922be9e038Ssthen 	}
1932be9e038Ssthen         if(strcmp(actnstr, "deny") == 0)
1942be9e038Ssthen                 action = respip_deny;
1952be9e038Ssthen         else if(strcmp(actnstr, "redirect") == 0)
1962be9e038Ssthen                 action = respip_redirect;
1972be9e038Ssthen         else if(strcmp(actnstr, "inform") == 0)
1982be9e038Ssthen                 action = respip_inform;
1992be9e038Ssthen         else if(strcmp(actnstr, "inform_deny") == 0)
2002be9e038Ssthen                 action = respip_inform_deny;
201c3b38330Ssthen         else if(strcmp(actnstr, "inform_redirect") == 0)
202c3b38330Ssthen                 action = respip_inform_redirect;
2032be9e038Ssthen         else if(strcmp(actnstr, "always_transparent") == 0)
2042be9e038Ssthen                 action = respip_always_transparent;
2052be9e038Ssthen         else if(strcmp(actnstr, "always_refuse") == 0)
2062be9e038Ssthen                 action = respip_always_refuse;
2072be9e038Ssthen         else if(strcmp(actnstr, "always_nxdomain") == 0)
2082be9e038Ssthen                 action = respip_always_nxdomain;
209eaf2578eSsthen         else if(strcmp(actnstr, "always_nodata") == 0)
210eaf2578eSsthen                 action = respip_always_nodata;
211eaf2578eSsthen         else if(strcmp(actnstr, "always_deny") == 0)
212eaf2578eSsthen                 action = respip_always_deny;
2132be9e038Ssthen         else {
2142be9e038Ssthen                 log_err("unknown response-ip action %s", actnstr);
2152be9e038Ssthen                 return 0;
2162be9e038Ssthen         }
2172be9e038Ssthen 	node->action = action;
2182be9e038Ssthen 	return 1;
2192be9e038Ssthen }
2202be9e038Ssthen 
2212be9e038Ssthen /** allocate and initialize an rrset structure; this function is based
2222be9e038Ssthen  * on new_local_rrset() from the localzone.c module */
2232be9e038Ssthen static struct ub_packed_rrset_key*
2242be9e038Ssthen new_rrset(struct regional* region, uint16_t rrtype, uint16_t rrclass)
2252be9e038Ssthen {
2262be9e038Ssthen 	struct packed_rrset_data* pd;
2272be9e038Ssthen 	struct ub_packed_rrset_key* rrset = regional_alloc_zero(
2282be9e038Ssthen 		region, sizeof(*rrset));
2292be9e038Ssthen 	if(!rrset) {
2302be9e038Ssthen 		log_err("out of memory");
2312be9e038Ssthen 		return NULL;
2322be9e038Ssthen 	}
2332be9e038Ssthen 	rrset->entry.key = rrset;
2342be9e038Ssthen 	pd = regional_alloc_zero(region, sizeof(*pd));
2352be9e038Ssthen 	if(!pd) {
2362be9e038Ssthen 		log_err("out of memory");
2372be9e038Ssthen 		return NULL;
2382be9e038Ssthen 	}
2392be9e038Ssthen 	pd->trust = rrset_trust_prim_noglue;
2402be9e038Ssthen 	pd->security = sec_status_insecure;
2412be9e038Ssthen 	rrset->entry.data = pd;
2422be9e038Ssthen 	rrset->rk.dname = regional_alloc_zero(region, 1);
2432be9e038Ssthen 	if(!rrset->rk.dname) {
2442be9e038Ssthen 		log_err("out of memory");
2452be9e038Ssthen 		return NULL;
2462be9e038Ssthen 	}
2472be9e038Ssthen 	rrset->rk.dname_len = 1;
2482be9e038Ssthen 	rrset->rk.type = htons(rrtype);
2492be9e038Ssthen 	rrset->rk.rrset_class = htons(rrclass);
2502be9e038Ssthen 	return rrset;
2512be9e038Ssthen }
2522be9e038Ssthen 
2532be9e038Ssthen /** enter local data as resource records into a response-ip node */
254eaf2578eSsthen 
255eaf2578eSsthen int
2562be9e038Ssthen respip_enter_rr(struct regional* region, struct resp_addr* raddr,
257eaf2578eSsthen 	uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata,
258eaf2578eSsthen 	size_t rdata_len, const char* rrstr, const char* netblockstr)
259eaf2578eSsthen {
260eaf2578eSsthen 	struct packed_rrset_data* pd;
261eaf2578eSsthen 	struct sockaddr* sa;
262eaf2578eSsthen 	sa = (struct sockaddr*)&raddr->node.addr;
263eaf2578eSsthen 	if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) {
264eaf2578eSsthen 		log_err("CNAME response-ip data (%s) can not co-exist with other "
265eaf2578eSsthen 			"response-ip data for netblock %s", rrstr, netblockstr);
266eaf2578eSsthen 		return 0;
267eaf2578eSsthen 	} else if (raddr->data &&
268eaf2578eSsthen 		raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
269eaf2578eSsthen 		log_err("response-ip data (%s) can not be added; CNAME response-ip "
270eaf2578eSsthen 			"data already in place for netblock %s", rrstr, netblockstr);
271eaf2578eSsthen 		return 0;
272eaf2578eSsthen 	} else if((rrtype != LDNS_RR_TYPE_CNAME) &&
273eaf2578eSsthen 		((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) ||
274eaf2578eSsthen 		(sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) {
275eaf2578eSsthen 		log_err("response-ip data %s record type does not correspond "
276eaf2578eSsthen 			"to netblock %s address family", rrstr, netblockstr);
277eaf2578eSsthen 		return 0;
278eaf2578eSsthen 	}
279eaf2578eSsthen 
280eaf2578eSsthen 	if(!raddr->data) {
281eaf2578eSsthen 		raddr->data = new_rrset(region, rrtype, rrclass);
282eaf2578eSsthen 		if(!raddr->data)
283eaf2578eSsthen 			return 0;
284eaf2578eSsthen 	}
285eaf2578eSsthen 	pd = raddr->data->entry.data;
286eaf2578eSsthen 	return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr);
287eaf2578eSsthen }
288eaf2578eSsthen 
289eaf2578eSsthen static int
290eaf2578eSsthen respip_enter_rrstr(struct regional* region, struct resp_addr* raddr,
2912be9e038Ssthen 		const char* rrstr, const char* netblock)
2922be9e038Ssthen {
2932be9e038Ssthen 	uint8_t* nm;
2942be9e038Ssthen 	uint16_t rrtype = 0, rrclass = 0;
2952be9e038Ssthen 	time_t ttl = 0;
2962be9e038Ssthen 	uint8_t rr[LDNS_RR_BUF_SIZE];
2972be9e038Ssthen 	uint8_t* rdata = NULL;
2982be9e038Ssthen 	size_t rdata_len = 0;
2992be9e038Ssthen 	char buf[65536];
3002be9e038Ssthen 	char bufshort[64];
3012be9e038Ssthen 	int ret;
302c3b38330Ssthen 	if(raddr->action != respip_redirect
303c3b38330Ssthen 		&& raddr->action != respip_inform_redirect) {
3042be9e038Ssthen 		log_err("cannot parse response-ip-data %s: response-ip "
3052be9e038Ssthen 			"action for %s is not redirect", rrstr, netblock);
3062be9e038Ssthen 		return 0;
3072be9e038Ssthen 	}
3082be9e038Ssthen 	ret = snprintf(buf, sizeof(buf), ". %s", rrstr);
3092be9e038Ssthen 	if(ret < 0 || ret >= (int)sizeof(buf)) {
3102be9e038Ssthen 		strlcpy(bufshort, rrstr, sizeof(bufshort));
3112be9e038Ssthen 		log_err("bad response-ip-data: %s...", bufshort);
3122be9e038Ssthen 		return 0;
3132be9e038Ssthen 	}
3142be9e038Ssthen 	if(!rrstr_get_rr_content(buf, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr),
3152be9e038Ssthen 		&rdata, &rdata_len)) {
3162be9e038Ssthen 		log_err("bad response-ip-data: %s", rrstr);
3172be9e038Ssthen 		return 0;
3182be9e038Ssthen 	}
3192be9e038Ssthen 	free(nm);
320eaf2578eSsthen 	return respip_enter_rr(region, raddr, rrtype, rrclass, ttl, rdata,
321eaf2578eSsthen 		rdata_len, rrstr, netblock);
3222be9e038Ssthen }
3232be9e038Ssthen 
3242be9e038Ssthen static int
3252be9e038Ssthen respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr)
3262be9e038Ssthen {
3272be9e038Ssthen 	struct resp_addr* node;
3282be9e038Ssthen 
3292be9e038Ssthen 	node=respip_find_or_create(set, ipstr, 0);
3302be9e038Ssthen 	if(!node || node->action == respip_none) {
3312be9e038Ssthen 		log_err("cannot parse response-ip-data %s: "
3322be9e038Ssthen 			"response-ip node for %s not found", rrstr, ipstr);
3332be9e038Ssthen 		return 0;
3342be9e038Ssthen 	}
335eaf2578eSsthen 	return respip_enter_rrstr(set->region, node, rrstr, ipstr);
3362be9e038Ssthen }
3372be9e038Ssthen 
3382be9e038Ssthen static int
3392be9e038Ssthen respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags,
3402be9e038Ssthen 	struct config_strbytelist* respip_tags,
3412be9e038Ssthen 	struct config_str2list* respip_actions,
3422be9e038Ssthen 	struct config_str2list* respip_data)
3432be9e038Ssthen {
3442be9e038Ssthen 	struct config_strbytelist* p;
3452be9e038Ssthen 	struct config_str2list* pa;
3462be9e038Ssthen 	struct config_str2list* pd;
3472be9e038Ssthen 
3482be9e038Ssthen 	set->tagname = tagname;
3492be9e038Ssthen 	set->num_tags = num_tags;
3502be9e038Ssthen 
3512be9e038Ssthen 	p = respip_tags;
3522be9e038Ssthen 	while(p) {
3532be9e038Ssthen 		struct config_strbytelist* np = p->next;
3542be9e038Ssthen 
3552be9e038Ssthen 		log_assert(p->str && p->str2);
3562be9e038Ssthen 		if(!respip_tag_cfg(set, p->str, p->str2, p->str2len)) {
3572be9e038Ssthen 			config_del_strbytelist(p);
3582be9e038Ssthen 			return 0;
3592be9e038Ssthen 		}
3602be9e038Ssthen 		free(p->str);
3612be9e038Ssthen 		free(p->str2);
3622be9e038Ssthen 		free(p);
3632be9e038Ssthen 		p = np;
3642be9e038Ssthen 	}
3652be9e038Ssthen 
3662be9e038Ssthen 	pa = respip_actions;
3672be9e038Ssthen 	while(pa) {
3682be9e038Ssthen 		struct config_str2list* np = pa->next;
3692be9e038Ssthen 		log_assert(pa->str && pa->str2);
3702be9e038Ssthen 		if(!respip_action_cfg(set, pa->str, pa->str2)) {
3712be9e038Ssthen 			config_deldblstrlist(pa);
3722be9e038Ssthen 			return 0;
3732be9e038Ssthen 		}
3742be9e038Ssthen 		free(pa->str);
3752be9e038Ssthen 		free(pa->str2);
3762be9e038Ssthen 		free(pa);
3772be9e038Ssthen 		pa = np;
3782be9e038Ssthen 	}
3792be9e038Ssthen 
3802be9e038Ssthen 	pd = respip_data;
3812be9e038Ssthen 	while(pd) {
3822be9e038Ssthen 		struct config_str2list* np = pd->next;
3832be9e038Ssthen 		log_assert(pd->str && pd->str2);
3842be9e038Ssthen 		if(!respip_data_cfg(set, pd->str, pd->str2)) {
3852be9e038Ssthen 			config_deldblstrlist(pd);
3862be9e038Ssthen 			return 0;
3872be9e038Ssthen 		}
3882be9e038Ssthen 		free(pd->str);
3892be9e038Ssthen 		free(pd->str2);
3902be9e038Ssthen 		free(pd);
3912be9e038Ssthen 		pd = np;
3922be9e038Ssthen 	}
3938240c1b9Ssthen 	addr_tree_init_parents(&set->ip_tree);
3942be9e038Ssthen 
3952be9e038Ssthen 	return 1;
3962be9e038Ssthen }
3972be9e038Ssthen 
3982be9e038Ssthen int
3992be9e038Ssthen respip_global_apply_cfg(struct respip_set* set, struct config_file* cfg)
4002be9e038Ssthen {
4012be9e038Ssthen 	int ret = respip_set_apply_cfg(set, cfg->tagname, cfg->num_tags,
4022be9e038Ssthen 		cfg->respip_tags, cfg->respip_actions, cfg->respip_data);
4032be9e038Ssthen 	cfg->respip_data = NULL;
4042be9e038Ssthen 	cfg->respip_actions = NULL;
4052be9e038Ssthen 	cfg->respip_tags = NULL;
4062be9e038Ssthen 	return ret;
4072be9e038Ssthen }
4082be9e038Ssthen 
4092be9e038Ssthen /** Iterate through raw view data and apply the view-specific respip
4102be9e038Ssthen  * configuration; at this point we should have already seen all the views,
4112be9e038Ssthen  * so if any of the views that respip data refer to does not exist, that's
4122be9e038Ssthen  * an error.  This additional iteration through view configuration data
4132be9e038Ssthen  * is expected to not have significant performance impact (or rather, its
4142be9e038Ssthen  * performance impact is not expected to be prohibitive in the configuration
4152be9e038Ssthen  * processing phase).
4162be9e038Ssthen  */
4172be9e038Ssthen int
4182be9e038Ssthen respip_views_apply_cfg(struct views* vs, struct config_file* cfg,
4192be9e038Ssthen 	int* have_view_respip_cfg)
4202be9e038Ssthen {
4212be9e038Ssthen 	struct config_view* cv;
4222be9e038Ssthen 	struct view* v;
4232be9e038Ssthen 	int ret;
4242be9e038Ssthen 
4252be9e038Ssthen 	for(cv = cfg->views; cv; cv = cv->next) {
4262be9e038Ssthen 
4272be9e038Ssthen 		/** if no respip config for this view then there's
4282be9e038Ssthen 		  * nothing to do; note that even though respip data must go
4292be9e038Ssthen 		  * with respip action, we're checking for both here because
4302be9e038Ssthen 		  * we want to catch the case where the respip action is missing
4312be9e038Ssthen 		  * while the data is present */
4322be9e038Ssthen 		if(!cv->respip_actions && !cv->respip_data)
4332be9e038Ssthen 			continue;
4342be9e038Ssthen 
4352be9e038Ssthen 		if(!(v = views_find_view(vs, cv->name, 1))) {
4362be9e038Ssthen 			log_err("view '%s' unexpectedly missing", cv->name);
4372be9e038Ssthen 			return 0;
4382be9e038Ssthen 		}
4392be9e038Ssthen 		if(!v->respip_set) {
4402be9e038Ssthen 			v->respip_set = respip_set_create();
4412be9e038Ssthen 			if(!v->respip_set) {
4422be9e038Ssthen 				log_err("out of memory");
4432be9e038Ssthen 				lock_rw_unlock(&v->lock);
4442be9e038Ssthen 				return 0;
4452be9e038Ssthen 			}
4462be9e038Ssthen 		}
4472be9e038Ssthen 		ret = respip_set_apply_cfg(v->respip_set, NULL, 0, NULL,
4482be9e038Ssthen 			cv->respip_actions, cv->respip_data);
4492be9e038Ssthen 		lock_rw_unlock(&v->lock);
4502be9e038Ssthen 		if(!ret) {
4512be9e038Ssthen 			log_err("Error while applying respip configuration "
4522be9e038Ssthen 				"for view '%s'", cv->name);
4532be9e038Ssthen 			return 0;
4542be9e038Ssthen 		}
4552be9e038Ssthen 		*have_view_respip_cfg = (*have_view_respip_cfg ||
4562be9e038Ssthen 			v->respip_set->ip_tree.count);
4572be9e038Ssthen 		cv->respip_actions = NULL;
4582be9e038Ssthen 		cv->respip_data = NULL;
4592be9e038Ssthen 	}
4602be9e038Ssthen 	return 1;
4612be9e038Ssthen }
4622be9e038Ssthen 
4632be9e038Ssthen /**
4642be9e038Ssthen  * make a deep copy of 'key' in 'region'.
4652be9e038Ssthen  * This is largely derived from packed_rrset_copy_region() and
4662be9e038Ssthen  * packed_rrset_ptr_fixup(), but differs in the following points:
4672be9e038Ssthen  *
4682be9e038Ssthen  * - It doesn't assume all data in 'key' are in a contiguous memory region.
4692be9e038Ssthen  *   Although that would be the case in most cases, 'key' can be passed from
4702be9e038Ssthen  *   a lower-level module and it might not build the rrset to meet the
4712be9e038Ssthen  *   assumption.  In fact, an rrset specified as response-ip-data or generated
4722be9e038Ssthen  *   in local_data_find_tag_datas() breaks the assumption.  So it would be
4732be9e038Ssthen  *   safer not to naively rely on the assumption.  On the other hand, this
4742be9e038Ssthen  *   function ensures the copied rrset data are in a contiguous region so
4752be9e038Ssthen  *   that it won't cause a disruption even if an upper layer module naively
4762be9e038Ssthen  *   assumes the memory layout.
4772be9e038Ssthen  * - It doesn't copy RRSIGs (if any) in 'key'.  The rrset will be used in
4782be9e038Ssthen  *   a reply that was already faked, so it doesn't make much sense to provide
4792be9e038Ssthen  *   partial sigs even if they are valid themselves.
4802be9e038Ssthen  * - It doesn't adjust TTLs as it basically has to be a verbatim copy of 'key'
4812be9e038Ssthen  *   just allocated in 'region' (the assumption is necessary TTL adjustment
4822be9e038Ssthen  *   has been already done in 'key').
4832be9e038Ssthen  *
4842be9e038Ssthen  * This function returns the copied rrset key on success, and NULL on memory
4852be9e038Ssthen  * allocation failure.
4862be9e038Ssthen  */
487e21c60efSsthen struct ub_packed_rrset_key*
488e21c60efSsthen respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region)
4892be9e038Ssthen {
4902be9e038Ssthen 	struct ub_packed_rrset_key* ck = regional_alloc(region,
4912be9e038Ssthen 		sizeof(struct ub_packed_rrset_key));
4922be9e038Ssthen 	struct packed_rrset_data* d;
4932be9e038Ssthen 	struct packed_rrset_data* data = key->entry.data;
4942be9e038Ssthen 	size_t dsize, i;
4952be9e038Ssthen 	uint8_t* nextrdata;
4962be9e038Ssthen 
4972be9e038Ssthen 	/* derived from packed_rrset_copy_region(), but don't use
4982be9e038Ssthen 	 * packed_rrset_sizeof() and do exclude RRSIGs */
4992be9e038Ssthen 	if(!ck)
5002be9e038Ssthen 		return NULL;
5012be9e038Ssthen 	ck->id = key->id;
5022be9e038Ssthen 	memset(&ck->entry, 0, sizeof(ck->entry));
5032be9e038Ssthen 	ck->entry.hash = key->entry.hash;
5042be9e038Ssthen 	ck->entry.key = ck;
5052be9e038Ssthen 	ck->rk = key->rk;
506a3167c07Ssthen 	if(key->rk.dname) {
5072be9e038Ssthen 		ck->rk.dname = regional_alloc_init(region, key->rk.dname,
5082be9e038Ssthen 			key->rk.dname_len);
5092be9e038Ssthen 		if(!ck->rk.dname)
5102be9e038Ssthen 			return NULL;
511a3167c07Ssthen 		ck->rk.dname_len = key->rk.dname_len;
512a3167c07Ssthen 	} else {
513a3167c07Ssthen 		ck->rk.dname = NULL;
514a3167c07Ssthen 		ck->rk.dname_len = 0;
515a3167c07Ssthen 	}
5162be9e038Ssthen 
517ebf5bb73Ssthen 	if((unsigned)data->count >= 0xffff00U)
518ebf5bb73Ssthen 		return NULL; /* guard against integer overflow in dsize */
5192be9e038Ssthen 	dsize = sizeof(struct packed_rrset_data) + data->count *
5202be9e038Ssthen 		(sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t));
521ebf5bb73Ssthen 	for(i=0; i<data->count; i++) {
522ebf5bb73Ssthen 		if((unsigned)dsize >= 0x0fffffffU ||
523ebf5bb73Ssthen 			(unsigned)data->rr_len[i] >= 0x0fffffffU)
524ebf5bb73Ssthen 			return NULL; /* guard against integer overflow */
5252be9e038Ssthen 		dsize += data->rr_len[i];
526ebf5bb73Ssthen 	}
5279982a05dSsthen 	d = regional_alloc_zero(region, dsize);
5282be9e038Ssthen 	if(!d)
5292be9e038Ssthen 		return NULL;
5302be9e038Ssthen 	*d = *data;
5312be9e038Ssthen 	d->rrsig_count = 0;
5322be9e038Ssthen 	ck->entry.data = d;
5332be9e038Ssthen 
5342be9e038Ssthen 	/* derived from packed_rrset_ptr_fixup() with copying the data */
5352be9e038Ssthen 	d->rr_len = (size_t*)((uint8_t*)d + sizeof(struct packed_rrset_data));
5362be9e038Ssthen 	d->rr_data = (uint8_t**)&(d->rr_len[d->count]);
5372be9e038Ssthen 	d->rr_ttl = (time_t*)&(d->rr_data[d->count]);
5382be9e038Ssthen 	nextrdata = (uint8_t*)&(d->rr_ttl[d->count]);
5392be9e038Ssthen 	for(i=0; i<d->count; i++) {
5402be9e038Ssthen 		d->rr_len[i] = data->rr_len[i];
5412be9e038Ssthen 		d->rr_ttl[i] = data->rr_ttl[i];
5422be9e038Ssthen 		d->rr_data[i] = nextrdata;
5432be9e038Ssthen 		memcpy(d->rr_data[i], data->rr_data[i], data->rr_len[i]);
5442be9e038Ssthen 		nextrdata += d->rr_len[i];
5452be9e038Ssthen 	}
5462be9e038Ssthen 
5472be9e038Ssthen 	return ck;
5482be9e038Ssthen }
5492be9e038Ssthen 
5502be9e038Ssthen int
5512be9e038Ssthen respip_init(struct module_env* env, int id)
5522be9e038Ssthen {
5532be9e038Ssthen 	(void)env;
5542be9e038Ssthen 	(void)id;
5552be9e038Ssthen 	return 1;
5562be9e038Ssthen }
5572be9e038Ssthen 
5582be9e038Ssthen void
5592be9e038Ssthen respip_deinit(struct module_env* env, int id)
5602be9e038Ssthen {
5612be9e038Ssthen 	(void)env;
5622be9e038Ssthen 	(void)id;
5632be9e038Ssthen }
5642be9e038Ssthen 
5652be9e038Ssthen /** Convert a packed AAAA or A RRset to sockaddr. */
5662be9e038Ssthen static int
5672be9e038Ssthen rdata2sockaddr(const struct packed_rrset_data* rd, uint16_t rtype, size_t i,
5682be9e038Ssthen 	struct sockaddr_storage* ss, socklen_t* addrlenp)
5692be9e038Ssthen {
5702be9e038Ssthen 	/* unbound can accept and cache odd-length AAAA/A records, so we have
5712be9e038Ssthen 	 * to validate the length. */
5722be9e038Ssthen 	if(rtype == LDNS_RR_TYPE_A && rd->rr_len[i] == 6) {
5732be9e038Ssthen 		struct sockaddr_in* sa4 = (struct sockaddr_in*)ss;
5742be9e038Ssthen 
5752be9e038Ssthen 		memset(sa4, 0, sizeof(*sa4));
5762be9e038Ssthen 		sa4->sin_family = AF_INET;
5772be9e038Ssthen 		memcpy(&sa4->sin_addr, rd->rr_data[i] + 2,
5782be9e038Ssthen 			sizeof(sa4->sin_addr));
5792be9e038Ssthen 		*addrlenp = sizeof(*sa4);
5802be9e038Ssthen 		return 1;
5812be9e038Ssthen 	} else if(rtype == LDNS_RR_TYPE_AAAA && rd->rr_len[i] == 18) {
5822be9e038Ssthen 		struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ss;
5832be9e038Ssthen 
5842be9e038Ssthen 		memset(sa6, 0, sizeof(*sa6));
5852be9e038Ssthen 		sa6->sin6_family = AF_INET6;
5862be9e038Ssthen 		memcpy(&sa6->sin6_addr, rd->rr_data[i] + 2,
5872be9e038Ssthen 			sizeof(sa6->sin6_addr));
5882be9e038Ssthen 		*addrlenp = sizeof(*sa6);
5892be9e038Ssthen 		return 1;
5902be9e038Ssthen 	}
5912be9e038Ssthen 	return 0;
5922be9e038Ssthen }
5932be9e038Ssthen 
5942be9e038Ssthen /**
5952be9e038Ssthen  * Search the given 'iptree' for response address information that matches
5962be9e038Ssthen  * any of the IP addresses in an AAAA or A in the answer section of the
5972be9e038Ssthen  * response (stored in 'rep').  If found, a pointer to the matched resp_addr
5982be9e038Ssthen  * structure will be returned, and '*rrset_id' is set to the index in
5992be9e038Ssthen  * rep->rrsets for the RRset that contains the matching IP address record
6002be9e038Ssthen  * (the index is normally 0, but can be larger than that if this is a CNAME
6012be9e038Ssthen  * chain or type-ANY response).
602eaf2578eSsthen  * Returns resp_addr holding read lock.
6032be9e038Ssthen  */
604eaf2578eSsthen static struct resp_addr*
605eaf2578eSsthen respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs,
606e21c60efSsthen 	size_t* rrset_id, size_t* rr_id)
6072be9e038Ssthen {
6082be9e038Ssthen 	size_t i;
6092be9e038Ssthen 	struct resp_addr* ra;
6102be9e038Ssthen 	struct sockaddr_storage ss;
6112be9e038Ssthen 	socklen_t addrlen;
6122be9e038Ssthen 
613eaf2578eSsthen 	lock_rw_rdlock(&rs->lock);
6142be9e038Ssthen 	for(i=0; i<rep->an_numrrsets; i++) {
6152be9e038Ssthen 		size_t j;
6162be9e038Ssthen 		const struct packed_rrset_data* rd;
6172be9e038Ssthen 		uint16_t rtype = ntohs(rep->rrsets[i]->rk.type);
6182be9e038Ssthen 
6192be9e038Ssthen 		if(rtype != LDNS_RR_TYPE_A && rtype != LDNS_RR_TYPE_AAAA)
6202be9e038Ssthen 			continue;
6212be9e038Ssthen 		rd = rep->rrsets[i]->entry.data;
6222be9e038Ssthen 		for(j = 0; j < rd->count; j++) {
6232be9e038Ssthen 			if(!rdata2sockaddr(rd, rtype, j, &ss, &addrlen))
6242be9e038Ssthen 				continue;
625eaf2578eSsthen 			ra = (struct resp_addr*)addr_tree_lookup(&rs->ip_tree,
626eaf2578eSsthen 				&ss, addrlen);
6272be9e038Ssthen 			if(ra) {
6282be9e038Ssthen 				*rrset_id = i;
629e21c60efSsthen 				*rr_id = j;
630eaf2578eSsthen 				lock_rw_rdlock(&ra->lock);
631eaf2578eSsthen 				lock_rw_unlock(&rs->lock);
6322be9e038Ssthen 				return ra;
6332be9e038Ssthen 			}
6342be9e038Ssthen 		}
6352be9e038Ssthen 	}
636eaf2578eSsthen 	lock_rw_unlock(&rs->lock);
6372be9e038Ssthen 	return NULL;
6382be9e038Ssthen }
6392be9e038Ssthen 
6402be9e038Ssthen /**
6412be9e038Ssthen  * See if response-ip or tag data should override the original answer rrset
6422be9e038Ssthen  * (which is rep->rrsets[rrset_id]) and if so override it.
6432be9e038Ssthen  * This is (mostly) equivalent to localzone.c:local_data_answer() but for
6442be9e038Ssthen  * response-ip actions.
6452be9e038Ssthen  * Note that this function distinguishes error conditions from "success but
6462be9e038Ssthen  * not overridden".  This is because we want to avoid accidentally applying
6472be9e038Ssthen  * the "no data" action in case of error.
6482be9e038Ssthen  * @param action: action to apply
649eaf2578eSsthen  * @param data: RRset to use for override
6502be9e038Ssthen  * @param qtype: original query type
6512be9e038Ssthen  * @param rep: original reply message
6522be9e038Ssthen  * @param rrset_id: the rrset ID in 'rep' to which the action should apply
6532be9e038Ssthen  * @param new_repp: see respip_rewrite_reply
6542be9e038Ssthen  * @param tag: if >= 0 the tag ID used to determine the action and data
6552be9e038Ssthen  * @param tag_datas: data corresponding to 'tag'.
6562be9e038Ssthen  * @param tag_datas_size: size of 'tag_datas'
6572be9e038Ssthen  * @param tagname: array of tag names, used for logging
6582be9e038Ssthen  * @param num_tags: size of 'tagname', used for logging
6592be9e038Ssthen  * @param redirect_rrsetp: ptr to redirect record
6602be9e038Ssthen  * @param region: region for building new reply
6612be9e038Ssthen  * @return 1 if overridden, 0 if not overridden, -1 on error.
6622be9e038Ssthen  */
6632be9e038Ssthen static int
664eaf2578eSsthen respip_data_answer(enum respip_action action,
665eaf2578eSsthen 	struct ub_packed_rrset_key* data,
6662be9e038Ssthen 	uint16_t qtype, const struct reply_info* rep,
6672be9e038Ssthen 	size_t rrset_id, struct reply_info** new_repp, int tag,
6682be9e038Ssthen 	struct config_strlist** tag_datas, size_t tag_datas_size,
6692be9e038Ssthen 	char* const* tagname, int num_tags,
6702be9e038Ssthen 	struct ub_packed_rrset_key** redirect_rrsetp, struct regional* region)
6712be9e038Ssthen {
672eaf2578eSsthen 	struct ub_packed_rrset_key* rp = data;
6732be9e038Ssthen 	struct reply_info* new_rep;
6742be9e038Ssthen 	*redirect_rrsetp = NULL;
6752be9e038Ssthen 
6762be9e038Ssthen 	if(action == respip_redirect && tag != -1 &&
6772be9e038Ssthen 		(size_t)tag<tag_datas_size && tag_datas[tag]) {
6782be9e038Ssthen 		struct query_info dataqinfo;
6792be9e038Ssthen 		struct ub_packed_rrset_key r;
6802be9e038Ssthen 
6812be9e038Ssthen 		/* Extract parameters of the original answer rrset that can be
6822be9e038Ssthen 		 * rewritten below, in the form of query_info.  Note that these
6832be9e038Ssthen 		 * can be different from the info of the original query if the
6842be9e038Ssthen 		 * rrset is a CNAME target.*/
6852be9e038Ssthen 		memset(&dataqinfo, 0, sizeof(dataqinfo));
6862be9e038Ssthen 		dataqinfo.qname = rep->rrsets[rrset_id]->rk.dname;
6872be9e038Ssthen 		dataqinfo.qname_len = rep->rrsets[rrset_id]->rk.dname_len;
6882be9e038Ssthen 		dataqinfo.qtype = ntohs(rep->rrsets[rrset_id]->rk.type);
6892be9e038Ssthen 		dataqinfo.qclass = ntohs(rep->rrsets[rrset_id]->rk.rrset_class);
6902be9e038Ssthen 
6912be9e038Ssthen 		memset(&r, 0, sizeof(r));
6922be9e038Ssthen 		if(local_data_find_tag_datas(&dataqinfo, tag_datas[tag], &r,
6932be9e038Ssthen 			region)) {
6942be9e038Ssthen 			verbose(VERB_ALGO,
6952be9e038Ssthen 				"response-ip redirect with tag data [%d] %s",
6962be9e038Ssthen 				tag, (tag<num_tags?tagname[tag]:"null"));
6972be9e038Ssthen 			/* use copy_rrset() to 'normalize' memory layout */
698e21c60efSsthen 			rp = respip_copy_rrset(&r, region);
6992be9e038Ssthen 			if(!rp)
7002be9e038Ssthen 				return -1;
7012be9e038Ssthen 		}
7022be9e038Ssthen 	}
7032be9e038Ssthen 	if(!rp)
7042be9e038Ssthen 		return 0;
7052be9e038Ssthen 
7062be9e038Ssthen 	/* If we are using response-ip-data, we need to make a copy of rrset
7072be9e038Ssthen 	 * to replace the rrset's dname.  Note that, unlike local data, we
7082be9e038Ssthen 	 * rename the dname for other actions than redirect.  This is because
7092be9e038Ssthen 	 * response-ip-data isn't associated to any specific name. */
710eaf2578eSsthen 	if(rp == data) {
711e21c60efSsthen 		rp = respip_copy_rrset(rp, region);
7122be9e038Ssthen 		if(!rp)
7132be9e038Ssthen 			return -1;
7142be9e038Ssthen 		rp->rk.dname = rep->rrsets[rrset_id]->rk.dname;
7152be9e038Ssthen 		rp->rk.dname_len = rep->rrsets[rrset_id]->rk.dname_len;
7162be9e038Ssthen 	}
7172be9e038Ssthen 
7182be9e038Ssthen 	/* Build a new reply with redirect rrset.  We keep any preceding CNAMEs
7192be9e038Ssthen 	 * and replace the address rrset that triggers the action.  If it's
7202be9e038Ssthen 	 * type ANY query, however, no other answer records should be kept
7212be9e038Ssthen 	 * (note that it can't be a CNAME chain in this case due to
7222be9e038Ssthen 	 * sanitizing). */
7232be9e038Ssthen 	if(qtype == LDNS_RR_TYPE_ANY)
7242be9e038Ssthen 		rrset_id = 0;
7252be9e038Ssthen 	new_rep = make_new_reply_info(rep, region, rrset_id + 1, rrset_id);
7262be9e038Ssthen 	if(!new_rep)
7272be9e038Ssthen 		return -1;
7282be9e038Ssthen 	rp->rk.flags |= PACKED_RRSET_FIXEDTTL; /* avoid adjusting TTL */
7292be9e038Ssthen 	new_rep->rrsets[rrset_id] = rp;
7302be9e038Ssthen 
7312be9e038Ssthen 	*redirect_rrsetp = rp;
7322be9e038Ssthen 	*new_repp = new_rep;
7332be9e038Ssthen 	return 1;
7342be9e038Ssthen }
7352be9e038Ssthen 
7362be9e038Ssthen /**
7372be9e038Ssthen  * apply response ip action in case where no action data is provided.
7382be9e038Ssthen  * this is similar to localzone.c:lz_zone_answer() but simplified due to
7392be9e038Ssthen  * the characteristics of response ip:
7402be9e038Ssthen  * - 'deny' variants will be handled at the caller side
7412be9e038Ssthen  * - no specific processing for 'transparent' variants: unlike local zones,
7422be9e038Ssthen  *   there is no such a case of 'no data but name existing'.  so all variants
7432be9e038Ssthen  *   just mean 'transparent if no data'.
7442be9e038Ssthen  * @param qtype: query type
7452be9e038Ssthen  * @param action: found action
7462be9e038Ssthen  * @param rep:
7472be9e038Ssthen  * @param new_repp
7482be9e038Ssthen  * @param rrset_id
7492be9e038Ssthen  * @param region: region for building new reply
7502be9e038Ssthen  * @return 1 on success, 0 on error.
7512be9e038Ssthen  */
7522be9e038Ssthen static int
7532be9e038Ssthen respip_nodata_answer(uint16_t qtype, enum respip_action action,
7542be9e038Ssthen 	const struct reply_info *rep, size_t rrset_id,
7552be9e038Ssthen 	struct reply_info** new_repp, struct regional* region)
7562be9e038Ssthen {
7572be9e038Ssthen 	struct reply_info* new_rep;
7582be9e038Ssthen 
7592be9e038Ssthen 	if(action == respip_refuse || action == respip_always_refuse) {
7602be9e038Ssthen 		new_rep = make_new_reply_info(rep, region, 0, 0);
7612be9e038Ssthen 		if(!new_rep)
7622be9e038Ssthen 			return 0;
7632be9e038Ssthen 		FLAGS_SET_RCODE(new_rep->flags, LDNS_RCODE_REFUSED);
7642be9e038Ssthen 		*new_repp = new_rep;
7652be9e038Ssthen 		return 1;
7662be9e038Ssthen 	} else if(action == respip_static || action == respip_redirect ||
767c3b38330Ssthen 		action == respip_always_nxdomain ||
768eaf2578eSsthen 		action == respip_always_nodata ||
769c3b38330Ssthen 		action == respip_inform_redirect) {
7702be9e038Ssthen 		/* Since we don't know about other types of the owner name,
7712be9e038Ssthen 		 * we generally return NOERROR/NODATA unless an NXDOMAIN action
7722be9e038Ssthen 		 * is explicitly specified. */
7732be9e038Ssthen 		int rcode = (action == respip_always_nxdomain)?
7742be9e038Ssthen 			LDNS_RCODE_NXDOMAIN:LDNS_RCODE_NOERROR;
7752be9e038Ssthen 		/* We should empty the answer section except for any preceding
7762be9e038Ssthen 		 * CNAMEs (in that case rrset_id > 0).  Type-ANY case is
7772be9e038Ssthen 		 * special as noted in respip_data_answer(). */
7782be9e038Ssthen 		if(qtype == LDNS_RR_TYPE_ANY)
7792be9e038Ssthen 			rrset_id = 0;
7802be9e038Ssthen 		new_rep = make_new_reply_info(rep, region, rrset_id, rrset_id);
7812be9e038Ssthen 		if(!new_rep)
7822be9e038Ssthen 			return 0;
7832be9e038Ssthen 		FLAGS_SET_RCODE(new_rep->flags, rcode);
7842be9e038Ssthen 		*new_repp = new_rep;
7852be9e038Ssthen 		return 1;
7862be9e038Ssthen 	}
7872be9e038Ssthen 
7882be9e038Ssthen 	return 1;
7892be9e038Ssthen }
7902be9e038Ssthen 
7912be9e038Ssthen /** Populate action info structure with the results of response-ip action
7922be9e038Ssthen  *  processing, iff as the result of response-ip processing we are actually
7932be9e038Ssthen  *  taking some action. Only action is set if action_only is true.
7942be9e038Ssthen  *  Returns true on success, false on failure.
7952be9e038Ssthen  */
7962be9e038Ssthen static int
7972be9e038Ssthen populate_action_info(struct respip_action_info* actinfo,
7982be9e038Ssthen 	enum respip_action action, const struct resp_addr* raddr,
7992be9e038Ssthen 	const struct ub_packed_rrset_key* ATTR_UNUSED(rrset),
8002be9e038Ssthen 	int ATTR_UNUSED(tag), const struct respip_set* ATTR_UNUSED(ipset),
801eaf2578eSsthen 	int ATTR_UNUSED(action_only), struct regional* region, int rpz_used,
802eaf2578eSsthen 	int rpz_log, char* log_name, int rpz_cname_override)
8032be9e038Ssthen {
8042be9e038Ssthen 	if(action == respip_none || !raddr)
8052be9e038Ssthen 		return 1;
8062be9e038Ssthen 	actinfo->action = action;
807eaf2578eSsthen 	actinfo->rpz_used = rpz_used;
808eaf2578eSsthen 	actinfo->rpz_log = rpz_log;
809eaf2578eSsthen 	actinfo->log_name = log_name;
810eaf2578eSsthen 	actinfo->rpz_cname_override = rpz_cname_override;
8112be9e038Ssthen 
8122be9e038Ssthen 	/* for inform variants, make a copy of the matched address block for
8132be9e038Ssthen 	 * later logging.  We make a copy to proactively avoid disruption if
8142be9e038Ssthen 	 *  and when we allow a dynamic update to the respip tree. */
815eaf2578eSsthen 	if(action == respip_inform || action == respip_inform_deny ||
816eaf2578eSsthen 		rpz_used) {
8172be9e038Ssthen 		struct respip_addr_info* a =
8182be9e038Ssthen 			regional_alloc_zero(region, sizeof(*a));
8192be9e038Ssthen 		if(!a) {
8202be9e038Ssthen 			log_err("out of memory");
8212be9e038Ssthen 			return 0;
8222be9e038Ssthen 		}
8232be9e038Ssthen 		a->addr = raddr->node.addr;
8242be9e038Ssthen 		a->addrlen = raddr->node.addrlen;
8252be9e038Ssthen 		a->net = raddr->node.net;
8262be9e038Ssthen 		actinfo->addrinfo = a;
8272be9e038Ssthen 	}
8282be9e038Ssthen 
8292be9e038Ssthen 	return 1;
8302be9e038Ssthen }
8312be9e038Ssthen 
832eaf2578eSsthen static int
833eaf2578eSsthen respip_use_rpz(struct resp_addr* raddr, struct rpz* r,
834eaf2578eSsthen 	enum respip_action* action,
835eaf2578eSsthen 	struct ub_packed_rrset_key** data, int* rpz_log, char** log_name,
8360bdb4f62Ssthen 	int* rpz_cname_override, struct regional* region, int* is_rpz,
8370bdb4f62Ssthen 	int* rpz_passthru)
838eaf2578eSsthen {
8390bdb4f62Ssthen 	if(rpz_passthru && *rpz_passthru)
8400bdb4f62Ssthen 		return 0;
841eaf2578eSsthen 	if(r->action_override == RPZ_DISABLED_ACTION) {
842eaf2578eSsthen 		*is_rpz = 0;
843eaf2578eSsthen 		return 1;
844eaf2578eSsthen 	}
845eaf2578eSsthen 	else if(r->action_override == RPZ_NO_OVERRIDE_ACTION)
846eaf2578eSsthen 		*action = raddr->action;
847eaf2578eSsthen 	else
848eaf2578eSsthen 		*action = rpz_action_to_respip_action(r->action_override);
849eaf2578eSsthen 	if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION &&
850eaf2578eSsthen 		r->cname_override) {
851eaf2578eSsthen 		*data = r->cname_override;
852eaf2578eSsthen 		*rpz_cname_override = 1;
853eaf2578eSsthen 	}
8540bdb4f62Ssthen 	if(*action == respip_always_transparent /* RPZ_PASSTHRU_ACTION */
8550bdb4f62Ssthen 		&& rpz_passthru)
8560bdb4f62Ssthen 		*rpz_passthru = 1;
857eaf2578eSsthen 	*rpz_log = r->log;
858eaf2578eSsthen 	if(r->log_name)
859eaf2578eSsthen 		if(!(*log_name = regional_strdup(region, r->log_name)))
860eaf2578eSsthen 			return 0;
861eaf2578eSsthen 	*is_rpz = 1;
862eaf2578eSsthen 	return 1;
863eaf2578eSsthen }
864eaf2578eSsthen 
8652be9e038Ssthen int
8662be9e038Ssthen respip_rewrite_reply(const struct query_info* qinfo,
8672be9e038Ssthen 	const struct respip_client_info* cinfo, const struct reply_info* rep,
8682be9e038Ssthen 	struct reply_info** new_repp, struct respip_action_info* actinfo,
8692be9e038Ssthen 	struct ub_packed_rrset_key** alias_rrset, int search_only,
8700bdb4f62Ssthen 	struct regional* region, struct auth_zones* az, int* rpz_passthru)
8712be9e038Ssthen {
8722be9e038Ssthen 	const uint8_t* ctaglist;
8732be9e038Ssthen 	size_t ctaglen;
8742be9e038Ssthen 	const uint8_t* tag_actions;
8752be9e038Ssthen 	size_t tag_actions_size;
8762be9e038Ssthen 	struct config_strlist** tag_datas;
8772be9e038Ssthen 	size_t tag_datas_size;
8782be9e038Ssthen 	struct view* view = NULL;
8792be9e038Ssthen 	struct respip_set* ipset = NULL;
880e21c60efSsthen 	size_t rrset_id = 0, rr_id = 0;
8812be9e038Ssthen 	enum respip_action action = respip_none;
8822be9e038Ssthen 	int tag = -1;
883eaf2578eSsthen 	struct resp_addr* raddr = NULL;
8842be9e038Ssthen 	int ret = 1;
8852be9e038Ssthen 	struct ub_packed_rrset_key* redirect_rrset = NULL;
886eaf2578eSsthen 	struct rpz* r;
887eba819a2Ssthen 	struct auth_zone* a = NULL;
888eaf2578eSsthen 	struct ub_packed_rrset_key* data = NULL;
889eaf2578eSsthen 	int rpz_used = 0;
890eaf2578eSsthen 	int rpz_log = 0;
891eaf2578eSsthen 	int rpz_cname_override = 0;
892eaf2578eSsthen 	char* log_name = NULL;
8932be9e038Ssthen 
8942be9e038Ssthen 	if(!cinfo)
8952be9e038Ssthen 		goto done;
8962be9e038Ssthen 	ctaglist = cinfo->taglist;
8972be9e038Ssthen 	ctaglen = cinfo->taglen;
8982be9e038Ssthen 	tag_actions = cinfo->tag_actions;
8992be9e038Ssthen 	tag_actions_size = cinfo->tag_actions_size;
9002be9e038Ssthen 	tag_datas = cinfo->tag_datas;
9012be9e038Ssthen 	tag_datas_size = cinfo->tag_datas_size;
9022be9e038Ssthen 	view = cinfo->view;
9032be9e038Ssthen 	ipset = cinfo->respip_set;
9042be9e038Ssthen 
905eaf2578eSsthen 	log_assert(ipset);
906eaf2578eSsthen 
9072be9e038Ssthen 	/** Try to use response-ip config from the view first; use
9082be9e038Ssthen 	  * global response-ip config if we don't have the view or we don't
9092be9e038Ssthen 	  * have the matching per-view config (and the view allows the use
9102be9e038Ssthen 	  * of global data in this case).
9112be9e038Ssthen 	  * Note that we lock the view even if we only use view members that
9122be9e038Ssthen 	  * currently don't change after creation.  This is for safety for
9132be9e038Ssthen 	  * future possible changes as the view documentation seems to expect
9142be9e038Ssthen 	  * any of its member can change in the view's lifetime.
9152be9e038Ssthen 	  * Note also that we assume 'view' is valid in this function, which
9162be9e038Ssthen 	  * should be safe (see unbound bug #1191) */
9172be9e038Ssthen 	if(view) {
9182be9e038Ssthen 		lock_rw_rdlock(&view->lock);
9192be9e038Ssthen 		if(view->respip_set) {
9202be9e038Ssthen 			if((raddr = respip_addr_lookup(rep,
921e21c60efSsthen 				view->respip_set, &rrset_id, &rr_id))) {
9222be9e038Ssthen 				/** for per-view respip directives the action
9232be9e038Ssthen 				 * can only be direct (i.e. not tag-based) */
9242be9e038Ssthen 				action = raddr->action;
9252be9e038Ssthen 			}
9262be9e038Ssthen 		}
9272be9e038Ssthen 		if(!raddr && !view->isfirst)
9282be9e038Ssthen 			goto done;
929a3167c07Ssthen 		if(!raddr && view->isfirst) {
930a3167c07Ssthen 			lock_rw_unlock(&view->lock);
931a3167c07Ssthen 			view = NULL;
932a3167c07Ssthen 		}
9332be9e038Ssthen 	}
934eaf2578eSsthen 	if(!raddr && (raddr = respip_addr_lookup(rep, ipset,
935e21c60efSsthen 		&rrset_id, &rr_id))) {
9362be9e038Ssthen 		action = (enum respip_action)local_data_find_tag_action(
9372be9e038Ssthen 			raddr->taglist, raddr->taglen, ctaglist, ctaglen,
9382be9e038Ssthen 			tag_actions, tag_actions_size,
9392be9e038Ssthen 			(enum localzone_type)raddr->action, &tag,
9402be9e038Ssthen 			ipset->tagname, ipset->num_tags);
9412be9e038Ssthen 	}
942eaf2578eSsthen 	lock_rw_rdlock(&az->rpz_lock);
9430bdb4f62Ssthen 	for(a = az->rpz_first; a && !raddr && !(rpz_passthru && *rpz_passthru); a = a->rpz_az_next) {
944a3167c07Ssthen 		lock_rw_rdlock(&a->lock);
945a3167c07Ssthen 		r = a->rpz;
946eaf2578eSsthen 		if(!r->taglist || taglist_intersect(r->taglist,
947eaf2578eSsthen 			r->taglistlen, ctaglist, ctaglen)) {
948eaf2578eSsthen 			if((raddr = respip_addr_lookup(rep,
949e21c60efSsthen 				r->respip_set, &rrset_id, &rr_id))) {
950eaf2578eSsthen 				if(!respip_use_rpz(raddr, r, &action, &data,
951eaf2578eSsthen 					&rpz_log, &log_name, &rpz_cname_override,
9520bdb4f62Ssthen 					region, &rpz_used, rpz_passthru)) {
953eaf2578eSsthen 					log_err("out of memory");
954eaf2578eSsthen 					lock_rw_unlock(&raddr->lock);
955a3167c07Ssthen 					lock_rw_unlock(&a->lock);
956eaf2578eSsthen 					lock_rw_unlock(&az->rpz_lock);
957eaf2578eSsthen 					return 0;
958eaf2578eSsthen 				}
959a3167c07Ssthen 				if(rpz_used) {
960e21c60efSsthen 					if(verbosity >= VERB_ALGO) {
961e21c60efSsthen 						struct sockaddr_storage ss;
962e21c60efSsthen 						socklen_t ss_len = 0;
963e21c60efSsthen 						char nm[256], ip[256];
964e21c60efSsthen 						char qn[255+1];
965e21c60efSsthen 						if(!rdata2sockaddr(rep->rrsets[rrset_id]->entry.data, ntohs(rep->rrsets[rrset_id]->rk.type), rr_id, &ss, &ss_len))
966e21c60efSsthen 							snprintf(ip, sizeof(ip), "invalidRRdata");
967e21c60efSsthen 						else
968e21c60efSsthen 							addr_to_str(&ss, ss_len, ip, sizeof(ip));
969e21c60efSsthen 						dname_str(qinfo->qname, qn);
970e21c60efSsthen 						addr_to_str(&raddr->node.addr,
971e21c60efSsthen 							raddr->node.addrlen,
972e21c60efSsthen 							nm, sizeof(nm));
9730bdb4f62Ssthen 						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)));
974e21c60efSsthen 					}
975a3167c07Ssthen 					/* break to make sure 'a' stays pointed
976a3167c07Ssthen 					 * to used auth_zone, and keeps lock */
977a3167c07Ssthen 					break;
978a3167c07Ssthen 				}
979eaf2578eSsthen 				lock_rw_unlock(&raddr->lock);
980eaf2578eSsthen 				raddr = NULL;
981eaf2578eSsthen 				actinfo->rpz_disabled++;
982eaf2578eSsthen 			}
983eaf2578eSsthen 		}
984a3167c07Ssthen 		lock_rw_unlock(&a->lock);
985eaf2578eSsthen 	}
986eaf2578eSsthen 	lock_rw_unlock(&az->rpz_lock);
9872be9e038Ssthen 	if(raddr && !search_only) {
9882be9e038Ssthen 		int result = 0;
9892be9e038Ssthen 
9902be9e038Ssthen 		/* first, see if we have response-ip or tag action for the
9912be9e038Ssthen 		 * action except for 'always' variants. */
9922be9e038Ssthen 		if(action != respip_always_refuse
9932be9e038Ssthen 			&& action != respip_always_transparent
9942be9e038Ssthen 			&& action != respip_always_nxdomain
995eaf2578eSsthen 			&& action != respip_always_nodata
996eaf2578eSsthen 			&& action != respip_always_deny
997eaf2578eSsthen 			&& (result = respip_data_answer(action,
998eaf2578eSsthen 			(data) ? data : raddr->data, qinfo->qtype, rep,
999eaf2578eSsthen 			rrset_id, new_repp, tag, tag_datas, tag_datas_size,
1000eaf2578eSsthen 			ipset->tagname, ipset->num_tags, &redirect_rrset,
1001eaf2578eSsthen 			region)) < 0) {
10022be9e038Ssthen 			ret = 0;
10032be9e038Ssthen 			goto done;
10042be9e038Ssthen 		}
10052be9e038Ssthen 
10062be9e038Ssthen 		/* if no action data applied, take action specific to the
10072be9e038Ssthen 		 * action without data. */
10082be9e038Ssthen 		if(!result && !respip_nodata_answer(qinfo->qtype, action, rep,
10092be9e038Ssthen 			rrset_id, new_repp, region)) {
10102be9e038Ssthen 			ret = 0;
10112be9e038Ssthen 			goto done;
10122be9e038Ssthen 		}
10132be9e038Ssthen 	}
10142be9e038Ssthen   done:
10152be9e038Ssthen 	if(view) {
10162be9e038Ssthen 		lock_rw_unlock(&view->lock);
10172be9e038Ssthen 	}
10182be9e038Ssthen 	if(ret) {
10192be9e038Ssthen 		/* If we're redirecting the original answer to a
10202be9e038Ssthen 		 * CNAME, record the CNAME rrset so the caller can take
10212be9e038Ssthen 		 * the appropriate action.  Note that we don't check the
10222be9e038Ssthen 		 * action type; it should normally be 'redirect', but it
10232be9e038Ssthen 		 * can be of other type when a data-dependent tag action
10242be9e038Ssthen 		 * uses redirect response-ip data.
10252be9e038Ssthen 		 */
10262be9e038Ssthen 		if(redirect_rrset &&
10272be9e038Ssthen 			redirect_rrset->rk.type == ntohs(LDNS_RR_TYPE_CNAME) &&
10282be9e038Ssthen 			qinfo->qtype != LDNS_RR_TYPE_ANY)
10292be9e038Ssthen 			*alias_rrset = redirect_rrset;
10302be9e038Ssthen 		/* on success, populate respip result structure */
10312be9e038Ssthen 		ret = populate_action_info(actinfo, action, raddr,
1032eaf2578eSsthen 			redirect_rrset, tag, ipset, search_only, region,
1033eaf2578eSsthen 				rpz_used, rpz_log, log_name, rpz_cname_override);
1034eaf2578eSsthen 	}
1035eaf2578eSsthen 	if(raddr) {
1036eaf2578eSsthen 		lock_rw_unlock(&raddr->lock);
10372be9e038Ssthen 	}
1038a3167c07Ssthen 	if(rpz_used) {
1039a3167c07Ssthen 		lock_rw_unlock(&a->lock);
1040a3167c07Ssthen 	}
10412be9e038Ssthen 	return ret;
10422be9e038Ssthen }
10432be9e038Ssthen 
10442be9e038Ssthen static int
10452be9e038Ssthen generate_cname_request(struct module_qstate* qstate,
10462be9e038Ssthen 	struct ub_packed_rrset_key* alias_rrset)
10472be9e038Ssthen {
10482be9e038Ssthen 	struct module_qstate* subq = NULL;
10492be9e038Ssthen 	struct query_info subqi;
10502be9e038Ssthen 
10512be9e038Ssthen 	memset(&subqi, 0, sizeof(subqi));
10522be9e038Ssthen 	get_cname_target(alias_rrset, &subqi.qname, &subqi.qname_len);
10532be9e038Ssthen 	if(!subqi.qname)
10542be9e038Ssthen 		return 0;    /* unexpected: not a valid CNAME RDATA */
10552be9e038Ssthen 	subqi.qtype = qstate->qinfo.qtype;
10562be9e038Ssthen 	subqi.qclass = qstate->qinfo.qclass;
10572be9e038Ssthen 	fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
10582be9e038Ssthen 	return (*qstate->env->attach_sub)(qstate, &subqi, BIT_RD, 0, 0, &subq);
10592be9e038Ssthen }
10602be9e038Ssthen 
10612be9e038Ssthen void
10622be9e038Ssthen respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
10632be9e038Ssthen 	struct outbound_entry* outbound)
10642be9e038Ssthen {
10652be9e038Ssthen 	struct respip_qstate* rq = (struct respip_qstate*)qstate->minfo[id];
10662be9e038Ssthen 
10672be9e038Ssthen 	log_query_info(VERB_QUERY, "respip operate: query", &qstate->qinfo);
10682be9e038Ssthen 	(void)outbound;
10692be9e038Ssthen 
10702be9e038Ssthen 	if(event == module_event_new || event == module_event_pass) {
10712be9e038Ssthen 		if(!rq) {
10722be9e038Ssthen 			rq = regional_alloc_zero(qstate->region, sizeof(*rq));
10732be9e038Ssthen 			if(!rq)
10742be9e038Ssthen 				goto servfail;
10752be9e038Ssthen 			rq->state = RESPIP_INIT;
10762be9e038Ssthen 			qstate->minfo[id] = rq;
10772be9e038Ssthen 		}
10782be9e038Ssthen 		if(rq->state == RESPIP_SUBQUERY_FINISHED) {
10792be9e038Ssthen 			qstate->ext_state[id] = module_finished;
10802be9e038Ssthen 			return;
10812be9e038Ssthen 		}
10822be9e038Ssthen 		verbose(VERB_ALGO, "respip: pass to next module");
10832be9e038Ssthen 		qstate->ext_state[id] = module_wait_module;
10842be9e038Ssthen 	} else if(event == module_event_moddone) {
10852be9e038Ssthen 		/* If the reply may be subject to response-ip rewriting
10862be9e038Ssthen 		 * according to the query type, check the actions.  If a
10872be9e038Ssthen 		 * rewrite is necessary, we'll replace the reply in qstate
10882be9e038Ssthen 		 * with the new one. */
10892be9e038Ssthen 		enum module_ext_state next_state = module_finished;
10902be9e038Ssthen 
10912be9e038Ssthen 		if((qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
10922be9e038Ssthen 			qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA ||
10932be9e038Ssthen 			qstate->qinfo.qtype == LDNS_RR_TYPE_ANY) &&
10942be9e038Ssthen 			qstate->return_msg && qstate->return_msg->rep) {
10952be9e038Ssthen 			struct reply_info* new_rep = qstate->return_msg->rep;
10962be9e038Ssthen 			struct ub_packed_rrset_key* alias_rrset = NULL;
1097eba819a2Ssthen 			struct respip_action_info actinfo = {0, 0, 0, 0, NULL, 0, NULL};
1098eaf2578eSsthen 			actinfo.action = respip_none;
10992be9e038Ssthen 
11002be9e038Ssthen 			if(!respip_rewrite_reply(&qstate->qinfo,
11012be9e038Ssthen 				qstate->client_info, qstate->return_msg->rep,
11022be9e038Ssthen 				&new_rep, &actinfo, &alias_rrset, 0,
11030bdb4f62Ssthen 				qstate->region, qstate->env->auth_zones,
11040bdb4f62Ssthen 				&qstate->rpz_passthru)) {
11052be9e038Ssthen 				goto servfail;
11062be9e038Ssthen 			}
11072be9e038Ssthen 			if(actinfo.action != respip_none) {
11082be9e038Ssthen 				/* save action info for logging on a
11092be9e038Ssthen 				 * per-front-end-query basis */
11102be9e038Ssthen 				if(!(qstate->respip_action_info =
11112be9e038Ssthen 					regional_alloc_init(qstate->region,
11122be9e038Ssthen 						&actinfo, sizeof(actinfo))))
11132be9e038Ssthen 				{
11142be9e038Ssthen 					log_err("out of memory");
11152be9e038Ssthen 					goto servfail;
11162be9e038Ssthen 				}
11172be9e038Ssthen 			} else {
11182be9e038Ssthen 				qstate->respip_action_info = NULL;
11192be9e038Ssthen 			}
1120eaf2578eSsthen 			if (actinfo.action == respip_always_deny ||
1121eaf2578eSsthen 				(new_rep == qstate->return_msg->rep &&
11222be9e038Ssthen 				(actinfo.action == respip_deny ||
1123eaf2578eSsthen 				actinfo.action == respip_inform_deny))) {
11242be9e038Ssthen 				/* for deny-variant actions (unless response-ip
11252be9e038Ssthen 				 * data is applied), mark the query state so
11262be9e038Ssthen 				 * the response will be dropped for all
11272be9e038Ssthen 				 * clients. */
11282be9e038Ssthen 				qstate->is_drop = 1;
11292be9e038Ssthen 			} else if(alias_rrset) {
11302be9e038Ssthen 				if(!generate_cname_request(qstate, alias_rrset))
11312be9e038Ssthen 					goto servfail;
11322be9e038Ssthen 				next_state = module_wait_subquery;
11332be9e038Ssthen 			}
11342be9e038Ssthen 			qstate->return_msg->rep = new_rep;
11352be9e038Ssthen 		}
11362be9e038Ssthen 		qstate->ext_state[id] = next_state;
11372be9e038Ssthen 	} else
11382be9e038Ssthen 		qstate->ext_state[id] = module_finished;
11392be9e038Ssthen 
11402be9e038Ssthen 	return;
11412be9e038Ssthen 
11422be9e038Ssthen   servfail:
11432be9e038Ssthen 	qstate->return_rcode = LDNS_RCODE_SERVFAIL;
11442be9e038Ssthen 	qstate->return_msg = NULL;
11452be9e038Ssthen }
11462be9e038Ssthen 
11472be9e038Ssthen int
11482be9e038Ssthen respip_merge_cname(struct reply_info* base_rep,
11492be9e038Ssthen 	const struct query_info* qinfo, const struct reply_info* tgt_rep,
11502be9e038Ssthen 	const struct respip_client_info* cinfo, int must_validate,
1151eaf2578eSsthen 	struct reply_info** new_repp, struct regional* region,
1152eaf2578eSsthen 	struct auth_zones* az)
11532be9e038Ssthen {
11542be9e038Ssthen 	struct reply_info* new_rep;
11552be9e038Ssthen 	struct reply_info* tmp_rep = NULL; /* just a placeholder */
11562be9e038Ssthen 	struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */
11572be9e038Ssthen 	uint16_t tgt_rcode;
11582be9e038Ssthen 	size_t i, j;
1159eba819a2Ssthen 	struct respip_action_info actinfo = {0, 0, 0, 0, NULL, 0, NULL};
1160eaf2578eSsthen 	actinfo.action = respip_none;
11612be9e038Ssthen 
11622be9e038Ssthen 	/* If the query for the CNAME target would result in an unusual rcode,
11632be9e038Ssthen 	 * we generally translate it as a failure for the base query
11642be9e038Ssthen 	 * (which would then be translated into SERVFAIL).  The only exception
11652be9e038Ssthen 	 * is NXDOMAIN and YXDOMAIN, which are passed to the end client(s).
11662be9e038Ssthen 	 * The YXDOMAIN case would be rare but still possible (when
11672be9e038Ssthen 	 * DNSSEC-validated DNAME has been cached but synthesizing CNAME
11682be9e038Ssthen 	 * can't be generated due to length limitation) */
11692be9e038Ssthen 	tgt_rcode = FLAGS_GET_RCODE(tgt_rep->flags);
11702be9e038Ssthen 	if((tgt_rcode != LDNS_RCODE_NOERROR &&
11712be9e038Ssthen 		tgt_rcode != LDNS_RCODE_NXDOMAIN &&
11722be9e038Ssthen 		tgt_rcode != LDNS_RCODE_YXDOMAIN) ||
11732be9e038Ssthen 		(must_validate && tgt_rep->security <= sec_status_bogus)) {
11742be9e038Ssthen 		return 0;
11752be9e038Ssthen 	}
11762be9e038Ssthen 
11772be9e038Ssthen 	/* see if the target reply would be subject to a response-ip action. */
11782be9e038Ssthen 	if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo,
11790bdb4f62Ssthen 		&alias_rrset, 1, region, az, NULL))
11802be9e038Ssthen 		return 0;
11812be9e038Ssthen 	if(actinfo.action != respip_none) {
11822be9e038Ssthen 		log_info("CNAME target of redirect response-ip action would "
11832be9e038Ssthen 			"be subject to response-ip action, too; stripped");
11842be9e038Ssthen 		*new_repp = base_rep;
11852be9e038Ssthen 		return 1;
11862be9e038Ssthen 	}
11872be9e038Ssthen 
11882be9e038Ssthen 	/* Append target reply to the base.  Since we cannot assume
11892be9e038Ssthen 	 * tgt_rep->rrsets is valid throughout the lifetime of new_rep
11902be9e038Ssthen 	 * or it can be safely shared by multiple threads, we need to make a
11912be9e038Ssthen 	 * deep copy. */
11922be9e038Ssthen 	new_rep = make_new_reply_info(base_rep, region,
11932be9e038Ssthen 		base_rep->an_numrrsets + tgt_rep->an_numrrsets,
11942be9e038Ssthen 		base_rep->an_numrrsets);
11952be9e038Ssthen 	if(!new_rep)
11962be9e038Ssthen 		return 0;
11972be9e038Ssthen 	for(i=0,j=base_rep->an_numrrsets; i<tgt_rep->an_numrrsets; i++,j++) {
1198e21c60efSsthen 		new_rep->rrsets[j] = respip_copy_rrset(tgt_rep->rrsets[i], region);
11992be9e038Ssthen 		if(!new_rep->rrsets[j])
12002be9e038Ssthen 			return 0;
12012be9e038Ssthen 	}
12022be9e038Ssthen 
12032be9e038Ssthen 	FLAGS_SET_RCODE(new_rep->flags, tgt_rcode);
12042be9e038Ssthen 	*new_repp = new_rep;
12052be9e038Ssthen 	return 1;
12062be9e038Ssthen }
12072be9e038Ssthen 
12082be9e038Ssthen void
12092be9e038Ssthen respip_inform_super(struct module_qstate* qstate, int id,
12102be9e038Ssthen 	struct module_qstate* super)
12112be9e038Ssthen {
12122be9e038Ssthen 	struct respip_qstate* rq = (struct respip_qstate*)super->minfo[id];
12132be9e038Ssthen 	struct reply_info* new_rep = NULL;
12142be9e038Ssthen 
12152be9e038Ssthen 	rq->state = RESPIP_SUBQUERY_FINISHED;
12162be9e038Ssthen 
12172be9e038Ssthen 	/* respip subquery should have always been created with a valid reply
12182be9e038Ssthen 	 * in super. */
12192be9e038Ssthen 	log_assert(super->return_msg && super->return_msg->rep);
12202be9e038Ssthen 
12212be9e038Ssthen 	/* return_msg can be NULL when, e.g., the sub query resulted in
12222be9e038Ssthen 	 * SERVFAIL, in which case we regard it as a failure of the original
12232be9e038Ssthen 	 * query.  Other checks are probably redundant, but we check them
12242be9e038Ssthen 	 * for safety. */
12252be9e038Ssthen 	if(!qstate->return_msg || !qstate->return_msg->rep ||
12262be9e038Ssthen 		qstate->return_rcode != LDNS_RCODE_NOERROR)
12272be9e038Ssthen 		goto fail;
12282be9e038Ssthen 
12292be9e038Ssthen 	if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo,
12302be9e038Ssthen 		qstate->return_msg->rep, super->client_info,
1231eaf2578eSsthen 		super->env->need_to_validate, &new_rep, super->region,
1232eaf2578eSsthen 		qstate->env->auth_zones))
12332be9e038Ssthen 		goto fail;
12342be9e038Ssthen 	super->return_msg->rep = new_rep;
12352be9e038Ssthen 	return;
12362be9e038Ssthen 
12372be9e038Ssthen   fail:
12382be9e038Ssthen 	super->return_rcode = LDNS_RCODE_SERVFAIL;
12392be9e038Ssthen 	super->return_msg = NULL;
12402be9e038Ssthen 	return;
12412be9e038Ssthen }
12422be9e038Ssthen 
12432be9e038Ssthen void
12442be9e038Ssthen respip_clear(struct module_qstate* qstate, int id)
12452be9e038Ssthen {
12462be9e038Ssthen 	qstate->minfo[id] = NULL;
12472be9e038Ssthen }
12482be9e038Ssthen 
12492be9e038Ssthen size_t
12502be9e038Ssthen respip_get_mem(struct module_env* env, int id)
12512be9e038Ssthen {
12522be9e038Ssthen 	(void)env;
12532be9e038Ssthen 	(void)id;
12542be9e038Ssthen 	return 0;
12552be9e038Ssthen }
12562be9e038Ssthen 
12572be9e038Ssthen /**
12582be9e038Ssthen  * The response-ip function block
12592be9e038Ssthen  */
12602be9e038Ssthen static struct module_func_block respip_block = {
12612be9e038Ssthen 	"respip",
1262*98bc733bSsthen 	NULL, NULL, &respip_init, &respip_deinit, &respip_operate,
1263*98bc733bSsthen 	&respip_inform_super, &respip_clear, &respip_get_mem
12642be9e038Ssthen };
12652be9e038Ssthen 
12662be9e038Ssthen struct module_func_block*
12672be9e038Ssthen respip_get_funcblock(void)
12682be9e038Ssthen {
12692be9e038Ssthen 	return &respip_block;
12702be9e038Ssthen }
12712be9e038Ssthen 
12722be9e038Ssthen enum respip_action
12732be9e038Ssthen resp_addr_get_action(const struct resp_addr* addr)
12742be9e038Ssthen {
12752be9e038Ssthen 	return addr ? addr->action : respip_none;
12762be9e038Ssthen }
12772be9e038Ssthen 
12782be9e038Ssthen struct ub_packed_rrset_key*
12792be9e038Ssthen resp_addr_get_rrset(struct resp_addr* addr)
12802be9e038Ssthen {
12812be9e038Ssthen 	return addr ? addr->data : NULL;
12822be9e038Ssthen }
12832be9e038Ssthen 
12842be9e038Ssthen int
12852be9e038Ssthen respip_set_is_empty(const struct respip_set* set)
12862be9e038Ssthen {
12872be9e038Ssthen 	return set ? set->ip_tree.count == 0 : 1;
12882be9e038Ssthen }
12892be9e038Ssthen 
12902be9e038Ssthen void
1291eaf2578eSsthen respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname,
12922be9e038Ssthen 	uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias,
129345872187Ssthen 	struct sockaddr_storage* addr, socklen_t addrlen)
12942be9e038Ssthen {
12952be9e038Ssthen 	char srcip[128], respip[128], txt[512];
12962be9e038Ssthen 	unsigned port;
1297eaf2578eSsthen 	struct respip_addr_info* respip_addr = respip_actinfo->addrinfo;
1298eaf2578eSsthen 	size_t txtlen = 0;
1299eaf2578eSsthen 	const char* actionstr = NULL;
13002be9e038Ssthen 
13012be9e038Ssthen 	if(local_alias)
13022be9e038Ssthen 		qname = local_alias->rrset->rk.dname;
130345872187Ssthen 	port = (unsigned)((addr->ss_family == AF_INET) ?
130445872187Ssthen 		ntohs(((struct sockaddr_in*)addr)->sin_port) :
130545872187Ssthen 		ntohs(((struct sockaddr_in6*)addr)->sin6_port));
130645872187Ssthen 	addr_to_str(addr, addrlen, srcip, sizeof(srcip));
13072be9e038Ssthen 	addr_to_str(&respip_addr->addr, respip_addr->addrlen,
13082be9e038Ssthen 		respip, sizeof(respip));
1309eaf2578eSsthen 	if(respip_actinfo->rpz_log) {
1310eaf2578eSsthen 		txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, "%s",
13110bdb4f62Ssthen 			"rpz: applied ");
1312eaf2578eSsthen 		if(respip_actinfo->rpz_cname_override)
1313eaf2578eSsthen 			actionstr = rpz_action_to_string(
1314eaf2578eSsthen 				RPZ_CNAME_OVERRIDE_ACTION);
1315eaf2578eSsthen 		else
1316eaf2578eSsthen 			actionstr = rpz_action_to_string(
1317eaf2578eSsthen 				respip_action_to_rpz_action(
1318eaf2578eSsthen 					respip_actinfo->action));
1319eaf2578eSsthen 	}
1320eaf2578eSsthen 	if(respip_actinfo->log_name) {
1321eaf2578eSsthen 		txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen,
1322eaf2578eSsthen 			"[%s] ", respip_actinfo->log_name);
1323eaf2578eSsthen 	}
1324eaf2578eSsthen 	snprintf(txt+txtlen, sizeof(txt)-txtlen,
1325eaf2578eSsthen 		"%s/%d %s %s@%u", respip, respip_addr->net,
1326eaf2578eSsthen 		(actionstr) ? actionstr : "inform", srcip, port);
1327ebf5bb73Ssthen 	log_nametypeclass(NO_VERBOSE, txt, qname, qtype, qclass);
13282be9e038Ssthen }
1329