xref: /freebsd/contrib/unbound/services/rpz.c (revision 46d2f61818f594174cafe31ee338c6e083fa1876)
1091e9e46SCy Schubert /*
2091e9e46SCy Schubert  * services/rpz.c - rpz service
3091e9e46SCy Schubert  *
4091e9e46SCy Schubert  * Copyright (c) 2019, NLnet Labs. All rights reserved.
5091e9e46SCy Schubert  *
6091e9e46SCy Schubert  * This software is open source.
7091e9e46SCy Schubert  *
8091e9e46SCy Schubert  * Redistribution and use in source and binary forms, with or without
9091e9e46SCy Schubert  * modification, are permitted provided that the following conditions
10091e9e46SCy Schubert  * are met:
11091e9e46SCy Schubert  *
12091e9e46SCy Schubert  * Redistributions of source code must retain the above copyright notice,
13091e9e46SCy Schubert  * this list of conditions and the following disclaimer.
14091e9e46SCy Schubert  *
15091e9e46SCy Schubert  * Redistributions in binary form must reproduce the above copyright notice,
16091e9e46SCy Schubert  * this list of conditions and the following disclaimer in the documentation
17091e9e46SCy Schubert  * and/or other materials provided with the distribution.
18091e9e46SCy Schubert  *
19091e9e46SCy Schubert  * Neither the name of the NLNET LABS nor the names of its contributors may
20091e9e46SCy Schubert  * be used to endorse or promote products derived from this software without
21091e9e46SCy Schubert  * specific prior written permission.
22091e9e46SCy Schubert  *
23091e9e46SCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24091e9e46SCy Schubert  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25091e9e46SCy Schubert  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26091e9e46SCy Schubert  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27091e9e46SCy Schubert  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28091e9e46SCy Schubert  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29091e9e46SCy Schubert  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30091e9e46SCy Schubert  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31091e9e46SCy Schubert  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32091e9e46SCy Schubert  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33091e9e46SCy Schubert  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34091e9e46SCy Schubert  */
35091e9e46SCy Schubert 
36091e9e46SCy Schubert /**
37091e9e46SCy Schubert  * \file
38091e9e46SCy Schubert  *
39091e9e46SCy Schubert  * This file contains functions to enable RPZ service.
40091e9e46SCy Schubert  */
41091e9e46SCy Schubert 
42091e9e46SCy Schubert #include "config.h"
43091e9e46SCy Schubert #include "services/rpz.h"
44091e9e46SCy Schubert #include "util/config_file.h"
45091e9e46SCy Schubert #include "sldns/wire2str.h"
46091e9e46SCy Schubert #include "sldns/str2wire.h"
47091e9e46SCy Schubert #include "util/data/dname.h"
48091e9e46SCy Schubert #include "util/net_help.h"
49091e9e46SCy Schubert #include "util/log.h"
50091e9e46SCy Schubert #include "util/data/dname.h"
51091e9e46SCy Schubert #include "util/locks.h"
52091e9e46SCy Schubert #include "util/regional.h"
5324e36522SCy Schubert #include "util/data/msgencode.h"
5424e36522SCy Schubert #include "services/cache/dns.h"
5524e36522SCy Schubert #include "iterator/iterator.h"
5624e36522SCy Schubert #include "iterator/iter_delegpt.h"
5724e36522SCy Schubert #include "daemon/worker.h"
5824e36522SCy Schubert 
5924e36522SCy Schubert typedef struct resp_addr rpz_aclnode_type;
6024e36522SCy Schubert 
6124e36522SCy Schubert struct matched_delegation_point {
6224e36522SCy Schubert 	uint8_t* dname;
6324e36522SCy Schubert 	size_t dname_len;
6424e36522SCy Schubert };
65091e9e46SCy Schubert 
66091e9e46SCy Schubert /** string for RPZ action enum */
67091e9e46SCy Schubert const char*
rpz_action_to_string(enum rpz_action a)68091e9e46SCy Schubert rpz_action_to_string(enum rpz_action a)
69091e9e46SCy Schubert {
70091e9e46SCy Schubert 	switch(a) {
7124e36522SCy Schubert 	case RPZ_NXDOMAIN_ACTION: return "rpz-nxdomain";
7224e36522SCy Schubert 	case RPZ_NODATA_ACTION: return "rpz-nodata";
7324e36522SCy Schubert 	case RPZ_PASSTHRU_ACTION: return "rpz-passthru";
7424e36522SCy Schubert 	case RPZ_DROP_ACTION: return "rpz-drop";
7524e36522SCy Schubert 	case RPZ_TCP_ONLY_ACTION: return "rpz-tcp-only";
7624e36522SCy Schubert 	case RPZ_INVALID_ACTION: return "rpz-invalid";
7724e36522SCy Schubert 	case RPZ_LOCAL_DATA_ACTION: return "rpz-local-data";
7824e36522SCy Schubert 	case RPZ_DISABLED_ACTION: return "rpz-disabled";
7924e36522SCy Schubert 	case RPZ_CNAME_OVERRIDE_ACTION: return "rpz-cname-override";
8024e36522SCy Schubert 	case RPZ_NO_OVERRIDE_ACTION: return "rpz-no-override";
8124e36522SCy Schubert 	default: return "rpz-unknown-action";
82091e9e46SCy Schubert 	}
83091e9e46SCy Schubert }
84091e9e46SCy Schubert 
85091e9e46SCy Schubert /** RPZ action enum for config string */
86091e9e46SCy Schubert static enum rpz_action
rpz_config_to_action(char * a)87091e9e46SCy Schubert rpz_config_to_action(char* a)
88091e9e46SCy Schubert {
8924e36522SCy Schubert 	if(strcmp(a, "nxdomain") == 0) return RPZ_NXDOMAIN_ACTION;
9024e36522SCy Schubert 	else if(strcmp(a, "nodata") == 0) return RPZ_NODATA_ACTION;
9124e36522SCy Schubert 	else if(strcmp(a, "passthru") == 0) return RPZ_PASSTHRU_ACTION;
9224e36522SCy Schubert 	else if(strcmp(a, "drop") == 0) return RPZ_DROP_ACTION;
9324e36522SCy Schubert 	else if(strcmp(a, "tcp_only") == 0) return RPZ_TCP_ONLY_ACTION;
9424e36522SCy Schubert 	else if(strcmp(a, "cname") == 0) return RPZ_CNAME_OVERRIDE_ACTION;
9524e36522SCy Schubert 	else if(strcmp(a, "disabled") == 0) return RPZ_DISABLED_ACTION;
9624e36522SCy Schubert 	else return RPZ_INVALID_ACTION;
97091e9e46SCy Schubert }
98091e9e46SCy Schubert 
99091e9e46SCy Schubert /** string for RPZ trigger enum */
100091e9e46SCy Schubert static const char*
rpz_trigger_to_string(enum rpz_trigger r)101091e9e46SCy Schubert rpz_trigger_to_string(enum rpz_trigger r)
102091e9e46SCy Schubert {
103091e9e46SCy Schubert 	switch(r) {
10424e36522SCy Schubert 	case RPZ_QNAME_TRIGGER: return "rpz-qname";
10524e36522SCy Schubert 	case RPZ_CLIENT_IP_TRIGGER: return "rpz-client-ip";
10624e36522SCy Schubert 	case RPZ_RESPONSE_IP_TRIGGER: return "rpz-response-ip";
10724e36522SCy Schubert 	case RPZ_NSDNAME_TRIGGER: return "rpz-nsdname";
10824e36522SCy Schubert 	case RPZ_NSIP_TRIGGER: return "rpz-nsip";
10924e36522SCy Schubert 	case RPZ_INVALID_TRIGGER: return "rpz-invalid";
11024e36522SCy Schubert 	default: return "rpz-unknown-trigger";
111091e9e46SCy Schubert 	}
112091e9e46SCy Schubert }
113091e9e46SCy Schubert 
114091e9e46SCy Schubert /**
115091e9e46SCy Schubert  * Get the label that is just before the root label.
116091e9e46SCy Schubert  * @param dname: dname to work on
117091e9e46SCy Schubert  * @param maxdnamelen: maximum length of the dname
118091e9e46SCy Schubert  * @return: pointer to TLD label, NULL if not found or invalid dname
119091e9e46SCy Schubert  */
120091e9e46SCy Schubert static uint8_t*
get_tld_label(uint8_t * dname,size_t maxdnamelen)121091e9e46SCy Schubert get_tld_label(uint8_t* dname, size_t maxdnamelen)
122091e9e46SCy Schubert {
123091e9e46SCy Schubert 	uint8_t* prevlab = dname;
124091e9e46SCy Schubert 	size_t dnamelen = 0;
125091e9e46SCy Schubert 
126091e9e46SCy Schubert 	/* one byte needed for label length */
127091e9e46SCy Schubert 	if(dnamelen+1 > maxdnamelen)
128091e9e46SCy Schubert 		return NULL;
129091e9e46SCy Schubert 
130091e9e46SCy Schubert 	/* only root label */
131091e9e46SCy Schubert 	if(*dname == 0)
132091e9e46SCy Schubert 		return NULL;
133091e9e46SCy Schubert 
134091e9e46SCy Schubert 	while(*dname) {
135091e9e46SCy Schubert 		dnamelen += ((size_t)*dname)+1;
136091e9e46SCy Schubert 		if(dnamelen+1 > maxdnamelen)
137091e9e46SCy Schubert 			return NULL;
138091e9e46SCy Schubert 		dname = dname+((size_t)*dname)+1;
139091e9e46SCy Schubert 		if(*dname != 0)
140091e9e46SCy Schubert 			prevlab = dname;
141091e9e46SCy Schubert 	}
142091e9e46SCy Schubert 	return prevlab;
143091e9e46SCy Schubert }
144091e9e46SCy Schubert 
145091e9e46SCy Schubert /**
14624e36522SCy Schubert  * The RR types that are to be ignored.
14724e36522SCy Schubert  * DNSSEC RRs at the apex, and SOA and NS are ignored.
14824e36522SCy Schubert  */
14924e36522SCy Schubert static int
rpz_type_ignored(uint16_t rr_type)15024e36522SCy Schubert rpz_type_ignored(uint16_t rr_type)
15124e36522SCy Schubert {
15224e36522SCy Schubert 	switch(rr_type) {
15324e36522SCy Schubert 		case LDNS_RR_TYPE_SOA:
15424e36522SCy Schubert 		case LDNS_RR_TYPE_NS:
15524e36522SCy Schubert 		case LDNS_RR_TYPE_DNAME:
15624e36522SCy Schubert 		/* all DNSSEC-related RRs must be ignored */
15724e36522SCy Schubert 		case LDNS_RR_TYPE_DNSKEY:
15824e36522SCy Schubert 		case LDNS_RR_TYPE_DS:
15924e36522SCy Schubert 		case LDNS_RR_TYPE_RRSIG:
16024e36522SCy Schubert 		case LDNS_RR_TYPE_NSEC:
16124e36522SCy Schubert 		case LDNS_RR_TYPE_NSEC3:
16224e36522SCy Schubert 		case LDNS_RR_TYPE_NSEC3PARAM:
16324e36522SCy Schubert 			return 1;
16424e36522SCy Schubert 		default:
16524e36522SCy Schubert 			break;
16624e36522SCy Schubert 	}
16724e36522SCy Schubert 	return 0;
16824e36522SCy Schubert }
16924e36522SCy Schubert 
17024e36522SCy Schubert /**
171091e9e46SCy Schubert  * Classify RPZ action for RR type/rdata
172091e9e46SCy Schubert  * @param rr_type: the RR type
173091e9e46SCy Schubert  * @param rdatawl: RDATA with 2 bytes length
174091e9e46SCy Schubert  * @param rdatalen: the length of rdatawl (including its 2 bytes length)
175091e9e46SCy Schubert  * @return: the RPZ action
176091e9e46SCy Schubert  */
177091e9e46SCy Schubert static enum rpz_action
rpz_rr_to_action(uint16_t rr_type,uint8_t * rdatawl,size_t rdatalen)178091e9e46SCy Schubert rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
179091e9e46SCy Schubert {
180091e9e46SCy Schubert 	char* endptr;
181091e9e46SCy Schubert 	uint8_t* rdata;
182091e9e46SCy Schubert 	int rdatalabs;
183091e9e46SCy Schubert 	uint8_t* tldlab = NULL;
184091e9e46SCy Schubert 
185091e9e46SCy Schubert 	switch(rr_type) {
186091e9e46SCy Schubert 		case LDNS_RR_TYPE_SOA:
187091e9e46SCy Schubert 		case LDNS_RR_TYPE_NS:
188091e9e46SCy Schubert 		case LDNS_RR_TYPE_DNAME:
189091e9e46SCy Schubert 		/* all DNSSEC-related RRs must be ignored */
190091e9e46SCy Schubert 		case LDNS_RR_TYPE_DNSKEY:
191091e9e46SCy Schubert 		case LDNS_RR_TYPE_DS:
192091e9e46SCy Schubert 		case LDNS_RR_TYPE_RRSIG:
193091e9e46SCy Schubert 		case LDNS_RR_TYPE_NSEC:
194091e9e46SCy Schubert 		case LDNS_RR_TYPE_NSEC3:
1955469a995SCy Schubert 		case LDNS_RR_TYPE_NSEC3PARAM:
196091e9e46SCy Schubert 			return RPZ_INVALID_ACTION;
197091e9e46SCy Schubert 		case LDNS_RR_TYPE_CNAME:
198091e9e46SCy Schubert 			break;
199091e9e46SCy Schubert 		default:
200091e9e46SCy Schubert 			return RPZ_LOCAL_DATA_ACTION;
201091e9e46SCy Schubert 	}
202091e9e46SCy Schubert 
203091e9e46SCy Schubert 	/* use CNAME target to determine RPZ action */
204091e9e46SCy Schubert 	log_assert(rr_type == LDNS_RR_TYPE_CNAME);
205091e9e46SCy Schubert 	if(rdatalen < 3)
206091e9e46SCy Schubert 		return RPZ_INVALID_ACTION;
207091e9e46SCy Schubert 
208091e9e46SCy Schubert 	rdata = rdatawl + 2; /* 2 bytes of rdata length */
209091e9e46SCy Schubert 	if(dname_valid(rdata, rdatalen-2) != rdatalen-2)
210091e9e46SCy Schubert 		return RPZ_INVALID_ACTION;
211091e9e46SCy Schubert 
212091e9e46SCy Schubert 	rdatalabs = dname_count_labels(rdata);
213091e9e46SCy Schubert 	if(rdatalabs == 1)
214091e9e46SCy Schubert 		return RPZ_NXDOMAIN_ACTION;
215091e9e46SCy Schubert 	else if(rdatalabs == 2) {
216091e9e46SCy Schubert 		if(dname_subdomain_c(rdata, (uint8_t*)&"\001*\000"))
217091e9e46SCy Schubert 			return RPZ_NODATA_ACTION;
218091e9e46SCy Schubert 		else if(dname_subdomain_c(rdata,
219091e9e46SCy Schubert 			(uint8_t*)&"\014rpz-passthru\000"))
220091e9e46SCy Schubert 			return RPZ_PASSTHRU_ACTION;
221091e9e46SCy Schubert 		else if(dname_subdomain_c(rdata, (uint8_t*)&"\010rpz-drop\000"))
222091e9e46SCy Schubert 			return RPZ_DROP_ACTION;
223091e9e46SCy Schubert 		else if(dname_subdomain_c(rdata,
224091e9e46SCy Schubert 			(uint8_t*)&"\014rpz-tcp-only\000"))
225091e9e46SCy Schubert 			return RPZ_TCP_ONLY_ACTION;
226091e9e46SCy Schubert 	}
227091e9e46SCy Schubert 
228091e9e46SCy Schubert 	/* all other TLDs starting with "rpz-" are invalid */
229091e9e46SCy Schubert 	tldlab = get_tld_label(rdata, rdatalen-2);
230091e9e46SCy Schubert 	if(tldlab && dname_lab_startswith(tldlab, "rpz-", &endptr))
231091e9e46SCy Schubert 		return RPZ_INVALID_ACTION;
232091e9e46SCy Schubert 
233091e9e46SCy Schubert 	/* no special label found */
234091e9e46SCy Schubert 	return RPZ_LOCAL_DATA_ACTION;
235091e9e46SCy Schubert }
236091e9e46SCy Schubert 
237091e9e46SCy Schubert static enum localzone_type
rpz_action_to_localzone_type(enum rpz_action a)238091e9e46SCy Schubert rpz_action_to_localzone_type(enum rpz_action a)
239091e9e46SCy Schubert {
240091e9e46SCy Schubert 	switch(a) {
241091e9e46SCy Schubert 	case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain;
242091e9e46SCy Schubert 	case RPZ_NODATA_ACTION: return local_zone_always_nodata;
243091e9e46SCy Schubert 	case RPZ_DROP_ACTION: return local_zone_always_deny;
244091e9e46SCy Schubert 	case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
24556850988SCy Schubert 	case RPZ_LOCAL_DATA_ACTION:
24656850988SCy Schubert 		ATTR_FALLTHROUGH
24756850988SCy Schubert 		/* fallthrough */
248091e9e46SCy Schubert 	case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect;
24924e36522SCy Schubert 	case RPZ_TCP_ONLY_ACTION: return local_zone_truncate;
25056850988SCy Schubert 	case RPZ_INVALID_ACTION:
25156850988SCy Schubert 		ATTR_FALLTHROUGH
25256850988SCy Schubert 		/* fallthrough */
253091e9e46SCy Schubert 	default: return local_zone_invalid;
254091e9e46SCy Schubert 	}
255091e9e46SCy Schubert }
256091e9e46SCy Schubert 
257091e9e46SCy Schubert enum respip_action
rpz_action_to_respip_action(enum rpz_action a)258091e9e46SCy Schubert rpz_action_to_respip_action(enum rpz_action a)
259091e9e46SCy Schubert {
260091e9e46SCy Schubert 	switch(a) {
261091e9e46SCy Schubert 	case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain;
262091e9e46SCy Schubert 	case RPZ_NODATA_ACTION: return respip_always_nodata;
263091e9e46SCy Schubert 	case RPZ_DROP_ACTION: return respip_always_deny;
264091e9e46SCy Schubert 	case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
26556850988SCy Schubert 	case RPZ_LOCAL_DATA_ACTION:
26656850988SCy Schubert 		ATTR_FALLTHROUGH
26756850988SCy Schubert 		/* fallthrough */
268091e9e46SCy Schubert 	case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect;
26924e36522SCy Schubert 	case RPZ_TCP_ONLY_ACTION: return respip_truncate;
27056850988SCy Schubert 	case RPZ_INVALID_ACTION:
27156850988SCy Schubert 		ATTR_FALLTHROUGH
27256850988SCy Schubert 		/* fallthrough */
273091e9e46SCy Schubert 	default: return respip_invalid;
274091e9e46SCy Schubert 	}
275091e9e46SCy Schubert }
276091e9e46SCy Schubert 
277091e9e46SCy Schubert static enum rpz_action
localzone_type_to_rpz_action(enum localzone_type lzt)278091e9e46SCy Schubert localzone_type_to_rpz_action(enum localzone_type lzt)
279091e9e46SCy Schubert {
280091e9e46SCy Schubert 	switch(lzt) {
281091e9e46SCy Schubert 	case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
282091e9e46SCy Schubert 	case local_zone_always_nodata: return RPZ_NODATA_ACTION;
283091e9e46SCy Schubert 	case local_zone_always_deny: return RPZ_DROP_ACTION;
284091e9e46SCy Schubert 	case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION;
285091e9e46SCy Schubert 	case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION;
28624e36522SCy Schubert 	case local_zone_truncate: return RPZ_TCP_ONLY_ACTION;
28756850988SCy Schubert 	case local_zone_invalid:
28856850988SCy Schubert 		ATTR_FALLTHROUGH
28956850988SCy Schubert 		/* fallthrough */
29024e36522SCy Schubert 	default: return RPZ_INVALID_ACTION;
291091e9e46SCy Schubert 	}
292091e9e46SCy Schubert }
293091e9e46SCy Schubert 
294091e9e46SCy Schubert enum rpz_action
respip_action_to_rpz_action(enum respip_action a)295091e9e46SCy Schubert respip_action_to_rpz_action(enum respip_action a)
296091e9e46SCy Schubert {
297091e9e46SCy Schubert 	switch(a) {
298091e9e46SCy Schubert 	case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
299091e9e46SCy Schubert 	case respip_always_nodata: return RPZ_NODATA_ACTION;
300091e9e46SCy Schubert 	case respip_always_deny: return RPZ_DROP_ACTION;
301091e9e46SCy Schubert 	case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
302091e9e46SCy Schubert 	case respip_redirect: return RPZ_LOCAL_DATA_ACTION;
30324e36522SCy Schubert 	case respip_truncate: return RPZ_TCP_ONLY_ACTION;
30456850988SCy Schubert 	case respip_invalid:
30556850988SCy Schubert 		ATTR_FALLTHROUGH
30656850988SCy Schubert 		/* fallthrough */
30724e36522SCy Schubert 	default: return RPZ_INVALID_ACTION;
308091e9e46SCy Schubert 	}
309091e9e46SCy Schubert }
310091e9e46SCy Schubert 
311091e9e46SCy Schubert /**
312091e9e46SCy Schubert  * Get RPZ trigger for dname
313091e9e46SCy Schubert  * @param dname: dname containing RPZ trigger
314091e9e46SCy Schubert  * @param dname_len: length of the dname
315091e9e46SCy Schubert  * @return: RPZ trigger enum
316091e9e46SCy Schubert  */
317091e9e46SCy Schubert static enum rpz_trigger
rpz_dname_to_trigger(uint8_t * dname,size_t dname_len)318091e9e46SCy Schubert rpz_dname_to_trigger(uint8_t* dname, size_t dname_len)
319091e9e46SCy Schubert {
320091e9e46SCy Schubert 	uint8_t* tldlab;
321091e9e46SCy Schubert 	char* endptr;
322091e9e46SCy Schubert 
323091e9e46SCy Schubert 	if(dname_valid(dname, dname_len) != dname_len)
324091e9e46SCy Schubert 		return RPZ_INVALID_TRIGGER;
325091e9e46SCy Schubert 
326091e9e46SCy Schubert 	tldlab = get_tld_label(dname, dname_len);
327091e9e46SCy Schubert 	if(!tldlab || !dname_lab_startswith(tldlab, "rpz-", &endptr))
328091e9e46SCy Schubert 		return RPZ_QNAME_TRIGGER;
329091e9e46SCy Schubert 
330091e9e46SCy Schubert 	if(dname_subdomain_c(tldlab,
331091e9e46SCy Schubert 		(uint8_t*)&"\015rpz-client-ip\000"))
332091e9e46SCy Schubert 		return RPZ_CLIENT_IP_TRIGGER;
333091e9e46SCy Schubert 	else if(dname_subdomain_c(tldlab, (uint8_t*)&"\006rpz-ip\000"))
334091e9e46SCy Schubert 		return RPZ_RESPONSE_IP_TRIGGER;
335091e9e46SCy Schubert 	else if(dname_subdomain_c(tldlab, (uint8_t*)&"\013rpz-nsdname\000"))
336091e9e46SCy Schubert 		return RPZ_NSDNAME_TRIGGER;
337091e9e46SCy Schubert 	else if(dname_subdomain_c(tldlab, (uint8_t*)&"\010rpz-nsip\000"))
338091e9e46SCy Schubert 		return RPZ_NSIP_TRIGGER;
339091e9e46SCy Schubert 
340091e9e46SCy Schubert 	return RPZ_QNAME_TRIGGER;
341091e9e46SCy Schubert }
342091e9e46SCy Schubert 
34324e36522SCy Schubert static inline struct clientip_synthesized_rrset*
rpz_clientip_synthesized_set_create(void)34424e36522SCy Schubert rpz_clientip_synthesized_set_create(void)
34524e36522SCy Schubert {
34624e36522SCy Schubert 	struct clientip_synthesized_rrset* set = calloc(1, sizeof(*set));
34724e36522SCy Schubert 	if(set == NULL) {
34824e36522SCy Schubert 		return NULL;
34924e36522SCy Schubert 	}
35024e36522SCy Schubert 	set->region = regional_create();
35124e36522SCy Schubert 	if(set->region == NULL) {
35224e36522SCy Schubert 		free(set);
35324e36522SCy Schubert 		return NULL;
35424e36522SCy Schubert 	}
35524e36522SCy Schubert 	addr_tree_init(&set->entries);
35624e36522SCy Schubert 	lock_rw_init(&set->lock);
35724e36522SCy Schubert 	return set;
35824e36522SCy Schubert }
35924e36522SCy Schubert 
36024e36522SCy Schubert static void
rpz_clientip_synthesized_rr_delete(rbnode_type * n,void * ATTR_UNUSED (arg))36124e36522SCy Schubert rpz_clientip_synthesized_rr_delete(rbnode_type* n, void* ATTR_UNUSED(arg))
36224e36522SCy Schubert {
36324e36522SCy Schubert 	struct clientip_synthesized_rr* r = (struct clientip_synthesized_rr*)n->key;
36424e36522SCy Schubert 	lock_rw_destroy(&r->lock);
36524e36522SCy Schubert #ifdef THREADS_DISABLED
36624e36522SCy Schubert 	(void)r;
36724e36522SCy Schubert #endif
36824e36522SCy Schubert }
36924e36522SCy Schubert 
37024e36522SCy Schubert static inline void
rpz_clientip_synthesized_set_delete(struct clientip_synthesized_rrset * set)37124e36522SCy Schubert rpz_clientip_synthesized_set_delete(struct clientip_synthesized_rrset* set)
37224e36522SCy Schubert {
37324e36522SCy Schubert 	if(set == NULL) {
37424e36522SCy Schubert 		return;
37524e36522SCy Schubert 	}
37624e36522SCy Schubert 	lock_rw_destroy(&set->lock);
37724e36522SCy Schubert 	traverse_postorder(&set->entries, rpz_clientip_synthesized_rr_delete, NULL);
37824e36522SCy Schubert 	regional_destroy(set->region);
37924e36522SCy Schubert 	free(set);
38024e36522SCy Schubert }
38124e36522SCy Schubert 
38224e36522SCy Schubert void
rpz_delete(struct rpz * r)38324e36522SCy Schubert rpz_delete(struct rpz* r)
384091e9e46SCy Schubert {
385091e9e46SCy Schubert 	if(!r)
386091e9e46SCy Schubert 		return;
387091e9e46SCy Schubert 	local_zones_delete(r->local_zones);
38824e36522SCy Schubert 	local_zones_delete(r->nsdname_zones);
389091e9e46SCy Schubert 	respip_set_delete(r->respip_set);
39024e36522SCy Schubert 	rpz_clientip_synthesized_set_delete(r->client_set);
39124e36522SCy Schubert 	rpz_clientip_synthesized_set_delete(r->ns_set);
392091e9e46SCy Schubert 	regional_destroy(r->region);
393091e9e46SCy Schubert 	free(r->taglist);
394091e9e46SCy Schubert 	free(r->log_name);
395091e9e46SCy Schubert 	free(r);
396091e9e46SCy Schubert }
397091e9e46SCy Schubert 
398091e9e46SCy Schubert int
rpz_clear(struct rpz * r)399091e9e46SCy Schubert rpz_clear(struct rpz* r)
400091e9e46SCy Schubert {
401091e9e46SCy Schubert 	/* must hold write lock on auth_zone */
402091e9e46SCy Schubert 	local_zones_delete(r->local_zones);
40324e36522SCy Schubert 	r->local_zones = NULL;
40424e36522SCy Schubert 	local_zones_delete(r->nsdname_zones);
40524e36522SCy Schubert 	r->nsdname_zones = NULL;
406091e9e46SCy Schubert 	respip_set_delete(r->respip_set);
40724e36522SCy Schubert 	r->respip_set = NULL;
40824e36522SCy Schubert 	rpz_clientip_synthesized_set_delete(r->client_set);
40924e36522SCy Schubert 	r->client_set = NULL;
41024e36522SCy Schubert 	rpz_clientip_synthesized_set_delete(r->ns_set);
41124e36522SCy Schubert 	r->ns_set = NULL;
412091e9e46SCy Schubert 	if(!(r->local_zones = local_zones_create())){
413091e9e46SCy Schubert 		return 0;
414091e9e46SCy Schubert 	}
41524e36522SCy Schubert 	r->nsdname_zones = local_zones_create();
41624e36522SCy Schubert 	if(r->nsdname_zones == NULL) {
41724e36522SCy Schubert 		return 0;
41824e36522SCy Schubert 	}
419091e9e46SCy Schubert 	if(!(r->respip_set = respip_set_create())) {
420091e9e46SCy Schubert 		return 0;
421091e9e46SCy Schubert 	}
42224e36522SCy Schubert 	if(!(r->client_set = rpz_clientip_synthesized_set_create())) {
42324e36522SCy Schubert 		return 0;
42424e36522SCy Schubert 	}
42524e36522SCy Schubert 	if(!(r->ns_set = rpz_clientip_synthesized_set_create())) {
42624e36522SCy Schubert 		return 0;
42724e36522SCy Schubert 	}
428091e9e46SCy Schubert 	return 1;
429091e9e46SCy Schubert }
430091e9e46SCy Schubert 
431091e9e46SCy Schubert void
rpz_finish_config(struct rpz * r)432091e9e46SCy Schubert rpz_finish_config(struct rpz* r)
433091e9e46SCy Schubert {
434091e9e46SCy Schubert 	lock_rw_wrlock(&r->respip_set->lock);
435091e9e46SCy Schubert 	addr_tree_init_parents(&r->respip_set->ip_tree);
436091e9e46SCy Schubert 	lock_rw_unlock(&r->respip_set->lock);
43724e36522SCy Schubert 
43824e36522SCy Schubert 	lock_rw_wrlock(&r->client_set->lock);
43924e36522SCy Schubert 	addr_tree_init_parents(&r->client_set->entries);
44024e36522SCy Schubert 	lock_rw_unlock(&r->client_set->lock);
44124e36522SCy Schubert 
44224e36522SCy Schubert 	lock_rw_wrlock(&r->ns_set->lock);
44324e36522SCy Schubert 	addr_tree_init_parents(&r->ns_set->entries);
44424e36522SCy Schubert 	lock_rw_unlock(&r->ns_set->lock);
445091e9e46SCy Schubert }
446091e9e46SCy Schubert 
447091e9e46SCy Schubert /** new rrset containing CNAME override, does not yet contain a dname */
448091e9e46SCy Schubert static struct ub_packed_rrset_key*
new_cname_override(struct regional * region,uint8_t * ct,size_t ctlen)449091e9e46SCy Schubert new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen)
450091e9e46SCy Schubert {
451091e9e46SCy Schubert 	struct ub_packed_rrset_key* rrset;
452091e9e46SCy Schubert 	struct packed_rrset_data* pd;
453091e9e46SCy Schubert 	uint16_t rdlength = htons(ctlen);
454091e9e46SCy Schubert 	rrset = (struct ub_packed_rrset_key*)regional_alloc_zero(region,
455091e9e46SCy Schubert 		sizeof(*rrset));
456091e9e46SCy Schubert 	if(!rrset) {
457091e9e46SCy Schubert 		log_err("out of memory");
458091e9e46SCy Schubert 		return NULL;
459091e9e46SCy Schubert 	}
460091e9e46SCy Schubert 	rrset->entry.key = rrset;
461091e9e46SCy Schubert 	pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd));
462091e9e46SCy Schubert 	if(!pd) {
463091e9e46SCy Schubert 		log_err("out of memory");
464091e9e46SCy Schubert 		return NULL;
465091e9e46SCy Schubert 	}
466091e9e46SCy Schubert 	pd->trust = rrset_trust_prim_noglue;
467091e9e46SCy Schubert 	pd->security = sec_status_insecure;
468091e9e46SCy Schubert 
469091e9e46SCy Schubert 	pd->count = 1;
470091e9e46SCy Schubert 	pd->rr_len = regional_alloc_zero(region, sizeof(*pd->rr_len));
471091e9e46SCy Schubert 	pd->rr_ttl = regional_alloc_zero(region, sizeof(*pd->rr_ttl));
472091e9e46SCy Schubert 	pd->rr_data = regional_alloc_zero(region, sizeof(*pd->rr_data));
473091e9e46SCy Schubert 	if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
474091e9e46SCy Schubert 		log_err("out of memory");
475091e9e46SCy Schubert 		return NULL;
476091e9e46SCy Schubert 	}
477091e9e46SCy Schubert 	pd->rr_len[0] = ctlen+2;
478091e9e46SCy Schubert 	pd->rr_ttl[0] = 3600;
479091e9e46SCy Schubert 	pd->rr_data[0] = regional_alloc_zero(region, 2 /* rdlength */ + ctlen);
480091e9e46SCy Schubert 	if(!pd->rr_data[0]) {
481091e9e46SCy Schubert 		log_err("out of memory");
482091e9e46SCy Schubert 		return NULL;
483091e9e46SCy Schubert 	}
484091e9e46SCy Schubert 	memmove(pd->rr_data[0], &rdlength, 2);
485091e9e46SCy Schubert 	memmove(pd->rr_data[0]+2, ct, ctlen);
486091e9e46SCy Schubert 
487091e9e46SCy Schubert 	rrset->entry.data = pd;
488091e9e46SCy Schubert 	rrset->rk.type = htons(LDNS_RR_TYPE_CNAME);
489091e9e46SCy Schubert 	rrset->rk.rrset_class = htons(LDNS_RR_CLASS_IN);
490091e9e46SCy Schubert 	return rrset;
491091e9e46SCy Schubert }
492091e9e46SCy Schubert 
493335c7cdaSCy Schubert /** delete the cname override */
494335c7cdaSCy Schubert static void
delete_cname_override(struct rpz * r)495335c7cdaSCy Schubert delete_cname_override(struct rpz* r)
496335c7cdaSCy Schubert {
497335c7cdaSCy Schubert 	if(r->cname_override) {
498335c7cdaSCy Schubert 		/* The cname override is what is allocated in the region. */
499335c7cdaSCy Schubert 		regional_free_all(r->region);
500335c7cdaSCy Schubert 		r->cname_override = NULL;
501335c7cdaSCy Schubert 	}
502335c7cdaSCy Schubert }
503335c7cdaSCy Schubert 
504335c7cdaSCy Schubert /** Apply rpz config elements to the rpz structure, false on failure. */
505335c7cdaSCy Schubert static int
rpz_apply_cfg_elements(struct rpz * r,struct config_auth * p)506335c7cdaSCy Schubert rpz_apply_cfg_elements(struct rpz* r, struct config_auth* p)
507335c7cdaSCy Schubert {
508335c7cdaSCy Schubert 	if(p->rpz_taglist && p->rpz_taglistlen) {
509335c7cdaSCy Schubert 		r->taglistlen = p->rpz_taglistlen;
510335c7cdaSCy Schubert 		r->taglist = memdup(p->rpz_taglist, r->taglistlen);
511335c7cdaSCy Schubert 		if(!r->taglist) {
512335c7cdaSCy Schubert 			log_err("malloc failure on RPZ taglist alloc");
513335c7cdaSCy Schubert 			return 0;
514335c7cdaSCy Schubert 		}
515335c7cdaSCy Schubert 	}
516335c7cdaSCy Schubert 
517335c7cdaSCy Schubert 	if(p->rpz_action_override) {
518335c7cdaSCy Schubert 		r->action_override = rpz_config_to_action(p->rpz_action_override);
519335c7cdaSCy Schubert 	}
520335c7cdaSCy Schubert 	else
521335c7cdaSCy Schubert 		r->action_override = RPZ_NO_OVERRIDE_ACTION;
522335c7cdaSCy Schubert 
523335c7cdaSCy Schubert 	if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
524335c7cdaSCy Schubert 		uint8_t nm[LDNS_MAX_DOMAINLEN+1];
525335c7cdaSCy Schubert 		size_t nmlen = sizeof(nm);
526335c7cdaSCy Schubert 
527335c7cdaSCy Schubert 		if(!p->rpz_cname) {
528335c7cdaSCy Schubert 			log_err("rpz: override with cname action found, but no "
529335c7cdaSCy Schubert 				"rpz-cname-override configured");
530335c7cdaSCy Schubert 			return 0;
531335c7cdaSCy Schubert 		}
532335c7cdaSCy Schubert 
533335c7cdaSCy Schubert 		if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) {
534335c7cdaSCy Schubert 			log_err("rpz: cannot parse cname override: %s",
535335c7cdaSCy Schubert 				p->rpz_cname);
536335c7cdaSCy Schubert 			return 0;
537335c7cdaSCy Schubert 		}
538335c7cdaSCy Schubert 		r->cname_override = new_cname_override(r->region, nm, nmlen);
539335c7cdaSCy Schubert 		if(!r->cname_override) {
540335c7cdaSCy Schubert 			return 0;
541335c7cdaSCy Schubert 		}
542335c7cdaSCy Schubert 	}
543335c7cdaSCy Schubert 	r->log = p->rpz_log;
544335c7cdaSCy Schubert 	r->signal_nxdomain_ra = p->rpz_signal_nxdomain_ra;
545335c7cdaSCy Schubert 	if(p->rpz_log_name) {
546335c7cdaSCy Schubert 		if(!(r->log_name = strdup(p->rpz_log_name))) {
547335c7cdaSCy Schubert 			log_err("malloc failure on RPZ log_name strdup");
548335c7cdaSCy Schubert 			return 0;
549335c7cdaSCy Schubert 		}
550335c7cdaSCy Schubert 	}
551335c7cdaSCy Schubert 	return 1;
552335c7cdaSCy Schubert }
553335c7cdaSCy Schubert 
554091e9e46SCy Schubert struct rpz*
rpz_create(struct config_auth * p)555091e9e46SCy Schubert rpz_create(struct config_auth* p)
556091e9e46SCy Schubert {
557091e9e46SCy Schubert 	struct rpz* r = calloc(1, sizeof(*r));
558091e9e46SCy Schubert 	if(!r)
559091e9e46SCy Schubert 		goto err;
560091e9e46SCy Schubert 
561091e9e46SCy Schubert 	r->region = regional_create_custom(sizeof(struct regional));
562091e9e46SCy Schubert 	if(!r->region) {
563091e9e46SCy Schubert 		goto err;
564091e9e46SCy Schubert 	}
565091e9e46SCy Schubert 
566091e9e46SCy Schubert 	if(!(r->local_zones = local_zones_create())){
567091e9e46SCy Schubert 		goto err;
568091e9e46SCy Schubert 	}
56924e36522SCy Schubert 
57024e36522SCy Schubert 	r->nsdname_zones = local_zones_create();
57124e36522SCy Schubert 	if(r->local_zones == NULL){
57224e36522SCy Schubert 		goto err;
57324e36522SCy Schubert 	}
57424e36522SCy Schubert 
575091e9e46SCy Schubert 	if(!(r->respip_set = respip_set_create())) {
576091e9e46SCy Schubert 		goto err;
577091e9e46SCy Schubert 	}
57824e36522SCy Schubert 
57924e36522SCy Schubert 	r->client_set = rpz_clientip_synthesized_set_create();
58024e36522SCy Schubert 	if(r->client_set == NULL) {
58124e36522SCy Schubert 		goto err;
58224e36522SCy Schubert 	}
58324e36522SCy Schubert 
58424e36522SCy Schubert 	r->ns_set = rpz_clientip_synthesized_set_create();
58524e36522SCy Schubert 	if(r->ns_set == NULL) {
58624e36522SCy Schubert 		goto err;
58724e36522SCy Schubert 	}
58824e36522SCy Schubert 
589335c7cdaSCy Schubert 	if(!rpz_apply_cfg_elements(r, p))
590091e9e46SCy Schubert 		goto err;
591091e9e46SCy Schubert 	return r;
592091e9e46SCy Schubert err:
593091e9e46SCy Schubert 	if(r) {
594091e9e46SCy Schubert 		if(r->local_zones)
595091e9e46SCy Schubert 			local_zones_delete(r->local_zones);
59624e36522SCy Schubert 		if(r->nsdname_zones)
59724e36522SCy Schubert 			local_zones_delete(r->nsdname_zones);
598091e9e46SCy Schubert 		if(r->respip_set)
599091e9e46SCy Schubert 			respip_set_delete(r->respip_set);
60024e36522SCy Schubert 		if(r->client_set != NULL)
60124e36522SCy Schubert 			rpz_clientip_synthesized_set_delete(r->client_set);
60224e36522SCy Schubert 		if(r->ns_set != NULL)
60324e36522SCy Schubert 			rpz_clientip_synthesized_set_delete(r->ns_set);
604091e9e46SCy Schubert 		if(r->taglist)
605091e9e46SCy Schubert 			free(r->taglist);
606369c6923SCy Schubert 		if(r->region)
607369c6923SCy Schubert 			regional_destroy(r->region);
608091e9e46SCy Schubert 		free(r);
609091e9e46SCy Schubert 	}
610091e9e46SCy Schubert 	return NULL;
611091e9e46SCy Schubert }
612091e9e46SCy Schubert 
613335c7cdaSCy Schubert int
rpz_config(struct rpz * r,struct config_auth * p)614335c7cdaSCy Schubert rpz_config(struct rpz* r, struct config_auth* p)
615335c7cdaSCy Schubert {
616335c7cdaSCy Schubert 	/* If the zonefile changes, it is read later, after which
617335c7cdaSCy Schubert 	 * rpz_clear and rpz_finish_config is called. */
618335c7cdaSCy Schubert 
619335c7cdaSCy Schubert 	/* free taglist, if any */
620335c7cdaSCy Schubert 	if(r->taglist) {
621335c7cdaSCy Schubert 		free(r->taglist);
622335c7cdaSCy Schubert 		r->taglist = NULL;
623335c7cdaSCy Schubert 		r->taglistlen = 0;
624335c7cdaSCy Schubert 	}
625335c7cdaSCy Schubert 
626335c7cdaSCy Schubert 	/* free logname, if any */
627335c7cdaSCy Schubert 	if(r->log_name) {
628335c7cdaSCy Schubert 		free(r->log_name);
629335c7cdaSCy Schubert 		r->log_name = NULL;
630335c7cdaSCy Schubert 	}
631335c7cdaSCy Schubert 
632335c7cdaSCy Schubert 	delete_cname_override(r);
633335c7cdaSCy Schubert 
634335c7cdaSCy Schubert 	if(!rpz_apply_cfg_elements(r, p))
635335c7cdaSCy Schubert 		return 0;
636335c7cdaSCy Schubert 	return 1;
637335c7cdaSCy Schubert }
638335c7cdaSCy Schubert 
639091e9e46SCy Schubert /**
640091e9e46SCy Schubert  * Remove RPZ zone name from dname
641091e9e46SCy Schubert  * Copy dname to newdname, without the originlen number of trailing bytes
642091e9e46SCy Schubert  */
643091e9e46SCy Schubert static size_t
strip_dname_origin(uint8_t * dname,size_t dnamelen,size_t originlen,uint8_t * newdname,size_t maxnewdnamelen)644091e9e46SCy Schubert strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen,
645091e9e46SCy Schubert 	uint8_t* newdname, size_t maxnewdnamelen)
646091e9e46SCy Schubert {
647091e9e46SCy Schubert 	size_t newdnamelen;
648091e9e46SCy Schubert 	if(dnamelen < originlen)
649091e9e46SCy Schubert 		return 0;
650091e9e46SCy Schubert 	newdnamelen = dnamelen - originlen;
651091e9e46SCy Schubert 	if(newdnamelen+1 > maxnewdnamelen)
652091e9e46SCy Schubert 		return 0;
653091e9e46SCy Schubert 	memmove(newdname, dname, newdnamelen);
654091e9e46SCy Schubert 	newdname[newdnamelen] = 0;
655091e9e46SCy Schubert 	return newdnamelen + 1;	/* + 1 for root label */
656091e9e46SCy Schubert }
657091e9e46SCy Schubert 
658091e9e46SCy Schubert static void
rpz_insert_local_zones_trigger(struct local_zones * lz,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rrtype,uint16_t rrclass,uint32_t ttl,uint8_t * rdata,size_t rdata_len,uint8_t * rr,size_t rr_len)65924e36522SCy Schubert rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname,
66024e36522SCy Schubert 	size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass,
66124e36522SCy Schubert 	uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
662091e9e46SCy Schubert {
663091e9e46SCy Schubert 	struct local_zone* z;
664091e9e46SCy Schubert 	enum localzone_type tp = local_zone_always_transparent;
665091e9e46SCy Schubert 	int dnamelabs = dname_count_labels(dname);
666091e9e46SCy Schubert 	int newzone = 0;
667091e9e46SCy Schubert 
66824e36522SCy Schubert 	if(a == RPZ_INVALID_ACTION) {
6695469a995SCy Schubert 		char str[255+1];
6705469a995SCy Schubert 		if(rrtype == LDNS_RR_TYPE_SOA || rrtype == LDNS_RR_TYPE_NS ||
6715469a995SCy Schubert 			rrtype == LDNS_RR_TYPE_DNAME ||
6725469a995SCy Schubert 			rrtype == LDNS_RR_TYPE_DNSKEY ||
6735469a995SCy Schubert 			rrtype == LDNS_RR_TYPE_RRSIG ||
6745469a995SCy Schubert 			rrtype == LDNS_RR_TYPE_NSEC ||
6755469a995SCy Schubert 			rrtype == LDNS_RR_TYPE_NSEC3PARAM ||
6765469a995SCy Schubert 			rrtype == LDNS_RR_TYPE_NSEC3 ||
6775469a995SCy Schubert 			rrtype == LDNS_RR_TYPE_DS) {
6785469a995SCy Schubert 			free(dname);
6795469a995SCy Schubert 			return; /* no need to log these types as unsupported */
6805469a995SCy Schubert 		}
6815469a995SCy Schubert 		dname_str(dname, str);
682a39a5a69SCy Schubert 		verbose(VERB_ALGO, "rpz: qname trigger, %s skipping unsupported action: %s",
6835469a995SCy Schubert 			str, rpz_action_to_string(a));
684091e9e46SCy Schubert 		free(dname);
685091e9e46SCy Schubert 		return;
686091e9e46SCy Schubert 	}
687091e9e46SCy Schubert 
68824e36522SCy Schubert 	lock_rw_wrlock(&lz->lock);
689091e9e46SCy Schubert 	/* exact match */
69024e36522SCy Schubert 	z = local_zones_find(lz, dname, dnamelen, dnamelabs, LDNS_RR_CLASS_IN);
69124e36522SCy Schubert 	if(z != NULL && a != RPZ_LOCAL_DATA_ACTION) {
69224e36522SCy Schubert 		char* rrstr = sldns_wire2str_rr(rr, rr_len);
69324e36522SCy Schubert 		if(rrstr == NULL) {
69424e36522SCy Schubert 			log_err("malloc error while inserting rpz nsdname trigger");
695091e9e46SCy Schubert 			free(dname);
69624e36522SCy Schubert 			lock_rw_unlock(&lz->lock);
697091e9e46SCy Schubert 			return;
698091e9e46SCy Schubert 		}
69924e36522SCy Schubert 		if(rrstr[0])
70024e36522SCy Schubert 			rrstr[strlen(rrstr)-1]=0; /* remove newline */
70124e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", rrstr);
702091e9e46SCy Schubert 		free(rrstr);
703091e9e46SCy Schubert 		free(dname);
70424e36522SCy Schubert 		lock_rw_unlock(&lz->lock);
705091e9e46SCy Schubert 		return;
706091e9e46SCy Schubert 	}
70724e36522SCy Schubert 	if(z == NULL) {
708091e9e46SCy Schubert 		tp = rpz_action_to_localzone_type(a);
70924e36522SCy Schubert 		z = local_zones_add_zone(lz, dname, dnamelen,
71024e36522SCy Schubert 					 dnamelabs, rrclass, tp);
71124e36522SCy Schubert 		if(z == NULL) {
71224e36522SCy Schubert 			log_warn("rpz: create failed");
71324e36522SCy Schubert 			lock_rw_unlock(&lz->lock);
714091e9e46SCy Schubert 			/* dname will be free'd in failed local_zone_create() */
715091e9e46SCy Schubert 			return;
716091e9e46SCy Schubert 		}
717091e9e46SCy Schubert 		newzone = 1;
718091e9e46SCy Schubert 	}
719091e9e46SCy Schubert 	if(a == RPZ_LOCAL_DATA_ACTION) {
72024e36522SCy Schubert 		char* rrstr = sldns_wire2str_rr(rr, rr_len);
72124e36522SCy Schubert 		if(rrstr == NULL) {
72224e36522SCy Schubert 			log_err("malloc error while inserting rpz nsdname trigger");
723091e9e46SCy Schubert 			free(dname);
72424e36522SCy Schubert 			lock_rw_unlock(&lz->lock);
725091e9e46SCy Schubert 			return;
726091e9e46SCy Schubert 		}
727091e9e46SCy Schubert 		lock_rw_wrlock(&z->lock);
72824e36522SCy Schubert 		local_zone_enter_rr(z, dname, dnamelen, dnamelabs, rrtype,
72924e36522SCy Schubert 				    rrclass, ttl, rdata, rdata_len, rrstr);
730091e9e46SCy Schubert 		lock_rw_unlock(&z->lock);
731091e9e46SCy Schubert 		free(rrstr);
732091e9e46SCy Schubert 	}
73324e36522SCy Schubert 	if(!newzone) {
734091e9e46SCy Schubert 		free(dname);
73524e36522SCy Schubert 	}
73624e36522SCy Schubert 	lock_rw_unlock(&lz->lock);
73724e36522SCy Schubert }
73824e36522SCy Schubert 
73924e36522SCy Schubert static void
rpz_log_dname(char const * msg,uint8_t * dname,size_t dname_len)74024e36522SCy Schubert rpz_log_dname(char const* msg, uint8_t* dname, size_t dname_len)
74124e36522SCy Schubert {
74224e36522SCy Schubert 	char buf[LDNS_MAX_DOMAINLEN+1];
74324e36522SCy Schubert 	(void)dname_len;
74424e36522SCy Schubert 	dname_str(dname, buf);
74524e36522SCy Schubert 	verbose(VERB_ALGO, "rpz: %s: <%s>", msg, buf);
74624e36522SCy Schubert }
74724e36522SCy Schubert 
74824e36522SCy Schubert static void
rpz_insert_qname_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rrtype,uint16_t rrclass,uint32_t ttl,uint8_t * rdata,size_t rdata_len,uint8_t * rr,size_t rr_len)74924e36522SCy Schubert rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
75024e36522SCy Schubert 	enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
75124e36522SCy Schubert 	uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
75224e36522SCy Schubert {
75324e36522SCy Schubert 	if(a == RPZ_INVALID_ACTION) {
75424e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: skipping invalid action");
75524e36522SCy Schubert 		free(dname);
756091e9e46SCy Schubert 		return;
757091e9e46SCy Schubert 	}
758091e9e46SCy Schubert 
75924e36522SCy Schubert 	rpz_insert_local_zones_trigger(r->local_zones, dname, dnamelen, a, rrtype,
76024e36522SCy Schubert 				       rrclass, ttl, rdata, rdata_len, rr, rr_len);
76124e36522SCy Schubert }
76224e36522SCy Schubert 
76324e36522SCy Schubert static int
rpz_strip_nsdname_suffix(uint8_t * dname,size_t maxdnamelen,uint8_t ** stripdname,size_t * stripdnamelen)76424e36522SCy Schubert rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen,
76524e36522SCy Schubert 	uint8_t** stripdname, size_t* stripdnamelen)
76624e36522SCy Schubert {
76724e36522SCy Schubert 	uint8_t* tldstart = get_tld_label(dname, maxdnamelen);
76824e36522SCy Schubert 	uint8_t swap;
76924e36522SCy Schubert 	if(tldstart == NULL) {
77024e36522SCy Schubert 		if(dname == NULL) {
77124e36522SCy Schubert 			*stripdname = NULL;
77224e36522SCy Schubert 			*stripdnamelen = 0;
77324e36522SCy Schubert 			return 0;
77424e36522SCy Schubert 		}
77524e36522SCy Schubert 		*stripdname = memdup(dname, maxdnamelen);
77624e36522SCy Schubert 		if(!*stripdname) {
77724e36522SCy Schubert 			*stripdnamelen = 0;
77824e36522SCy Schubert 			log_err("malloc failure for rpz strip suffix");
77924e36522SCy Schubert 			return 0;
78024e36522SCy Schubert 		}
78124e36522SCy Schubert 		*stripdnamelen = maxdnamelen;
78224e36522SCy Schubert 		return 1;
78324e36522SCy Schubert 	}
78424e36522SCy Schubert 	/* shorten the domain name briefly,
78524e36522SCy Schubert 	 * then we allocate a new name with the correct length */
78624e36522SCy Schubert 	swap = *tldstart;
78724e36522SCy Schubert 	*tldstart = 0;
78824e36522SCy Schubert 	(void)dname_count_size_labels(dname, stripdnamelen);
78924e36522SCy Schubert 	*stripdname = memdup(dname, *stripdnamelen);
79024e36522SCy Schubert 	*tldstart = swap;
79124e36522SCy Schubert 	if(!*stripdname) {
79224e36522SCy Schubert 		*stripdnamelen = 0;
79324e36522SCy Schubert 		log_err("malloc failure for rpz strip suffix");
79424e36522SCy Schubert 		return 0;
79524e36522SCy Schubert 	}
79624e36522SCy Schubert 	return 1;
79724e36522SCy Schubert }
79824e36522SCy Schubert 
79924e36522SCy Schubert static void
rpz_insert_nsdname_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rrtype,uint16_t rrclass,uint32_t ttl,uint8_t * rdata,size_t rdata_len,uint8_t * rr,size_t rr_len)80024e36522SCy Schubert rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
80124e36522SCy Schubert 	enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
80224e36522SCy Schubert 	uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
80324e36522SCy Schubert {
80424e36522SCy Schubert 	uint8_t* dname_stripped = NULL;
80524e36522SCy Schubert 	size_t dnamelen_stripped = 0;
80624e36522SCy Schubert 
80724e36522SCy Schubert 	rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped,
80824e36522SCy Schubert 		&dnamelen_stripped);
80924e36522SCy Schubert 	if(a == RPZ_INVALID_ACTION) {
81024e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: skipping invalid action");
81124e36522SCy Schubert 		free(dname_stripped);
81224e36522SCy Schubert 		return;
81324e36522SCy Schubert 	}
81424e36522SCy Schubert 
81524e36522SCy Schubert 	/* dname_stripped is consumed or freed by the insert routine */
81624e36522SCy Schubert 	rpz_insert_local_zones_trigger(r->nsdname_zones, dname_stripped,
81724e36522SCy Schubert 		dnamelen_stripped, a, rrtype, rrclass, ttl, rdata, rdata_len,
81824e36522SCy Schubert 		rr, rr_len);
81924e36522SCy Schubert }
82024e36522SCy Schubert 
82124e36522SCy Schubert static int
rpz_insert_ipaddr_based_trigger(struct respip_set * set,struct sockaddr_storage * addr,socklen_t addrlen,int net,enum rpz_action a,uint16_t rrtype,uint16_t rrclass,uint32_t ttl,uint8_t * rdata,size_t rdata_len,uint8_t * rr,size_t rr_len)82224e36522SCy Schubert rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* addr,
82324e36522SCy Schubert 	socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype,
82424e36522SCy Schubert 	uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len,
82524e36522SCy Schubert 	uint8_t* rr, size_t rr_len)
82624e36522SCy Schubert {
82724e36522SCy Schubert 	struct resp_addr* node;
82824e36522SCy Schubert 	char* rrstr;
82924e36522SCy Schubert 	enum respip_action respa = rpz_action_to_respip_action(a);
83024e36522SCy Schubert 
83124e36522SCy Schubert 	lock_rw_wrlock(&set->lock);
83224e36522SCy Schubert 	rrstr = sldns_wire2str_rr(rr, rr_len);
83324e36522SCy Schubert 	if(rrstr == NULL) {
83424e36522SCy Schubert 		log_err("malloc error while inserting rpz ipaddr based trigger");
83524e36522SCy Schubert 		lock_rw_unlock(&set->lock);
83624e36522SCy Schubert 		return 0;
83724e36522SCy Schubert 	}
83824e36522SCy Schubert 
83924e36522SCy Schubert 	node = respip_sockaddr_find_or_create(set, addr, addrlen, net, 1, rrstr);
84024e36522SCy Schubert 	if(node == NULL) {
84124e36522SCy Schubert 		lock_rw_unlock(&set->lock);
84224e36522SCy Schubert 		free(rrstr);
84324e36522SCy Schubert 		return 0;
84424e36522SCy Schubert 	}
84524e36522SCy Schubert 
84624e36522SCy Schubert 	lock_rw_wrlock(&node->lock);
84724e36522SCy Schubert 	lock_rw_unlock(&set->lock);
84824e36522SCy Schubert 
84924e36522SCy Schubert 	node->action = respa;
85024e36522SCy Schubert 
85124e36522SCy Schubert 	if(a == RPZ_LOCAL_DATA_ACTION) {
85224e36522SCy Schubert 		respip_enter_rr(set->region, node, rrtype,
85324e36522SCy Schubert 				rrclass, ttl, rdata, rdata_len, rrstr, "");
85424e36522SCy Schubert 	}
85524e36522SCy Schubert 
85624e36522SCy Schubert 	lock_rw_unlock(&node->lock);
85724e36522SCy Schubert 	free(rrstr);
85824e36522SCy Schubert 	return 1;
85924e36522SCy Schubert }
86024e36522SCy Schubert 
86124e36522SCy Schubert static inline struct clientip_synthesized_rr*
rpz_clientip_ensure_entry(struct clientip_synthesized_rrset * set,struct sockaddr_storage * addr,socklen_t addrlen,int net)86224e36522SCy Schubert rpz_clientip_ensure_entry(struct clientip_synthesized_rrset* set,
86324e36522SCy Schubert 	struct sockaddr_storage* addr, socklen_t addrlen, int net)
86424e36522SCy Schubert {
86524e36522SCy Schubert 	int insert_ok;
86624e36522SCy Schubert 	struct clientip_synthesized_rr* node =
86724e36522SCy Schubert 		(struct clientip_synthesized_rr*)addr_tree_find(&set->entries,
86824e36522SCy Schubert 								addr, addrlen, net);
86924e36522SCy Schubert 
87024e36522SCy Schubert 	if(node != NULL) { return node; }
87124e36522SCy Schubert 
87224e36522SCy Schubert 	/* node does not yet exist => allocate one */
87324e36522SCy Schubert 	node = regional_alloc_zero(set->region, sizeof(*node));
87424e36522SCy Schubert 	if(node == NULL) {
87524e36522SCy Schubert 		log_err("out of memory");
87624e36522SCy Schubert 		return NULL;
87724e36522SCy Schubert 	}
87824e36522SCy Schubert 
87924e36522SCy Schubert 	lock_rw_init(&node->lock);
88024e36522SCy Schubert 	node->action = RPZ_INVALID_ACTION;
88124e36522SCy Schubert 	insert_ok = addr_tree_insert(&set->entries, &node->node,
88224e36522SCy Schubert 				     addr, addrlen, net);
88324e36522SCy Schubert 	if (!insert_ok) {
88424e36522SCy Schubert 		log_warn("rpz: unexpected: unable to insert clientip address node");
88524e36522SCy Schubert 		/* we can not free the just allocated node.
88624e36522SCy Schubert 		 * theoretically a memleak */
88724e36522SCy Schubert 		return NULL;
88824e36522SCy Schubert 	}
88924e36522SCy Schubert 
89024e36522SCy Schubert 	return node;
89124e36522SCy Schubert }
89224e36522SCy Schubert 
89324e36522SCy Schubert static void
rpz_report_rrset_error(const char * msg,uint8_t * rr,size_t rr_len)89424e36522SCy Schubert rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) {
89524e36522SCy Schubert 	char* rrstr = sldns_wire2str_rr(rr, rr_len);
89624e36522SCy Schubert 	if(rrstr == NULL) {
89724e36522SCy Schubert 		log_err("malloc error while inserting rpz clientip based record");
89824e36522SCy Schubert 		return;
89924e36522SCy Schubert 	}
90024e36522SCy Schubert 	log_err("rpz: unexpected: unable to insert %s: %s", msg, rrstr);
90124e36522SCy Schubert 	free(rrstr);
90224e36522SCy Schubert }
90324e36522SCy Schubert 
90424e36522SCy Schubert /* from localzone.c; difference is we don't have a dname */
9059cf5bc93SCy Schubert static struct local_rrset*
rpz_clientip_new_rrset(struct regional * region,struct clientip_synthesized_rr * raddr,uint16_t rrtype,uint16_t rrclass)90624e36522SCy Schubert rpz_clientip_new_rrset(struct regional* region,
90724e36522SCy Schubert 	struct clientip_synthesized_rr* raddr, uint16_t rrtype, uint16_t rrclass)
90824e36522SCy Schubert {
90924e36522SCy Schubert 	struct packed_rrset_data* pd;
91024e36522SCy Schubert 	struct local_rrset* rrset = (struct local_rrset*)
91124e36522SCy Schubert 		regional_alloc_zero(region, sizeof(*rrset));
91224e36522SCy Schubert 	if(rrset == NULL) {
91324e36522SCy Schubert 		log_err("out of memory");
91424e36522SCy Schubert 		return NULL;
91524e36522SCy Schubert 	}
91624e36522SCy Schubert 	rrset->next = raddr->data;
91724e36522SCy Schubert 	raddr->data = rrset;
91824e36522SCy Schubert 	rrset->rrset = (struct ub_packed_rrset_key*)
91924e36522SCy Schubert 		regional_alloc_zero(region, sizeof(*rrset->rrset));
92024e36522SCy Schubert 	if(rrset->rrset == NULL) {
92124e36522SCy Schubert 		log_err("out of memory");
92224e36522SCy Schubert 		return NULL;
92324e36522SCy Schubert 	}
92424e36522SCy Schubert 	rrset->rrset->entry.key = rrset->rrset;
92524e36522SCy Schubert 	pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd));
92624e36522SCy Schubert 	if(pd == NULL) {
92724e36522SCy Schubert 		log_err("out of memory");
92824e36522SCy Schubert 		return NULL;
92924e36522SCy Schubert 	}
93024e36522SCy Schubert 	pd->trust = rrset_trust_prim_noglue;
93124e36522SCy Schubert 	pd->security = sec_status_insecure;
93224e36522SCy Schubert 	rrset->rrset->entry.data = pd;
93324e36522SCy Schubert 	rrset->rrset->rk.type = htons(rrtype);
93424e36522SCy Schubert 	rrset->rrset->rk.rrset_class = htons(rrclass);
93524e36522SCy Schubert 	rrset->rrset->rk.dname = regional_alloc_zero(region, 1);
93624e36522SCy Schubert 	if(rrset->rrset->rk.dname == NULL) {
93724e36522SCy Schubert 		log_err("out of memory");
93824e36522SCy Schubert 		return NULL;
93924e36522SCy Schubert 	}
94024e36522SCy Schubert 	rrset->rrset->rk.dname_len = 1;
94124e36522SCy Schubert 	return rrset;
94224e36522SCy Schubert }
94324e36522SCy Schubert 
94424e36522SCy Schubert static int
rpz_clientip_enter_rr(struct regional * region,struct clientip_synthesized_rr * raddr,uint16_t rrtype,uint16_t rrclass,time_t ttl,uint8_t * rdata,size_t rdata_len)94524e36522SCy Schubert rpz_clientip_enter_rr(struct regional* region, struct clientip_synthesized_rr* raddr,
94624e36522SCy Schubert 	uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata,
94724e36522SCy Schubert 	size_t rdata_len)
94824e36522SCy Schubert {
94924e36522SCy Schubert 	struct local_rrset* rrset;
95024e36522SCy Schubert 	if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data != NULL) {
95124e36522SCy Schubert 		log_err("CNAME response-ip data can not co-exist with other "
95224e36522SCy Schubert 			"client-ip data");
95324e36522SCy Schubert 		return 0;
95424e36522SCy Schubert 	}
95524e36522SCy Schubert 
95624e36522SCy Schubert 	rrset = rpz_clientip_new_rrset(region, raddr, rrtype, rrclass);
95724e36522SCy Schubert 	if(raddr->data == NULL) {
95824e36522SCy Schubert 		return 0;
95924e36522SCy Schubert 	}
96024e36522SCy Schubert 
96124e36522SCy Schubert 	return rrset_insert_rr(region, rrset->rrset->entry.data, rdata, rdata_len, ttl, "");
96224e36522SCy Schubert }
96324e36522SCy Schubert 
96424e36522SCy Schubert static int
rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset * set,struct sockaddr_storage * addr,socklen_t addrlen,int net,enum rpz_action a,uint16_t rrtype,uint16_t rrclass,uint32_t ttl,uint8_t * rdata,size_t rdata_len,uint8_t * rr,size_t rr_len)96524e36522SCy Schubert rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct sockaddr_storage* addr,
96624e36522SCy Schubert 	socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype,
96724e36522SCy Schubert 	uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len,
96824e36522SCy Schubert 	uint8_t* rr, size_t rr_len)
96924e36522SCy Schubert {
97024e36522SCy Schubert 	struct clientip_synthesized_rr* node;
97124e36522SCy Schubert 
97224e36522SCy Schubert 	lock_rw_wrlock(&set->lock);
97324e36522SCy Schubert 
97424e36522SCy Schubert 	node = rpz_clientip_ensure_entry(set, addr, addrlen, net);
97524e36522SCy Schubert 	if(node == NULL) {
97624e36522SCy Schubert 		lock_rw_unlock(&set->lock);
97724e36522SCy Schubert 		rpz_report_rrset_error("client ip address", rr, rr_len);
97824e36522SCy Schubert 		return 0;
97924e36522SCy Schubert 	}
98024e36522SCy Schubert 
98124e36522SCy Schubert 	lock_rw_wrlock(&node->lock);
98224e36522SCy Schubert 	lock_rw_unlock(&set->lock);
98324e36522SCy Schubert 
98424e36522SCy Schubert 	node->action = a;
98524e36522SCy Schubert 	if(a == RPZ_LOCAL_DATA_ACTION) {
98624e36522SCy Schubert 		if(!rpz_clientip_enter_rr(set->region, node, rrtype,
98724e36522SCy Schubert 			rrclass, ttl, rdata, rdata_len)) {
98824e36522SCy Schubert 			verbose(VERB_ALGO, "rpz: unable to insert clientip rr");
98924e36522SCy Schubert 			lock_rw_unlock(&node->lock);
99024e36522SCy Schubert 			return 0;
99124e36522SCy Schubert 		}
99224e36522SCy Schubert 
99324e36522SCy Schubert 	}
99424e36522SCy Schubert 
99524e36522SCy Schubert 	lock_rw_unlock(&node->lock);
99624e36522SCy Schubert 
99724e36522SCy Schubert 	return 1;
99824e36522SCy Schubert }
99924e36522SCy Schubert 
100024e36522SCy Schubert static int
rpz_insert_clientip_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rrtype,uint16_t rrclass,uint32_t ttl,uint8_t * rdata,size_t rdata_len,uint8_t * rr,size_t rr_len)100124e36522SCy Schubert rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
100224e36522SCy Schubert 	enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
100324e36522SCy Schubert 	uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
100424e36522SCy Schubert {
100524e36522SCy Schubert 	struct sockaddr_storage addr;
100624e36522SCy Schubert 	socklen_t addrlen;
100724e36522SCy Schubert 	int net, af;
100824e36522SCy Schubert 
100924e36522SCy Schubert 	if(a == RPZ_INVALID_ACTION) {
101024e36522SCy Schubert 		return 0;
101124e36522SCy Schubert 	}
101224e36522SCy Schubert 
101324e36522SCy Schubert 	if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
101424e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: unable to parse client ip");
101524e36522SCy Schubert 		return 0;
101624e36522SCy Schubert 	}
101724e36522SCy Schubert 
101824e36522SCy Schubert 	return rpz_clientip_insert_trigger_rr(r->client_set, &addr, addrlen, net,
101924e36522SCy Schubert 			a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
102024e36522SCy Schubert }
102124e36522SCy Schubert 
102224e36522SCy Schubert static int
rpz_insert_nsip_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rrtype,uint16_t rrclass,uint32_t ttl,uint8_t * rdata,size_t rdata_len,uint8_t * rr,size_t rr_len)102324e36522SCy Schubert rpz_insert_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
102424e36522SCy Schubert 	enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
102524e36522SCy Schubert 	uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
102624e36522SCy Schubert {
102724e36522SCy Schubert 	struct sockaddr_storage addr;
102824e36522SCy Schubert 	socklen_t addrlen;
102924e36522SCy Schubert 	int net, af;
103024e36522SCy Schubert 
103124e36522SCy Schubert 	if(a == RPZ_INVALID_ACTION) {
103224e36522SCy Schubert 		return 0;
103324e36522SCy Schubert 	}
103424e36522SCy Schubert 
103524e36522SCy Schubert 	if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
103624e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: unable to parse ns ip");
103724e36522SCy Schubert 		return 0;
103824e36522SCy Schubert 	}
103924e36522SCy Schubert 
104024e36522SCy Schubert 	return rpz_clientip_insert_trigger_rr(r->ns_set, &addr, addrlen, net,
104124e36522SCy Schubert 			a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
104224e36522SCy Schubert }
104324e36522SCy Schubert 
1044091e9e46SCy Schubert /** Insert RR into RPZ's respip_set */
1045091e9e46SCy Schubert static int
rpz_insert_response_ip_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rrtype,uint16_t rrclass,uint32_t ttl,uint8_t * rdata,size_t rdata_len,uint8_t * rr,size_t rr_len)1046091e9e46SCy Schubert rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
1047091e9e46SCy Schubert 	enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
1048091e9e46SCy Schubert 	uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
1049091e9e46SCy Schubert {
1050091e9e46SCy Schubert 	struct sockaddr_storage addr;
1051091e9e46SCy Schubert 	socklen_t addrlen;
1052091e9e46SCy Schubert 	int net, af;
1053091e9e46SCy Schubert 
105424e36522SCy Schubert 	if(a == RPZ_INVALID_ACTION) {
105524e36522SCy Schubert 		return 0;
105624e36522SCy Schubert 	}
105724e36522SCy Schubert 
105824e36522SCy Schubert 	if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
105924e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: unable to parse response ip");
106024e36522SCy Schubert 		return 0;
106124e36522SCy Schubert 	}
106224e36522SCy Schubert 
106324e36522SCy Schubert 	if(a == RPZ_INVALID_ACTION ||
106424e36522SCy Schubert 		rpz_action_to_respip_action(a) == respip_invalid) {
10655469a995SCy Schubert 		char str[255+1];
10665469a995SCy Schubert 		dname_str(dname, str);
1067a39a5a69SCy Schubert 		verbose(VERB_ALGO, "rpz: respip trigger, %s skipping unsupported action: %s",
10685469a995SCy Schubert 			str, rpz_action_to_string(a));
1069091e9e46SCy Schubert 		return 0;
1070091e9e46SCy Schubert 	}
1071091e9e46SCy Schubert 
107224e36522SCy Schubert 	return rpz_insert_ipaddr_based_trigger(r->respip_set, &addr, addrlen, net,
107324e36522SCy Schubert 			a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
1074091e9e46SCy Schubert }
1075091e9e46SCy Schubert 
1076091e9e46SCy Schubert int
rpz_insert_rr(struct rpz * r,uint8_t * azname,size_t aznamelen,uint8_t * dname,size_t dnamelen,uint16_t rr_type,uint16_t rr_class,uint32_t rr_ttl,uint8_t * rdatawl,size_t rdatalen,uint8_t * rr,size_t rr_len)107725039b37SCy Schubert rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
1078091e9e46SCy Schubert 	size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl,
1079091e9e46SCy Schubert 	uint8_t* rdatawl, size_t rdatalen, uint8_t* rr, size_t rr_len)
1080091e9e46SCy Schubert {
1081091e9e46SCy Schubert 	size_t policydnamelen;
1082091e9e46SCy Schubert 	/* name is free'd in local_zone delete */
1083091e9e46SCy Schubert 	enum rpz_trigger t;
1084091e9e46SCy Schubert 	enum rpz_action a;
1085091e9e46SCy Schubert 	uint8_t* policydname;
1086091e9e46SCy Schubert 
108724e36522SCy Schubert 	if(rpz_type_ignored(rr_type)) {
108824e36522SCy Schubert 		/* this rpz action is not valid, eg. this is the SOA or NS RR */
108924e36522SCy Schubert 		return 1;
109024e36522SCy Schubert 	}
109125039b37SCy Schubert 	if(!dname_subdomain_c(dname, azname)) {
1092c0caa2e2SCy Schubert 		char* dname_str = sldns_wire2str_dname(dname, dnamelen);
1093c0caa2e2SCy Schubert 		char* azname_str = sldns_wire2str_dname(azname, aznamelen);
1094c0caa2e2SCy Schubert 		if(dname_str && azname_str) {
109524e36522SCy Schubert 			log_err("rpz: name of record (%s) to insert into RPZ is not a "
1096c0caa2e2SCy Schubert 				"subdomain of the configured name of the RPZ zone (%s)",
1097c0caa2e2SCy Schubert 				dname_str, azname_str);
1098c0caa2e2SCy Schubert 		} else {
109924e36522SCy Schubert 			log_err("rpz: name of record to insert into RPZ is not a "
110025039b37SCy Schubert 				"subdomain of the configured name of the RPZ zone");
1101c0caa2e2SCy Schubert 		}
1102c0caa2e2SCy Schubert 		free(dname_str);
1103c0caa2e2SCy Schubert 		free(azname_str);
1104091e9e46SCy Schubert 		return 0;
110525039b37SCy Schubert 	}
110625039b37SCy Schubert 
110725039b37SCy Schubert 	log_assert(dnamelen >= aznamelen);
110825039b37SCy Schubert 	if(!(policydname = calloc(1, (dnamelen-aznamelen)+1))) {
110925039b37SCy Schubert 		log_err("malloc error while inserting RPZ RR");
111025039b37SCy Schubert 		return 0;
111125039b37SCy Schubert 	}
1112091e9e46SCy Schubert 
1113091e9e46SCy Schubert 	a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
1114091e9e46SCy Schubert 	if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
1115091e9e46SCy Schubert 		policydname, (dnamelen-aznamelen)+1))) {
1116091e9e46SCy Schubert 		free(policydname);
1117091e9e46SCy Schubert 		return 0;
1118091e9e46SCy Schubert 	}
1119091e9e46SCy Schubert 	t = rpz_dname_to_trigger(policydname, policydnamelen);
1120091e9e46SCy Schubert 	if(t == RPZ_INVALID_TRIGGER) {
1121091e9e46SCy Schubert 		free(policydname);
112224e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: skipping invalid trigger");
1123091e9e46SCy Schubert 		return 1;
1124091e9e46SCy Schubert 	}
1125091e9e46SCy Schubert 	if(t == RPZ_QNAME_TRIGGER) {
112624e36522SCy Schubert 		/* policydname will be consumed, no free */
1127091e9e46SCy Schubert 		rpz_insert_qname_trigger(r, policydname, policydnamelen,
1128091e9e46SCy Schubert 			a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
1129091e9e46SCy Schubert 			rr_len);
113024e36522SCy Schubert 	} else if(t == RPZ_RESPONSE_IP_TRIGGER) {
1131091e9e46SCy Schubert 		rpz_insert_response_ip_trigger(r, policydname, policydnamelen,
1132091e9e46SCy Schubert 			a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
1133091e9e46SCy Schubert 			rr_len);
1134091e9e46SCy Schubert 		free(policydname);
113524e36522SCy Schubert 	} else if(t == RPZ_CLIENT_IP_TRIGGER) {
113624e36522SCy Schubert 		rpz_insert_clientip_trigger(r, policydname, policydnamelen,
113724e36522SCy Schubert 			a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
113824e36522SCy Schubert 			rr_len);
1139091e9e46SCy Schubert 		free(policydname);
114024e36522SCy Schubert 	} else if(t == RPZ_NSIP_TRIGGER) {
114124e36522SCy Schubert 		rpz_insert_nsip_trigger(r, policydname, policydnamelen,
114224e36522SCy Schubert 			a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
114324e36522SCy Schubert 			rr_len);
114424e36522SCy Schubert 		free(policydname);
114524e36522SCy Schubert 	} else if(t == RPZ_NSDNAME_TRIGGER) {
114624e36522SCy Schubert 		rpz_insert_nsdname_trigger(r, policydname, policydnamelen,
114724e36522SCy Schubert 			a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
114824e36522SCy Schubert 			rr_len);
114924e36522SCy Schubert 		free(policydname);
115024e36522SCy Schubert 	} else {
115124e36522SCy Schubert 		free(policydname);
115224e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: skipping unsupported trigger: %s",
1153091e9e46SCy Schubert 			rpz_trigger_to_string(t));
1154091e9e46SCy Schubert 	}
1155091e9e46SCy Schubert 	return 1;
1156091e9e46SCy Schubert }
1157091e9e46SCy Schubert 
1158091e9e46SCy Schubert /**
1159091e9e46SCy Schubert  * Find RPZ local-zone by qname.
116024e36522SCy Schubert  * @param zones: local-zone tree
1161091e9e46SCy Schubert  * @param qname: qname
1162091e9e46SCy Schubert  * @param qname_len: length of qname
1163091e9e46SCy Schubert  * @param qclass: qclass
116424e36522SCy Schubert  * @param only_exact: if 1 only exact (non wildcard) matches are returned
1165091e9e46SCy Schubert  * @param wr: get write lock for local-zone if 1, read lock if 0
1166091e9e46SCy Schubert  * @param zones_keep_lock: if set do not release the r->local_zones lock, this
1167091e9e46SCy Schubert  * 	  makes the caller of this function responsible for releasing the lock.
1168091e9e46SCy Schubert  * @return: NULL or local-zone holding rd or wr lock
1169091e9e46SCy Schubert  */
1170091e9e46SCy Schubert static struct local_zone*
rpz_find_zone(struct local_zones * zones,uint8_t * qname,size_t qname_len,uint16_t qclass,int only_exact,int wr,int zones_keep_lock)117124e36522SCy Schubert rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint16_t qclass,
1172091e9e46SCy Schubert 	int only_exact, int wr, int zones_keep_lock)
1173091e9e46SCy Schubert {
1174091e9e46SCy Schubert 	uint8_t* ce;
1175f44e67d1SCy Schubert 	size_t ce_len;
1176f44e67d1SCy Schubert 	int ce_labs;
1177091e9e46SCy Schubert 	uint8_t wc[LDNS_MAX_DOMAINLEN+1];
1178091e9e46SCy Schubert 	int exact;
1179091e9e46SCy Schubert 	struct local_zone* z = NULL;
118024e36522SCy Schubert 
1181091e9e46SCy Schubert 	if(wr) {
118224e36522SCy Schubert 		lock_rw_wrlock(&zones->lock);
1183091e9e46SCy Schubert 	} else {
118424e36522SCy Schubert 		lock_rw_rdlock(&zones->lock);
1185091e9e46SCy Schubert 	}
118624e36522SCy Schubert 	z = local_zones_find_le(zones, qname, qname_len,
1187091e9e46SCy Schubert 		dname_count_labels(qname),
1188091e9e46SCy Schubert 		LDNS_RR_CLASS_IN, &exact);
1189091e9e46SCy Schubert 	if(!z || (only_exact && !exact)) {
119024e36522SCy Schubert 		if(!zones_keep_lock) {
119124e36522SCy Schubert 			lock_rw_unlock(&zones->lock);
119224e36522SCy Schubert 		}
1193091e9e46SCy Schubert 		return NULL;
1194091e9e46SCy Schubert 	}
1195091e9e46SCy Schubert 	if(wr) {
1196091e9e46SCy Schubert 		lock_rw_wrlock(&z->lock);
1197091e9e46SCy Schubert 	} else {
1198091e9e46SCy Schubert 		lock_rw_rdlock(&z->lock);
1199091e9e46SCy Schubert 	}
1200091e9e46SCy Schubert 	if(!zones_keep_lock) {
120124e36522SCy Schubert 		lock_rw_unlock(&zones->lock);
1202091e9e46SCy Schubert 	}
1203091e9e46SCy Schubert 
1204091e9e46SCy Schubert 	if(exact)
1205091e9e46SCy Schubert 		return z;
1206091e9e46SCy Schubert 
1207091e9e46SCy Schubert 	/* No exact match found, lookup wildcard. closest encloser must
1208091e9e46SCy Schubert 	 * be the shared parent between the qname and the best local
1209091e9e46SCy Schubert 	 * zone match, append '*' to that and do another lookup. */
1210091e9e46SCy Schubert 
1211091e9e46SCy Schubert 	ce = dname_get_shared_topdomain(z->name, qname);
12125469a995SCy Schubert 	if(!ce /* should not happen */) {
1213091e9e46SCy Schubert 		lock_rw_unlock(&z->lock);
1214091e9e46SCy Schubert 		if(zones_keep_lock) {
121524e36522SCy Schubert 			lock_rw_unlock(&zones->lock);
1216091e9e46SCy Schubert 		}
1217091e9e46SCy Schubert 		return NULL;
1218091e9e46SCy Schubert 	}
1219091e9e46SCy Schubert 	ce_labs = dname_count_size_labels(ce, &ce_len);
1220091e9e46SCy Schubert 	if(ce_len+2 > sizeof(wc)) {
1221091e9e46SCy Schubert 		lock_rw_unlock(&z->lock);
1222091e9e46SCy Schubert 		if(zones_keep_lock) {
122324e36522SCy Schubert 			lock_rw_unlock(&zones->lock);
1224091e9e46SCy Schubert 		}
1225091e9e46SCy Schubert 		return NULL;
1226091e9e46SCy Schubert 	}
1227091e9e46SCy Schubert 	wc[0] = 1; /* length of wildcard label */
1228091e9e46SCy Schubert 	wc[1] = (uint8_t)'*'; /* wildcard label */
1229091e9e46SCy Schubert 	memmove(wc+2, ce, ce_len);
1230091e9e46SCy Schubert 	lock_rw_unlock(&z->lock);
1231091e9e46SCy Schubert 
1232091e9e46SCy Schubert 	if(!zones_keep_lock) {
1233091e9e46SCy Schubert 		if(wr) {
123424e36522SCy Schubert 			lock_rw_wrlock(&zones->lock);
1235091e9e46SCy Schubert 		} else {
123624e36522SCy Schubert 			lock_rw_rdlock(&zones->lock);
1237091e9e46SCy Schubert 		}
1238091e9e46SCy Schubert 	}
123924e36522SCy Schubert 	z = local_zones_find_le(zones, wc,
1240091e9e46SCy Schubert 		ce_len+2, ce_labs+1, qclass, &exact);
1241091e9e46SCy Schubert 	if(!z || !exact) {
124224e36522SCy Schubert 		lock_rw_unlock(&zones->lock);
1243091e9e46SCy Schubert 		return NULL;
1244091e9e46SCy Schubert 	}
1245091e9e46SCy Schubert 	if(wr) {
1246091e9e46SCy Schubert 		lock_rw_wrlock(&z->lock);
1247091e9e46SCy Schubert 	} else {
1248091e9e46SCy Schubert 		lock_rw_rdlock(&z->lock);
1249091e9e46SCy Schubert 	}
1250091e9e46SCy Schubert 	if(!zones_keep_lock) {
125124e36522SCy Schubert 		lock_rw_unlock(&zones->lock);
1252091e9e46SCy Schubert 	}
1253091e9e46SCy Schubert 	return z;
1254091e9e46SCy Schubert }
1255091e9e46SCy Schubert 
12568f76bb7dSCy Schubert /** Find entry for RR type in the list of rrsets for the clientip. */
12578f76bb7dSCy Schubert static struct local_rrset*
rpz_find_synthesized_rrset(uint16_t qtype,struct clientip_synthesized_rr * data,int alias_ok)12588f76bb7dSCy Schubert rpz_find_synthesized_rrset(uint16_t qtype,
1259335c7cdaSCy Schubert 	struct clientip_synthesized_rr* data, int alias_ok)
12608f76bb7dSCy Schubert {
1261335c7cdaSCy Schubert 	struct local_rrset* cursor = data->data, *cname = NULL;
12628f76bb7dSCy Schubert 	while( cursor != NULL) {
12638f76bb7dSCy Schubert 		struct packed_rrset_key* packed_rrset = &cursor->rrset->rk;
12648f76bb7dSCy Schubert 		if(htons(qtype) == packed_rrset->type) {
12658f76bb7dSCy Schubert 			return cursor;
12668f76bb7dSCy Schubert 		}
1267335c7cdaSCy Schubert 		if(ntohs(packed_rrset->type) == LDNS_RR_TYPE_CNAME && alias_ok)
1268335c7cdaSCy Schubert 			cname = cursor;
12698f76bb7dSCy Schubert 		cursor = cursor->next;
12708f76bb7dSCy Schubert 	}
1271335c7cdaSCy Schubert 	if(alias_ok)
1272335c7cdaSCy Schubert 		return cname;
12738f76bb7dSCy Schubert 	return NULL;
12748f76bb7dSCy Schubert }
12758f76bb7dSCy Schubert 
1276091e9e46SCy Schubert /**
1277091e9e46SCy Schubert  * Remove RR from RPZ's local-data
1278091e9e46SCy Schubert  * @param z: local-zone for RPZ, holding write lock
1279091e9e46SCy Schubert  * @param policydname: dname of RR to remove
128024e36522SCy Schubert  * @param policydnamelen: length of policydname
1281091e9e46SCy Schubert  * @param rr_type: RR type of RR to remove
1282091e9e46SCy Schubert  * @param rdata: rdata of RR to remove
1283091e9e46SCy Schubert  * @param rdatalen: length of rdata
1284091e9e46SCy Schubert  * @return: 1 if zone must be removed after RR deletion
1285091e9e46SCy Schubert  */
1286091e9e46SCy Schubert static int
rpz_data_delete_rr(struct local_zone * z,uint8_t * policydname,size_t policydnamelen,uint16_t rr_type,uint8_t * rdata,size_t rdatalen)1287091e9e46SCy Schubert rpz_data_delete_rr(struct local_zone* z, uint8_t* policydname,
1288091e9e46SCy Schubert 	size_t policydnamelen, uint16_t rr_type, uint8_t* rdata,
1289091e9e46SCy Schubert 	size_t rdatalen)
1290091e9e46SCy Schubert {
1291091e9e46SCy Schubert 	struct local_data* ld;
1292091e9e46SCy Schubert 	struct packed_rrset_data* d;
1293091e9e46SCy Schubert 	size_t index;
1294091e9e46SCy Schubert 	ld = local_zone_find_data(z, policydname, policydnamelen,
1295091e9e46SCy Schubert 		dname_count_labels(policydname));
1296091e9e46SCy Schubert 	if(ld) {
1297091e9e46SCy Schubert 		struct local_rrset* prev=NULL, *p=ld->rrsets;
1298091e9e46SCy Schubert 		while(p && ntohs(p->rrset->rk.type) != rr_type) {
1299091e9e46SCy Schubert 			prev = p;
1300091e9e46SCy Schubert 			p = p->next;
1301091e9e46SCy Schubert 		}
1302091e9e46SCy Schubert 		if(!p)
1303091e9e46SCy Schubert 			return 0;
1304091e9e46SCy Schubert 		d = (struct packed_rrset_data*)p->rrset->entry.data;
1305091e9e46SCy Schubert 		if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) {
1306091e9e46SCy Schubert 			if(d->count == 1) {
1307091e9e46SCy Schubert 				/* no memory recycling for zone deletions ... */
1308091e9e46SCy Schubert 				if(prev) prev->next = p->next;
1309091e9e46SCy Schubert 				else ld->rrsets = p->next;
1310091e9e46SCy Schubert 			}
1311091e9e46SCy Schubert 			if(d->count > 1) {
1312091e9e46SCy Schubert 				if(!local_rrset_remove_rr(d, index))
1313091e9e46SCy Schubert 					return 0;
1314091e9e46SCy Schubert 			}
1315091e9e46SCy Schubert 		}
1316091e9e46SCy Schubert 	}
1317091e9e46SCy Schubert 	if(ld && ld->rrsets)
1318091e9e46SCy Schubert 		return 0;
1319091e9e46SCy Schubert 	return 1;
1320091e9e46SCy Schubert }
1321091e9e46SCy Schubert 
1322091e9e46SCy Schubert /**
1323091e9e46SCy Schubert  * Remove RR from RPZ's respip set
1324091e9e46SCy Schubert  * @param raddr: respip node
1325091e9e46SCy Schubert  * @param rr_type: RR type of RR to remove
1326091e9e46SCy Schubert  * @param rdata: rdata of RR to remove
1327091e9e46SCy Schubert  * @param rdatalen: length of rdata
1328091e9e46SCy Schubert  * @return: 1 if zone must be removed after RR deletion
1329091e9e46SCy Schubert  */
1330091e9e46SCy Schubert static int
rpz_rrset_delete_rr(struct resp_addr * raddr,uint16_t rr_type,uint8_t * rdata,size_t rdatalen)1331091e9e46SCy Schubert rpz_rrset_delete_rr(struct resp_addr* raddr, uint16_t rr_type, uint8_t* rdata,
1332091e9e46SCy Schubert 	size_t rdatalen)
1333091e9e46SCy Schubert {
1334091e9e46SCy Schubert 	size_t index;
1335091e9e46SCy Schubert 	struct packed_rrset_data* d;
1336091e9e46SCy Schubert 	if(!raddr->data)
1337091e9e46SCy Schubert 		return 1;
1338091e9e46SCy Schubert 	d = raddr->data->entry.data;
1339091e9e46SCy Schubert 	if(ntohs(raddr->data->rk.type) != rr_type) {
1340091e9e46SCy Schubert 		return 0;
1341091e9e46SCy Schubert 	}
1342091e9e46SCy Schubert 	if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) {
1343091e9e46SCy Schubert 		if(d->count == 1) {
1344091e9e46SCy Schubert 			/* regional alloc'd */
1345091e9e46SCy Schubert 			raddr->data->entry.data = NULL;
1346091e9e46SCy Schubert 			raddr->data = NULL;
1347091e9e46SCy Schubert 			return 1;
1348091e9e46SCy Schubert 		}
1349091e9e46SCy Schubert 		if(d->count > 1) {
1350091e9e46SCy Schubert 			if(!local_rrset_remove_rr(d, index))
1351091e9e46SCy Schubert 				return 0;
1352091e9e46SCy Schubert 		}
1353091e9e46SCy Schubert 	}
1354091e9e46SCy Schubert 	return 0;
1355091e9e46SCy Schubert 
1356091e9e46SCy Schubert }
1357091e9e46SCy Schubert 
13588f76bb7dSCy Schubert /** Remove RR from rpz localzones structure */
1359091e9e46SCy Schubert static void
rpz_remove_local_zones_trigger(struct local_zones * zones,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rr_type,uint16_t rr_class,uint8_t * rdatawl,size_t rdatalen)13608f76bb7dSCy Schubert rpz_remove_local_zones_trigger(struct local_zones* zones, uint8_t* dname,
13618f76bb7dSCy Schubert 	size_t dnamelen, enum rpz_action a, uint16_t rr_type,
13628f76bb7dSCy Schubert 	uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen)
1363091e9e46SCy Schubert {
1364091e9e46SCy Schubert 	struct local_zone* z;
1365091e9e46SCy Schubert 	int delete_zone = 1;
13668f76bb7dSCy Schubert 	z = rpz_find_zone(zones, dname, dnamelen, rr_class,
1367091e9e46SCy Schubert 		1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/);
1368091e9e46SCy Schubert 	if(!z) {
136924e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
1370091e9e46SCy Schubert 			"RPZ domain not found");
1371091e9e46SCy Schubert 		return;
1372091e9e46SCy Schubert 	}
1373091e9e46SCy Schubert 	if(a == RPZ_LOCAL_DATA_ACTION)
1374091e9e46SCy Schubert 		delete_zone = rpz_data_delete_rr(z, dname,
1375091e9e46SCy Schubert 			dnamelen, rr_type, rdatawl, rdatalen);
1376091e9e46SCy Schubert 	else if(a != localzone_type_to_rpz_action(z->type)) {
137725039b37SCy Schubert 		lock_rw_unlock(&z->lock);
13788f76bb7dSCy Schubert 		lock_rw_unlock(&zones->lock);
1379091e9e46SCy Schubert 		return;
1380091e9e46SCy Schubert 	}
1381091e9e46SCy Schubert 	lock_rw_unlock(&z->lock);
1382091e9e46SCy Schubert 	if(delete_zone) {
13838f76bb7dSCy Schubert 		local_zones_del_zone(zones, z);
1384091e9e46SCy Schubert 	}
13858f76bb7dSCy Schubert 	lock_rw_unlock(&zones->lock);
13868f76bb7dSCy Schubert }
13878f76bb7dSCy Schubert 
13888f76bb7dSCy Schubert /** Remove RR from RPZ's local-zone */
13898f76bb7dSCy Schubert static void
rpz_remove_qname_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rr_type,uint16_t rr_class,uint8_t * rdatawl,size_t rdatalen)13908f76bb7dSCy Schubert rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
13918f76bb7dSCy Schubert 	enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
13928f76bb7dSCy Schubert 	uint8_t* rdatawl, size_t rdatalen)
13938f76bb7dSCy Schubert {
13948f76bb7dSCy Schubert 	rpz_remove_local_zones_trigger(r->local_zones, dname, dnamelen,
13958f76bb7dSCy Schubert 		a, rr_type, rr_class, rdatawl, rdatalen);
1396091e9e46SCy Schubert }
1397091e9e46SCy Schubert 
1398091e9e46SCy Schubert static void
rpz_remove_response_ip_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rr_type,uint8_t * rdatawl,size_t rdatalen)1399091e9e46SCy Schubert rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
1400091e9e46SCy Schubert 	enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
1401091e9e46SCy Schubert {
1402091e9e46SCy Schubert 	struct resp_addr* node;
1403091e9e46SCy Schubert 	struct sockaddr_storage addr;
1404091e9e46SCy Schubert 	socklen_t addrlen;
1405091e9e46SCy Schubert 	int net, af;
1406091e9e46SCy Schubert 	int delete_respip = 1;
1407091e9e46SCy Schubert 
1408091e9e46SCy Schubert 	if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
1409091e9e46SCy Schubert 		return;
1410091e9e46SCy Schubert 
1411091e9e46SCy Schubert 	lock_rw_wrlock(&r->respip_set->lock);
1412091e9e46SCy Schubert 	if(!(node = (struct resp_addr*)addr_tree_find(
1413091e9e46SCy Schubert 		&r->respip_set->ip_tree, &addr, addrlen, net))) {
141424e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
1415091e9e46SCy Schubert 			"RPZ domain not found");
1416091e9e46SCy Schubert 		lock_rw_unlock(&r->respip_set->lock);
1417091e9e46SCy Schubert 		return;
1418091e9e46SCy Schubert 	}
1419091e9e46SCy Schubert 
1420091e9e46SCy Schubert 	lock_rw_wrlock(&node->lock);
1421091e9e46SCy Schubert 	if(a == RPZ_LOCAL_DATA_ACTION) {
1422091e9e46SCy Schubert 		/* remove RR, signal whether RR can be removed */
1423091e9e46SCy Schubert 		delete_respip = rpz_rrset_delete_rr(node, rr_type, rdatawl,
1424091e9e46SCy Schubert 			rdatalen);
1425091e9e46SCy Schubert 	}
1426091e9e46SCy Schubert 	lock_rw_unlock(&node->lock);
1427091e9e46SCy Schubert 	if(delete_respip)
1428091e9e46SCy Schubert 		respip_sockaddr_delete(r->respip_set, node);
1429091e9e46SCy Schubert 	lock_rw_unlock(&r->respip_set->lock);
1430091e9e46SCy Schubert }
1431091e9e46SCy Schubert 
14328f76bb7dSCy Schubert /** find and remove type from list of local_rrset entries*/
14338f76bb7dSCy Schubert static void
del_local_rrset_from_list(struct local_rrset ** list_head,uint16_t dtype)14348f76bb7dSCy Schubert del_local_rrset_from_list(struct local_rrset** list_head, uint16_t dtype)
14358f76bb7dSCy Schubert {
14368f76bb7dSCy Schubert 	struct local_rrset* prev=NULL, *p=*list_head;
14378f76bb7dSCy Schubert 	while(p && ntohs(p->rrset->rk.type) != dtype) {
14388f76bb7dSCy Schubert 		prev = p;
14398f76bb7dSCy Schubert 		p = p->next;
14408f76bb7dSCy Schubert 	}
14418f76bb7dSCy Schubert 	if(!p)
14428f76bb7dSCy Schubert 		return; /* rrset type not found */
14438f76bb7dSCy Schubert 	/* unlink it */
14448f76bb7dSCy Schubert 	if(prev) prev->next = p->next;
14458f76bb7dSCy Schubert 	else *list_head = p->next;
14468f76bb7dSCy Schubert 	/* no memory recycling for zone deletions ... */
14478f76bb7dSCy Schubert }
14488f76bb7dSCy Schubert 
14498f76bb7dSCy Schubert /** Delete client-ip trigger RR from its RRset and perhaps also the rrset
14508f76bb7dSCy Schubert  * from the linked list. Returns if the local data is empty and the node can
14518f76bb7dSCy Schubert  * be deleted too, or not. */
rpz_remove_clientip_rr(struct clientip_synthesized_rr * node,uint16_t rr_type,uint8_t * rdatawl,size_t rdatalen)14528f76bb7dSCy Schubert static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node,
14538f76bb7dSCy Schubert 	uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
14548f76bb7dSCy Schubert {
14558f76bb7dSCy Schubert 	struct local_rrset* rrset;
14568f76bb7dSCy Schubert 	struct packed_rrset_data* d;
14578f76bb7dSCy Schubert 	size_t index;
1458335c7cdaSCy Schubert 	rrset = rpz_find_synthesized_rrset(rr_type, node, 0);
14598f76bb7dSCy Schubert 	if(rrset == NULL)
14608f76bb7dSCy Schubert 		return 0; /* type not found, ignore */
14618f76bb7dSCy Schubert 	d = (struct packed_rrset_data*)rrset->rrset->entry.data;
14628f76bb7dSCy Schubert 	if(!packed_rrset_find_rr(d, rdatawl, rdatalen, &index))
14638f76bb7dSCy Schubert 		return 0; /* RR not found, ignore */
14648f76bb7dSCy Schubert 	if(d->count == 1) {
14658f76bb7dSCy Schubert 		/* regional alloc'd */
14668f76bb7dSCy Schubert 		/* delete the type entry from the list */
14678f76bb7dSCy Schubert 		del_local_rrset_from_list(&node->data, rr_type);
14688f76bb7dSCy Schubert 		/* if the list is empty, the node can be removed too */
14698f76bb7dSCy Schubert 		if(node->data == NULL)
14708f76bb7dSCy Schubert 			return 1;
14718f76bb7dSCy Schubert 	} else if (d->count > 1) {
14728f76bb7dSCy Schubert 		if(!local_rrset_remove_rr(d, index))
14738f76bb7dSCy Schubert 			return 0;
14748f76bb7dSCy Schubert 	}
14758f76bb7dSCy Schubert 	return 0;
14768f76bb7dSCy Schubert }
14778f76bb7dSCy Schubert 
14788f76bb7dSCy Schubert /** remove trigger RR from clientip_syntheized set tree. */
14798f76bb7dSCy Schubert static void
rpz_clientip_remove_trigger_rr(struct clientip_synthesized_rrset * set,struct sockaddr_storage * addr,socklen_t addrlen,int net,enum rpz_action a,uint16_t rr_type,uint8_t * rdatawl,size_t rdatalen)14808f76bb7dSCy Schubert rpz_clientip_remove_trigger_rr(struct clientip_synthesized_rrset* set,
14818f76bb7dSCy Schubert 	struct sockaddr_storage* addr, socklen_t addrlen, int net,
14828f76bb7dSCy Schubert 	enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
14838f76bb7dSCy Schubert {
14848f76bb7dSCy Schubert 	struct clientip_synthesized_rr* node;
14858f76bb7dSCy Schubert 	int delete_node = 1;
14868f76bb7dSCy Schubert 
14878f76bb7dSCy Schubert 	lock_rw_wrlock(&set->lock);
14888f76bb7dSCy Schubert 	node = (struct clientip_synthesized_rr*)addr_tree_find(&set->entries,
14898f76bb7dSCy Schubert 		addr, addrlen, net);
14908f76bb7dSCy Schubert 	if(node == NULL) {
14918f76bb7dSCy Schubert 		/* netblock not found */
14928f76bb7dSCy Schubert 		verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
14938f76bb7dSCy Schubert 			"RPZ address, netblock not found");
14948f76bb7dSCy Schubert 		lock_rw_unlock(&set->lock);
14958f76bb7dSCy Schubert 		return;
14968f76bb7dSCy Schubert 	}
14978f76bb7dSCy Schubert 	lock_rw_wrlock(&node->lock);
14988f76bb7dSCy Schubert 	if(a == RPZ_LOCAL_DATA_ACTION) {
14998f76bb7dSCy Schubert 		/* remove RR, signal whether entry can be removed */
15008f76bb7dSCy Schubert 		delete_node = rpz_remove_clientip_rr(node, rr_type, rdatawl,
15018f76bb7dSCy Schubert 			rdatalen);
15028f76bb7dSCy Schubert 	} else if(a != node->action) {
15038f76bb7dSCy Schubert 		/* ignore the RR with different action specification */
15048f76bb7dSCy Schubert 		delete_node = 0;
15058f76bb7dSCy Schubert 	}
15068f76bb7dSCy Schubert 	if(delete_node) {
15078f76bb7dSCy Schubert 		rbtree_delete(&set->entries, node->node.node.key);
15088f76bb7dSCy Schubert 	}
15098f76bb7dSCy Schubert 	lock_rw_unlock(&set->lock);
15108f76bb7dSCy Schubert 	lock_rw_unlock(&node->lock);
15118f76bb7dSCy Schubert 	if(delete_node) {
15128f76bb7dSCy Schubert 		lock_rw_destroy(&node->lock);
15138f76bb7dSCy Schubert 	}
15148f76bb7dSCy Schubert }
15158f76bb7dSCy Schubert 
15168f76bb7dSCy Schubert /** Remove clientip trigger RR from RPZ. */
15178f76bb7dSCy Schubert static void
rpz_remove_clientip_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rr_type,uint8_t * rdatawl,size_t rdatalen)15188f76bb7dSCy Schubert rpz_remove_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
15198f76bb7dSCy Schubert 	enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
15208f76bb7dSCy Schubert {
15218f76bb7dSCy Schubert 	struct sockaddr_storage addr;
15228f76bb7dSCy Schubert 	socklen_t addrlen;
15238f76bb7dSCy Schubert 	int net, af;
15248f76bb7dSCy Schubert 	if(a == RPZ_INVALID_ACTION)
15258f76bb7dSCy Schubert 		return;
15268f76bb7dSCy Schubert 	if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
15278f76bb7dSCy Schubert 		return;
15288f76bb7dSCy Schubert 	rpz_clientip_remove_trigger_rr(r->client_set, &addr, addrlen, net,
15298f76bb7dSCy Schubert 		a, rr_type, rdatawl, rdatalen);
15308f76bb7dSCy Schubert }
15318f76bb7dSCy Schubert 
15328f76bb7dSCy Schubert /** Remove nsip trigger RR from RPZ. */
15338f76bb7dSCy Schubert static void
rpz_remove_nsip_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rr_type,uint8_t * rdatawl,size_t rdatalen)15348f76bb7dSCy Schubert rpz_remove_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
15358f76bb7dSCy Schubert 	enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
15368f76bb7dSCy Schubert {
15378f76bb7dSCy Schubert 	struct sockaddr_storage addr;
15388f76bb7dSCy Schubert 	socklen_t addrlen;
15398f76bb7dSCy Schubert 	int net, af;
15408f76bb7dSCy Schubert 	if(a == RPZ_INVALID_ACTION)
15418f76bb7dSCy Schubert 		return;
15428f76bb7dSCy Schubert 	if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
15438f76bb7dSCy Schubert 		return;
15448f76bb7dSCy Schubert 	rpz_clientip_remove_trigger_rr(r->ns_set, &addr, addrlen, net,
15458f76bb7dSCy Schubert 		a, rr_type, rdatawl, rdatalen);
15468f76bb7dSCy Schubert }
15478f76bb7dSCy Schubert 
15488f76bb7dSCy Schubert /** Remove nsdname trigger RR from RPZ. */
15498f76bb7dSCy Schubert static void
rpz_remove_nsdname_trigger(struct rpz * r,uint8_t * dname,size_t dnamelen,enum rpz_action a,uint16_t rr_type,uint16_t rr_class,uint8_t * rdatawl,size_t rdatalen)15508f76bb7dSCy Schubert rpz_remove_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
15518f76bb7dSCy Schubert 	enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
15528f76bb7dSCy Schubert 	uint8_t* rdatawl, size_t rdatalen)
15538f76bb7dSCy Schubert {
15548f76bb7dSCy Schubert 	uint8_t* dname_stripped = NULL;
15558f76bb7dSCy Schubert 	size_t dnamelen_stripped = 0;
15568f76bb7dSCy Schubert 	if(a == RPZ_INVALID_ACTION)
15578f76bb7dSCy Schubert 		return;
15588f76bb7dSCy Schubert 	if(!rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped,
15598f76bb7dSCy Schubert 		&dnamelen_stripped))
15608f76bb7dSCy Schubert 		return;
15618f76bb7dSCy Schubert 	rpz_remove_local_zones_trigger(r->nsdname_zones, dname_stripped,
15628f76bb7dSCy Schubert 		dnamelen_stripped, a, rr_type, rr_class, rdatawl, rdatalen);
15638f76bb7dSCy Schubert 	free(dname_stripped);
15648f76bb7dSCy Schubert }
15658f76bb7dSCy Schubert 
1566091e9e46SCy Schubert void
rpz_remove_rr(struct rpz * r,uint8_t * azname,size_t aznamelen,uint8_t * dname,size_t dnamelen,uint16_t rr_type,uint16_t rr_class,uint8_t * rdatawl,size_t rdatalen)15678f76bb7dSCy Schubert rpz_remove_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
15688f76bb7dSCy Schubert 	size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl,
15698f76bb7dSCy Schubert 	size_t rdatalen)
1570091e9e46SCy Schubert {
1571091e9e46SCy Schubert 	size_t policydnamelen;
1572091e9e46SCy Schubert 	enum rpz_trigger t;
1573091e9e46SCy Schubert 	enum rpz_action a;
1574091e9e46SCy Schubert 	uint8_t* policydname;
1575091e9e46SCy Schubert 
15768f76bb7dSCy Schubert 	if(rpz_type_ignored(rr_type)) {
15778f76bb7dSCy Schubert 		/* this rpz action is not valid, eg. this is the SOA or NS RR */
15788f76bb7dSCy Schubert 		return;
15798f76bb7dSCy Schubert 	}
15808f76bb7dSCy Schubert 	if(!dname_subdomain_c(dname, azname)) {
15818f76bb7dSCy Schubert 		/* not subdomain of the RPZ zone. */
15828f76bb7dSCy Schubert 		return;
15838f76bb7dSCy Schubert 	}
15848f76bb7dSCy Schubert 
1585091e9e46SCy Schubert 	if(!(policydname = calloc(1, LDNS_MAX_DOMAINLEN + 1)))
1586091e9e46SCy Schubert 		return;
1587091e9e46SCy Schubert 
1588091e9e46SCy Schubert 	a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
1589091e9e46SCy Schubert 	if(a == RPZ_INVALID_ACTION) {
1590091e9e46SCy Schubert 		free(policydname);
1591091e9e46SCy Schubert 		return;
1592091e9e46SCy Schubert 	}
1593091e9e46SCy Schubert 	if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
1594091e9e46SCy Schubert 		policydname, LDNS_MAX_DOMAINLEN + 1))) {
1595091e9e46SCy Schubert 		free(policydname);
1596091e9e46SCy Schubert 		return;
1597091e9e46SCy Schubert 	}
1598091e9e46SCy Schubert 	t = rpz_dname_to_trigger(policydname, policydnamelen);
15998f76bb7dSCy Schubert 	if(t == RPZ_INVALID_TRIGGER) {
16008f76bb7dSCy Schubert 		/* skipping invalid trigger */
16018f76bb7dSCy Schubert 		free(policydname);
16028f76bb7dSCy Schubert 		return;
16038f76bb7dSCy Schubert 	}
1604091e9e46SCy Schubert 	if(t == RPZ_QNAME_TRIGGER) {
1605091e9e46SCy Schubert 		rpz_remove_qname_trigger(r, policydname, policydnamelen, a,
1606091e9e46SCy Schubert 			rr_type, rr_class, rdatawl, rdatalen);
1607091e9e46SCy Schubert 	} else if(t == RPZ_RESPONSE_IP_TRIGGER) {
1608091e9e46SCy Schubert 		rpz_remove_response_ip_trigger(r, policydname, policydnamelen,
1609091e9e46SCy Schubert 			a, rr_type, rdatawl, rdatalen);
16108f76bb7dSCy Schubert 	} else if(t == RPZ_CLIENT_IP_TRIGGER) {
16118f76bb7dSCy Schubert 		rpz_remove_clientip_trigger(r, policydname, policydnamelen, a,
16128f76bb7dSCy Schubert 			rr_type, rdatawl, rdatalen);
16138f76bb7dSCy Schubert 	} else if(t == RPZ_NSIP_TRIGGER) {
16148f76bb7dSCy Schubert 		rpz_remove_nsip_trigger(r, policydname, policydnamelen, a,
16158f76bb7dSCy Schubert 			rr_type, rdatawl, rdatalen);
16168f76bb7dSCy Schubert 	} else if(t == RPZ_NSDNAME_TRIGGER) {
16178f76bb7dSCy Schubert 		rpz_remove_nsdname_trigger(r, policydname, policydnamelen, a,
16188f76bb7dSCy Schubert 			rr_type, rr_class, rdatawl, rdatalen);
1619091e9e46SCy Schubert 	}
16208f76bb7dSCy Schubert 	/* else it was an unsupported trigger, also skipped. */
1621091e9e46SCy Schubert 	free(policydname);
1622091e9e46SCy Schubert }
1623091e9e46SCy Schubert 
1624091e9e46SCy Schubert /** print log information for an applied RPZ policy. Based on local-zone's
1625091e9e46SCy Schubert  * lz_inform_print().
162624e36522SCy Schubert  * The repinfo contains the reply address. If it is NULL, the module
162724e36522SCy Schubert  * state is used to report the first IP address (if any).
162824e36522SCy Schubert  * The dname is used, for the applied rpz, if NULL, addrnode is used.
1629091e9e46SCy Schubert  */
1630091e9e46SCy Schubert static void
log_rpz_apply(char * trigger,uint8_t * dname,struct addr_tree_node * addrnode,enum rpz_action a,struct query_info * qinfo,struct comm_reply * repinfo,struct module_qstate * ms,char * log_name)163124e36522SCy Schubert log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode,
163224e36522SCy Schubert 	enum rpz_action a, struct query_info* qinfo,
163324e36522SCy Schubert 	struct comm_reply* repinfo, struct module_qstate* ms, char* log_name)
1634091e9e46SCy Schubert {
163524e36522SCy Schubert 	char ip[128], txt[512], portstr[32];
1636091e9e46SCy Schubert 	char dnamestr[LDNS_MAX_DOMAINLEN+1];
163724e36522SCy Schubert 	uint16_t port = 0;
163824e36522SCy Schubert 	if(dname) {
1639091e9e46SCy Schubert 		dname_str(dname, dnamestr);
164024e36522SCy Schubert 	} else if(addrnode) {
16419cf5bc93SCy Schubert 		char addrbuf[128];
16429cf5bc93SCy Schubert 		addr_to_str(&addrnode->addr, addrnode->addrlen, addrbuf, sizeof(addrbuf));
16439cf5bc93SCy Schubert 		snprintf(dnamestr, sizeof(dnamestr), "%s/%d", addrbuf, addrnode->net);
164424e36522SCy Schubert 	} else {
164524e36522SCy Schubert 		dnamestr[0]=0;
164624e36522SCy Schubert 	}
164724e36522SCy Schubert 	if(repinfo) {
1648865f46b2SCy Schubert 		addr_to_str(&repinfo->client_addr, repinfo->client_addrlen, ip, sizeof(ip));
1649865f46b2SCy Schubert 		port = ntohs(((struct sockaddr_in*)&repinfo->client_addr)->sin_port);
165024e36522SCy Schubert 	} else if(ms && ms->mesh_info && ms->mesh_info->reply_list) {
1651865f46b2SCy Schubert 		addr_to_str(&ms->mesh_info->reply_list->query_reply.client_addr,
1652865f46b2SCy Schubert 			ms->mesh_info->reply_list->query_reply.client_addrlen,
1653865f46b2SCy Schubert 			ip, sizeof(ip));
1654865f46b2SCy Schubert 		port = ntohs(((struct sockaddr_in*)&ms->mesh_info->reply_list->query_reply.client_addr)->sin_port);
165524e36522SCy Schubert 	} else {
165624e36522SCy Schubert 		ip[0]=0;
165724e36522SCy Schubert 		port = 0;
165824e36522SCy Schubert 	}
165924e36522SCy Schubert 	snprintf(portstr, sizeof(portstr), "@%u", (unsigned)port);
166024e36522SCy Schubert 	snprintf(txt, sizeof(txt), "rpz: applied %s%s%s%s%s%s %s %s%s",
166124e36522SCy Schubert 		(log_name?"[":""), (log_name?log_name:""), (log_name?"] ":""),
166224e36522SCy Schubert 		(strcmp(trigger,"qname")==0?"":trigger),
166324e36522SCy Schubert 		(strcmp(trigger,"qname")==0?"":" "),
166424e36522SCy Schubert 		dnamestr, rpz_action_to_string(a),
166524e36522SCy Schubert 		(ip[0]?ip:""), (ip[0]?portstr:""));
1666091e9e46SCy Schubert 	log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
1667091e9e46SCy Schubert }
1668091e9e46SCy Schubert 
166924e36522SCy Schubert static struct clientip_synthesized_rr*
rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset * set,struct sockaddr_storage * addr,socklen_t addrlen,char * triggername)167024e36522SCy Schubert rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set,
167124e36522SCy Schubert 	struct sockaddr_storage* addr, socklen_t addrlen, char* triggername)
1672091e9e46SCy Schubert {
167324e36522SCy Schubert 	struct clientip_synthesized_rr* raddr = NULL;
167424e36522SCy Schubert 	enum rpz_action action = RPZ_INVALID_ACTION;
167524e36522SCy Schubert 
167624e36522SCy Schubert 	lock_rw_rdlock(&set->lock);
167724e36522SCy Schubert 
167824e36522SCy Schubert 	raddr = (struct clientip_synthesized_rr*)addr_tree_lookup(&set->entries,
167924e36522SCy Schubert 			addr, addrlen);
168024e36522SCy Schubert 	if(raddr != NULL) {
168124e36522SCy Schubert 		lock_rw_rdlock(&raddr->lock);
168224e36522SCy Schubert 		action = raddr->action;
168324e36522SCy Schubert 		if(verbosity >= VERB_ALGO) {
168424e36522SCy Schubert 			char ip[256], net[256];
168524e36522SCy Schubert 			addr_to_str(addr, addrlen, ip, sizeof(ip));
168624e36522SCy Schubert 			addr_to_str(&raddr->node.addr, raddr->node.addrlen,
168724e36522SCy Schubert 				net, sizeof(net));
168824e36522SCy Schubert 			verbose(VERB_ALGO, "rpz: trigger %s %s/%d on %s action=%s",
168924e36522SCy Schubert 				triggername, net, raddr->node.net, ip, rpz_action_to_string(action));
169024e36522SCy Schubert 		}
169124e36522SCy Schubert 	}
169224e36522SCy Schubert 	lock_rw_unlock(&set->lock);
169324e36522SCy Schubert 
169424e36522SCy Schubert 	return raddr;
169524e36522SCy Schubert }
169624e36522SCy Schubert 
169724e36522SCy Schubert static inline
169824e36522SCy Schubert struct clientip_synthesized_rr*
rpz_resolve_client_action_and_zone(struct auth_zones * az,struct query_info * qinfo,struct comm_reply * repinfo,uint8_t * taglist,size_t taglen,struct ub_server_stats * stats,struct local_zone ** z_out,struct auth_zone ** a_out,struct rpz ** r_out)169924e36522SCy Schubert rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo,
170024e36522SCy Schubert 	struct comm_reply* repinfo, uint8_t* taglist, size_t taglen,
170124e36522SCy Schubert 	struct ub_server_stats* stats,
170224e36522SCy Schubert 	/* output parameters */
170324e36522SCy Schubert 	struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out)
170424e36522SCy Schubert {
170524e36522SCy Schubert 	struct clientip_synthesized_rr* node = NULL;
170624e36522SCy Schubert 	struct auth_zone* a = NULL;
170725039b37SCy Schubert 	struct rpz* r = NULL;
1708091e9e46SCy Schubert 	struct local_zone* z = NULL;
170924e36522SCy Schubert 
1710091e9e46SCy Schubert 	lock_rw_rdlock(&az->rpz_lock);
171124e36522SCy Schubert 
171225039b37SCy Schubert 	for(a = az->rpz_first; a; a = a->rpz_az_next) {
171325039b37SCy Schubert 		lock_rw_rdlock(&a->lock);
171425039b37SCy Schubert 		r = a->rpz;
171524e36522SCy Schubert 		if(r->disabled) {
171624e36522SCy Schubert 			lock_rw_unlock(&a->lock);
171724e36522SCy Schubert 			continue;
171824e36522SCy Schubert 		}
171924e36522SCy Schubert 		if(r->taglist && !taglist_intersect(r->taglist,
172024e36522SCy Schubert 					r->taglistlen, taglist, taglen)) {
172124e36522SCy Schubert 			lock_rw_unlock(&a->lock);
172224e36522SCy Schubert 			continue;
172324e36522SCy Schubert 		}
172424e36522SCy Schubert 		z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len,
1725091e9e46SCy Schubert 			qinfo->qclass, 0, 0, 0);
1726865f46b2SCy Schubert 		node = rpz_ipbased_trigger_lookup(r->client_set,
1727865f46b2SCy Schubert 			&repinfo->client_addr, repinfo->client_addrlen,
1728865f46b2SCy Schubert 			"clientip");
172924e36522SCy Schubert 		if((z || node) && r->action_override == RPZ_DISABLED_ACTION) {
1730091e9e46SCy Schubert 			if(r->log)
173124e36522SCy Schubert 				log_rpz_apply((node?"clientip":"qname"),
173224e36522SCy Schubert 					(z?z->name:NULL),
173324e36522SCy Schubert 					(node?&node->node:NULL),
1734091e9e46SCy Schubert 					r->action_override,
173524e36522SCy Schubert 					qinfo, repinfo, NULL, r->log_name);
1736091e9e46SCy Schubert 			stats->rpz_action[r->action_override]++;
173724e36522SCy Schubert 			if(z != NULL) {
1738091e9e46SCy Schubert 				lock_rw_unlock(&z->lock);
1739091e9e46SCy Schubert 				z = NULL;
1740091e9e46SCy Schubert 			}
174124e36522SCy Schubert 			if(node != NULL) {
174224e36522SCy Schubert 				lock_rw_unlock(&node->lock);
174324e36522SCy Schubert 				node = NULL;
174424e36522SCy Schubert 			}
174524e36522SCy Schubert 		}
174624e36522SCy Schubert 		if(z || node) {
1747091e9e46SCy Schubert 			break;
1748091e9e46SCy Schubert 		}
174924e36522SCy Schubert 		/* not found in this auth_zone */
175025039b37SCy Schubert 		lock_rw_unlock(&a->lock);
175124e36522SCy Schubert 	}
175224e36522SCy Schubert 
175324e36522SCy Schubert 	lock_rw_unlock(&az->rpz_lock);
175424e36522SCy Schubert 
175524e36522SCy Schubert 	*r_out = r;
175624e36522SCy Schubert 	*a_out = a;
175724e36522SCy Schubert 	*z_out = z;
175824e36522SCy Schubert 
175924e36522SCy Schubert 	return node;
176024e36522SCy Schubert }
176124e36522SCy Schubert 
176224e36522SCy Schubert static inline int
rpz_is_udp_query(struct comm_reply * repinfo)176324e36522SCy Schubert rpz_is_udp_query(struct comm_reply* repinfo) {
176424e36522SCy Schubert 	return repinfo != NULL
176524e36522SCy Schubert 			? (repinfo->c != NULL
176624e36522SCy Schubert 				? repinfo->c->type == comm_udp
176724e36522SCy Schubert 				: 0)
176824e36522SCy Schubert 			: 0;
176924e36522SCy Schubert }
177024e36522SCy Schubert 
177124e36522SCy Schubert /** encode answer consisting of 1 rrset */
177224e36522SCy Schubert static int
rpz_local_encode(struct module_env * env,struct query_info * qinfo,struct edns_data * edns,struct comm_reply * repinfo,sldns_buffer * buf,struct regional * temp,struct ub_packed_rrset_key * rrset,int ansec,int rcode,struct ub_packed_rrset_key * soa_rrset)177324e36522SCy Schubert rpz_local_encode(struct module_env* env, struct query_info* qinfo,
177424e36522SCy Schubert 	struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
177524e36522SCy Schubert 	struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec,
177624e36522SCy Schubert 	int rcode, struct ub_packed_rrset_key* soa_rrset)
177724e36522SCy Schubert {
177824e36522SCy Schubert 	struct reply_info rep;
177924e36522SCy Schubert 	uint16_t udpsize;
178024e36522SCy Schubert 	struct ub_packed_rrset_key* rrsetlist[3];
178124e36522SCy Schubert 
178224e36522SCy Schubert 	memset(&rep, 0, sizeof(rep));
178324e36522SCy Schubert 	rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode);
178424e36522SCy Schubert 	rep.qdcount = 1;
178524e36522SCy Schubert 	rep.rrset_count = ansec;
178624e36522SCy Schubert 	rep.rrsets = rrsetlist;
178724e36522SCy Schubert 	if(ansec > 0) {
178824e36522SCy Schubert 		rep.an_numrrsets = 1;
178924e36522SCy Schubert 		rep.rrsets[0] = rrset;
179024e36522SCy Schubert 		rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0];
179124e36522SCy Schubert 	}
179224e36522SCy Schubert 	if(soa_rrset != NULL) {
179324e36522SCy Schubert 		rep.ar_numrrsets = 1;
179424e36522SCy Schubert 		rep.rrsets[rep.rrset_count] = soa_rrset;
179524e36522SCy Schubert 		rep.rrset_count ++;
179624e36522SCy Schubert 		if(rep.ttl < ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]) {
179724e36522SCy Schubert 			rep.ttl = ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0];
179824e36522SCy Schubert 		}
179924e36522SCy Schubert 	}
180024e36522SCy Schubert 
180124e36522SCy Schubert 	udpsize = edns->udp_size;
180224e36522SCy Schubert 	edns->edns_version = EDNS_ADVERTISED_VERSION;
180324e36522SCy Schubert 	edns->udp_size = EDNS_ADVERTISED_SIZE;
180424e36522SCy Schubert 	edns->ext_rcode = 0;
180524e36522SCy Schubert 	edns->bits &= EDNS_DO;
180624e36522SCy Schubert 	if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,
180724e36522SCy Schubert 		repinfo, temp, env->now_tv) ||
180824e36522SCy Schubert 	  !reply_info_answer_encode(qinfo, &rep,
180924e36522SCy Schubert 		*(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2),
181024e36522SCy Schubert 		buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) {
181124e36522SCy Schubert 		error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
181224e36522SCy Schubert 			*(uint16_t*)sldns_buffer_begin(buf),
181324e36522SCy Schubert 			sldns_buffer_read_u16_at(buf, 2), edns);
181424e36522SCy Schubert 	}
181524e36522SCy Schubert 
181624e36522SCy Schubert 	return 1;
181724e36522SCy Schubert }
181824e36522SCy Schubert 
181924e36522SCy Schubert /** allocate SOA record ubrrsetkey in region */
182024e36522SCy Schubert static struct ub_packed_rrset_key*
make_soa_ubrrset(struct auth_zone * auth_zone,struct auth_rrset * soa,struct regional * temp)182124e36522SCy Schubert make_soa_ubrrset(struct auth_zone* auth_zone, struct auth_rrset* soa,
182224e36522SCy Schubert 	struct regional* temp)
182324e36522SCy Schubert {
182424e36522SCy Schubert 	struct ub_packed_rrset_key csoa;
182524e36522SCy Schubert 	if(!soa)
182624e36522SCy Schubert 		return NULL;
182724e36522SCy Schubert 	memset(&csoa, 0, sizeof(csoa));
182824e36522SCy Schubert 	csoa.entry.key = &csoa;
182924e36522SCy Schubert 	csoa.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
183024e36522SCy Schubert 	csoa.rk.type = htons(LDNS_RR_TYPE_SOA);
183124e36522SCy Schubert 	csoa.rk.flags |= PACKED_RRSET_FIXEDTTL
183224e36522SCy Schubert 		| PACKED_RRSET_RPZ;
183324e36522SCy Schubert 	csoa.rk.dname = auth_zone->name;
183424e36522SCy Schubert 	csoa.rk.dname_len = auth_zone->namelen;
183524e36522SCy Schubert 	csoa.entry.hash = rrset_key_hash(&csoa.rk);
183624e36522SCy Schubert 	csoa.entry.data = soa->data;
183724e36522SCy Schubert 	return respip_copy_rrset(&csoa, temp);
183824e36522SCy Schubert }
183924e36522SCy Schubert 
184024e36522SCy Schubert static void
rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr * raddr,struct module_env * env,struct query_info * qinfo,struct edns_data * edns,struct comm_reply * repinfo,sldns_buffer * buf,struct regional * temp,struct auth_zone * auth_zone)184124e36522SCy Schubert rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr,
184224e36522SCy Schubert 	struct module_env* env, struct query_info* qinfo,
184324e36522SCy Schubert 	struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
184424e36522SCy Schubert 	struct regional* temp, struct auth_zone* auth_zone)
184524e36522SCy Schubert {
184624e36522SCy Schubert 	struct local_rrset* rrset;
184724e36522SCy Schubert 	enum rpz_action action = RPZ_INVALID_ACTION;
184824e36522SCy Schubert 	struct ub_packed_rrset_key* rp = NULL;
184924e36522SCy Schubert 	struct ub_packed_rrset_key* rsoa = NULL;
185024e36522SCy Schubert 	int rcode = LDNS_RCODE_NOERROR|BIT_AA;
185124e36522SCy Schubert 	int rrset_count = 1;
185224e36522SCy Schubert 
185324e36522SCy Schubert 	/* prepare synthesized answer for client */
185424e36522SCy Schubert 	action = raddr->action;
185524e36522SCy Schubert 	if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) {
185624e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: bug: local-data action but no local data");
185724e36522SCy Schubert 		return;
185824e36522SCy Schubert 	}
185924e36522SCy Schubert 
186024e36522SCy Schubert 	/* check query type / rr type */
1861335c7cdaSCy Schubert 	rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr, 1);
186224e36522SCy Schubert 	if(rrset == NULL) {
186324e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: unable to find local-data for query");
186424e36522SCy Schubert 		rrset_count = 0;
186524e36522SCy Schubert 		goto nodata;
186624e36522SCy Schubert 	}
186724e36522SCy Schubert 
186824e36522SCy Schubert 	rp = respip_copy_rrset(rrset->rrset, temp);
186924e36522SCy Schubert 	if(!rp) {
187024e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: local data action: out of memory");
187124e36522SCy Schubert 		return;
187224e36522SCy Schubert 	}
187324e36522SCy Schubert 
187424e36522SCy Schubert 	rp->rk.flags |= PACKED_RRSET_FIXEDTTL | PACKED_RRSET_RPZ;
187524e36522SCy Schubert 	rp->rk.dname = qinfo->qname;
187624e36522SCy Schubert 	rp->rk.dname_len = qinfo->qname_len;
187724e36522SCy Schubert 	rp->entry.hash = rrset_key_hash(&rp->rk);
187824e36522SCy Schubert nodata:
187924e36522SCy Schubert 	if(auth_zone) {
188024e36522SCy Schubert 		struct auth_rrset* soa = NULL;
188124e36522SCy Schubert 		soa = auth_zone_get_soa_rrset(auth_zone);
188224e36522SCy Schubert 		if(soa) {
188324e36522SCy Schubert 			rsoa = make_soa_ubrrset(auth_zone, soa, temp);
188424e36522SCy Schubert 			if(!rsoa) {
188524e36522SCy Schubert 				verbose(VERB_ALGO, "rpz: local data action soa: out of memory");
188624e36522SCy Schubert 				return;
188724e36522SCy Schubert 			}
188824e36522SCy Schubert 		}
188924e36522SCy Schubert 	}
189024e36522SCy Schubert 
189124e36522SCy Schubert 	rpz_local_encode(env, qinfo, edns, repinfo, buf, temp, rp,
189224e36522SCy Schubert 		rrset_count, rcode, rsoa);
189324e36522SCy Schubert }
189424e36522SCy Schubert 
1895335c7cdaSCy Schubert /** Apply the cname override action, during worker request callback.
1896335c7cdaSCy Schubert  * false on failure. */
1897335c7cdaSCy Schubert static int
rpz_apply_cname_override_action(struct rpz * r,struct query_info * qinfo,struct regional * temp)1898335c7cdaSCy Schubert rpz_apply_cname_override_action(struct rpz* r,
1899335c7cdaSCy Schubert 	struct query_info* qinfo, struct regional* temp)
1900335c7cdaSCy Schubert {
1901335c7cdaSCy Schubert 	if(!r)
1902335c7cdaSCy Schubert 		return 0;
1903335c7cdaSCy Schubert 	qinfo->local_alias = regional_alloc_zero(temp,
1904335c7cdaSCy Schubert 		sizeof(struct local_rrset));
1905335c7cdaSCy Schubert 	if(qinfo->local_alias == NULL)
1906335c7cdaSCy Schubert 		return 0; /* out of memory */
1907335c7cdaSCy Schubert 	qinfo->local_alias->rrset = respip_copy_rrset(r->cname_override, temp);
1908335c7cdaSCy Schubert 	if(qinfo->local_alias->rrset == NULL) {
1909335c7cdaSCy Schubert 		qinfo->local_alias = NULL;
1910335c7cdaSCy Schubert 		return 0; /* out of memory */
1911335c7cdaSCy Schubert 	}
1912335c7cdaSCy Schubert 	qinfo->local_alias->rrset->rk.dname = qinfo->qname;
1913335c7cdaSCy Schubert 	qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
1914335c7cdaSCy Schubert 	return 1;
1915335c7cdaSCy Schubert }
1916335c7cdaSCy Schubert 
191724e36522SCy Schubert /** add additional section SOA record to the reply.
191824e36522SCy Schubert  * Since this gets fed into the normal iterator answer creation, it
191924e36522SCy Schubert  * gets minimal-responses applied to it, that can remove the additional SOA
192024e36522SCy Schubert  * again. */
192124e36522SCy Schubert static int
rpz_add_soa(struct reply_info * rep,struct module_qstate * ms,struct auth_zone * az)192224e36522SCy Schubert rpz_add_soa(struct reply_info* rep, struct module_qstate* ms,
192324e36522SCy Schubert 	struct auth_zone* az)
192424e36522SCy Schubert {
192524e36522SCy Schubert 	struct auth_rrset* soa = NULL;
192624e36522SCy Schubert 	struct ub_packed_rrset_key* rsoa = NULL;
192724e36522SCy Schubert 	struct ub_packed_rrset_key** prevrrsets;
192824e36522SCy Schubert 	if(!az) return 1;
192924e36522SCy Schubert 	soa = auth_zone_get_soa_rrset(az);
193024e36522SCy Schubert 	if(!soa) return 1;
193124e36522SCy Schubert 	if(!rep) return 0;
193224e36522SCy Schubert 	rsoa = make_soa_ubrrset(az, soa, ms->region);
193324e36522SCy Schubert 	if(!rsoa) return 0;
193424e36522SCy Schubert 	prevrrsets = rep->rrsets;
193524e36522SCy Schubert 	rep->rrsets = regional_alloc_zero(ms->region,
193624e36522SCy Schubert 		sizeof(*rep->rrsets)*(rep->rrset_count+1));
193724e36522SCy Schubert 	if(!rep->rrsets)
193824e36522SCy Schubert 		return 0;
193924e36522SCy Schubert 	if(prevrrsets && rep->rrset_count > 0)
194024e36522SCy Schubert 		memcpy(rep->rrsets, prevrrsets, rep->rrset_count*sizeof(*rep->rrsets));
194124e36522SCy Schubert 	rep->rrset_count++;
194224e36522SCy Schubert 	rep->ar_numrrsets++;
194324e36522SCy Schubert 	rep->rrsets[rep->rrset_count-1] = rsoa;
194424e36522SCy Schubert 	return 1;
194524e36522SCy Schubert }
194624e36522SCy Schubert 
194724e36522SCy Schubert static inline struct dns_msg*
rpz_dns_msg_new(struct regional * region)194824e36522SCy Schubert rpz_dns_msg_new(struct regional* region)
194924e36522SCy Schubert {
195024e36522SCy Schubert 	struct dns_msg* msg =
195124e36522SCy Schubert 			(struct dns_msg*)regional_alloc(region,
195224e36522SCy Schubert 							sizeof(struct dns_msg));
195324e36522SCy Schubert 	if(msg == NULL) { return NULL; }
195424e36522SCy Schubert 	memset(msg, 0, sizeof(struct dns_msg));
195524e36522SCy Schubert 
195624e36522SCy Schubert 	return msg;
195724e36522SCy Schubert }
195824e36522SCy Schubert 
195924e36522SCy Schubert static inline struct dns_msg*
rpz_synthesize_nodata(struct rpz * ATTR_UNUSED (r),struct module_qstate * ms,struct query_info * qinfo,struct auth_zone * az)196024e36522SCy Schubert rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
196124e36522SCy Schubert 	struct query_info* qinfo, struct auth_zone* az)
196224e36522SCy Schubert {
196324e36522SCy Schubert 	struct dns_msg* msg = rpz_dns_msg_new(ms->region);
196424e36522SCy Schubert 	if(msg == NULL) { return msg; }
196524e36522SCy Schubert 	msg->qinfo = *qinfo;
196624e36522SCy Schubert 	msg->rep = construct_reply_info_base(ms->region,
19679cf5bc93SCy Schubert 					     LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
196824e36522SCy Schubert 					     1, /* qd */
196924e36522SCy Schubert 					     0, /* ttl */
197024e36522SCy Schubert 					     0, /* prettl */
197124e36522SCy Schubert 					     0, /* expttl */
1972*46d2f618SCy Schubert 					     0, /* norecttl */
197324e36522SCy Schubert 					     0, /* an */
197424e36522SCy Schubert 					     0, /* ns */
197524e36522SCy Schubert 					     0, /* ar */
197624e36522SCy Schubert 					     0, /* total */
19778f76bb7dSCy Schubert 					     sec_status_insecure,
19788f76bb7dSCy Schubert 					     LDNS_EDE_NONE);
197924e36522SCy Schubert 	if(msg->rep)
198024e36522SCy Schubert 		msg->rep->authoritative = 1;
198124e36522SCy Schubert 	if(!rpz_add_soa(msg->rep, ms, az))
198224e36522SCy Schubert 		return NULL;
198324e36522SCy Schubert 	return msg;
198424e36522SCy Schubert }
198524e36522SCy Schubert 
198624e36522SCy Schubert static inline struct dns_msg*
rpz_synthesize_nxdomain(struct rpz * r,struct module_qstate * ms,struct query_info * qinfo,struct auth_zone * az)19879cf5bc93SCy Schubert rpz_synthesize_nxdomain(struct rpz* r, struct module_qstate* ms,
198824e36522SCy Schubert 	struct query_info* qinfo, struct auth_zone* az)
198924e36522SCy Schubert {
199024e36522SCy Schubert 	struct dns_msg* msg = rpz_dns_msg_new(ms->region);
19919cf5bc93SCy Schubert 	uint16_t flags;
199224e36522SCy Schubert 	if(msg == NULL) { return msg; }
199324e36522SCy Schubert 	msg->qinfo = *qinfo;
19949cf5bc93SCy Schubert 	flags = LDNS_RCODE_NXDOMAIN | BIT_QR | BIT_AA | BIT_RA;
19959cf5bc93SCy Schubert 	if(r->signal_nxdomain_ra)
19969cf5bc93SCy Schubert 		flags &= ~BIT_RA;
199724e36522SCy Schubert 	msg->rep = construct_reply_info_base(ms->region,
19989cf5bc93SCy Schubert 					     flags,
199924e36522SCy Schubert 					     1, /* qd */
200024e36522SCy Schubert 					     0, /* ttl */
200124e36522SCy Schubert 					     0, /* prettl */
200224e36522SCy Schubert 					     0, /* expttl */
2003*46d2f618SCy Schubert 					     0, /* norecttl */
200424e36522SCy Schubert 					     0, /* an */
200524e36522SCy Schubert 					     0, /* ns */
200624e36522SCy Schubert 					     0, /* ar */
200724e36522SCy Schubert 					     0, /* total */
20088f76bb7dSCy Schubert 					     sec_status_insecure,
20098f76bb7dSCy Schubert 					     LDNS_EDE_NONE);
201024e36522SCy Schubert 	if(msg->rep)
201124e36522SCy Schubert 		msg->rep->authoritative = 1;
201224e36522SCy Schubert 	if(!rpz_add_soa(msg->rep, ms, az))
201324e36522SCy Schubert 		return NULL;
201424e36522SCy Schubert 	return msg;
201524e36522SCy Schubert }
201624e36522SCy Schubert 
201724e36522SCy Schubert static inline struct dns_msg*
rpz_synthesize_localdata_from_rrset(struct rpz * ATTR_UNUSED (r),struct module_qstate * ms,struct query_info * qi,struct local_rrset * rrset,struct auth_zone * az)201824e36522SCy Schubert rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
201924e36522SCy Schubert 	struct query_info* qi, struct local_rrset* rrset, struct auth_zone* az)
202024e36522SCy Schubert {
202124e36522SCy Schubert 	struct dns_msg* msg = NULL;
202224e36522SCy Schubert 	struct reply_info* new_reply_info;
202324e36522SCy Schubert 	struct ub_packed_rrset_key* rp;
202424e36522SCy Schubert 
202524e36522SCy Schubert 
202624e36522SCy Schubert 	msg = rpz_dns_msg_new(ms->region);
202724e36522SCy Schubert 	if(msg == NULL) { return NULL; }
202824e36522SCy Schubert 
2029335c7cdaSCy Schubert 	msg->qinfo = *qi;
203024e36522SCy Schubert         new_reply_info = construct_reply_info_base(ms->region,
20319cf5bc93SCy Schubert                                                    LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
203224e36522SCy Schubert                                                    1, /* qd */
203324e36522SCy Schubert                                                    0, /* ttl */
203424e36522SCy Schubert                                                    0, /* prettl */
203524e36522SCy Schubert                                                    0, /* expttl */
2036*46d2f618SCy Schubert                                                    0, /* norecttl */
203724e36522SCy Schubert                                                    1, /* an */
203824e36522SCy Schubert                                                    0, /* ns */
203924e36522SCy Schubert                                                    0, /* ar */
204024e36522SCy Schubert                                                    1, /* total */
20418f76bb7dSCy Schubert                                                    sec_status_insecure,
20428f76bb7dSCy Schubert                                                    LDNS_EDE_NONE);
204324e36522SCy Schubert 	if(new_reply_info == NULL) {
204424e36522SCy Schubert 		log_err("out of memory");
204524e36522SCy Schubert 		return NULL;
204624e36522SCy Schubert 	}
204724e36522SCy Schubert 	new_reply_info->authoritative = 1;
204824e36522SCy Schubert 	rp = respip_copy_rrset(rrset->rrset, ms->region);
204924e36522SCy Schubert 	if(rp == NULL) {
205024e36522SCy Schubert 		log_err("out of memory");
205124e36522SCy Schubert 		return NULL;
205224e36522SCy Schubert 	}
205324e36522SCy Schubert 	rp->rk.dname = qi->qname;
205424e36522SCy Schubert 	rp->rk.dname_len = qi->qname_len;
205524e36522SCy Schubert 	/* this rrset is from the rpz data, or synthesized.
205624e36522SCy Schubert 	 * It is not actually from the network, so we flag it with this
205724e36522SCy Schubert 	 * flags as a fake RRset. If later the cache is used to look up
205824e36522SCy Schubert 	 * rrsets, then the fake ones are not returned (if you look without
205924e36522SCy Schubert 	 * the flag). For like CNAME lookups from the iterator or A, AAAA
206024e36522SCy Schubert 	 * lookups for nameserver targets, it would use the without flag
206124e36522SCy Schubert 	 * actual data. So that the actual network data and fake data
206224e36522SCy Schubert 	 * are kept track of separately. */
206324e36522SCy Schubert 	rp->rk.flags |= PACKED_RRSET_RPZ;
206424e36522SCy Schubert 	new_reply_info->rrsets[0] = rp;
206524e36522SCy Schubert 	msg->rep = new_reply_info;
206624e36522SCy Schubert 	if(!rpz_add_soa(msg->rep, ms, az))
206724e36522SCy Schubert 		return NULL;
206824e36522SCy Schubert 	return msg;
206924e36522SCy Schubert }
207024e36522SCy Schubert 
207124e36522SCy Schubert static inline struct dns_msg*
rpz_synthesize_nsip_localdata(struct rpz * r,struct module_qstate * ms,struct query_info * qi,struct clientip_synthesized_rr * data,struct auth_zone * az)207224e36522SCy Schubert rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms,
2073335c7cdaSCy Schubert 	struct query_info* qi, struct clientip_synthesized_rr* data,
2074335c7cdaSCy Schubert 	struct auth_zone* az)
207524e36522SCy Schubert {
207624e36522SCy Schubert 	struct local_rrset* rrset;
207724e36522SCy Schubert 
2078335c7cdaSCy Schubert 	rrset = rpz_find_synthesized_rrset(qi->qtype, data, 1);
207924e36522SCy Schubert 	if(rrset == NULL) {
208024e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: nsip: no matching local data found");
208124e36522SCy Schubert 		return NULL;
208224e36522SCy Schubert 	}
208324e36522SCy Schubert 
2084335c7cdaSCy Schubert 	return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az);
208524e36522SCy Schubert }
208624e36522SCy Schubert 
208724e36522SCy Schubert /* copy'n'paste from localzone.c */
208824e36522SCy Schubert static struct local_rrset*
local_data_find_type(struct local_data * data,uint16_t type,int alias_ok)208924e36522SCy Schubert local_data_find_type(struct local_data* data, uint16_t type, int alias_ok)
209024e36522SCy Schubert {
2091335c7cdaSCy Schubert 	struct local_rrset* p, *cname = NULL;
209224e36522SCy Schubert 	type = htons(type);
209324e36522SCy Schubert 	for(p = data->rrsets; p; p = p->next) {
209424e36522SCy Schubert 		if(p->rrset->rk.type == type)
209524e36522SCy Schubert 			return p;
209624e36522SCy Schubert 		if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME))
2097335c7cdaSCy Schubert 			cname = p;
209824e36522SCy Schubert 	}
2099335c7cdaSCy Schubert 	if(alias_ok)
2100335c7cdaSCy Schubert 		return cname;
210124e36522SCy Schubert 	return NULL;
210224e36522SCy Schubert }
210324e36522SCy Schubert 
210424e36522SCy Schubert /* based on localzone.c:local_data_answer() */
210524e36522SCy Schubert static inline struct dns_msg*
rpz_synthesize_nsdname_localdata(struct rpz * r,struct module_qstate * ms,struct query_info * qi,struct local_zone * z,struct matched_delegation_point const * match,struct auth_zone * az)210624e36522SCy Schubert rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms,
2107335c7cdaSCy Schubert 	struct query_info* qi, struct local_zone* z,
2108335c7cdaSCy Schubert 	struct matched_delegation_point const* match, struct auth_zone* az)
210924e36522SCy Schubert {
211024e36522SCy Schubert 	struct local_data key;
211124e36522SCy Schubert 	struct local_data* ld;
211224e36522SCy Schubert 	struct local_rrset* rrset;
211324e36522SCy Schubert 
211424e36522SCy Schubert 	if(match->dname == NULL) { return NULL; }
211524e36522SCy Schubert 
211624e36522SCy Schubert 	key.node.key = &key;
211724e36522SCy Schubert 	key.name = match->dname;
211824e36522SCy Schubert 	key.namelen = match->dname_len;
211924e36522SCy Schubert 	key.namelabs = dname_count_labels(match->dname);
212024e36522SCy Schubert 
212124e36522SCy Schubert 	rpz_log_dname("nsdname local data", key.name, key.namelen);
212224e36522SCy Schubert 
212324e36522SCy Schubert 	ld = (struct local_data*)rbtree_search(&z->data, &key.node);
212424e36522SCy Schubert 	if(ld == NULL) {
212524e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: nsdname: impossible: qname not found");
212624e36522SCy Schubert 		return NULL;
212724e36522SCy Schubert 	}
212824e36522SCy Schubert 
2129335c7cdaSCy Schubert 	rrset = local_data_find_type(ld, qi->qtype, 1);
213024e36522SCy Schubert 	if(rrset == NULL) {
213124e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: nsdname: no matching local data found");
213224e36522SCy Schubert 		return NULL;
213324e36522SCy Schubert 	}
213424e36522SCy Schubert 
2135335c7cdaSCy Schubert 	return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az);
213624e36522SCy Schubert }
213724e36522SCy Schubert 
213824e36522SCy Schubert /* like local_data_answer for qname triggers after a cname */
213924e36522SCy Schubert static struct dns_msg*
rpz_synthesize_qname_localdata_msg(struct rpz * r,struct module_qstate * ms,struct query_info * qinfo,struct local_zone * z,struct auth_zone * az)214024e36522SCy Schubert rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms,
214124e36522SCy Schubert 	struct query_info* qinfo, struct local_zone* z, struct auth_zone* az)
214224e36522SCy Schubert {
214324e36522SCy Schubert 	struct local_data key;
214424e36522SCy Schubert 	struct local_data* ld;
214524e36522SCy Schubert 	struct local_rrset* rrset;
214624e36522SCy Schubert 	key.node.key = &key;
214724e36522SCy Schubert 	key.name = qinfo->qname;
214824e36522SCy Schubert 	key.namelen = qinfo->qname_len;
214924e36522SCy Schubert 	key.namelabs = dname_count_labels(qinfo->qname);
215024e36522SCy Schubert 	ld = (struct local_data*)rbtree_search(&z->data, &key.node);
215124e36522SCy Schubert 	if(ld == NULL) {
2152335c7cdaSCy Schubert 		verbose(VERB_ALGO, "rpz: qname: name not found");
215324e36522SCy Schubert 		return NULL;
215424e36522SCy Schubert 	}
215524e36522SCy Schubert 	rrset = local_data_find_type(ld, qinfo->qtype, 1);
215624e36522SCy Schubert 	if(rrset == NULL) {
2157335c7cdaSCy Schubert 		verbose(VERB_ALGO, "rpz: qname: type not found");
215824e36522SCy Schubert 		return NULL;
215924e36522SCy Schubert 	}
216024e36522SCy Schubert 	return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az);
216124e36522SCy Schubert }
216224e36522SCy Schubert 
2163335c7cdaSCy Schubert /** Synthesize a CNAME message for RPZ action override */
2164335c7cdaSCy Schubert static struct dns_msg*
rpz_synthesize_cname_override_msg(struct rpz * r,struct module_qstate * ms,struct query_info * qinfo)2165335c7cdaSCy Schubert rpz_synthesize_cname_override_msg(struct rpz* r, struct module_qstate* ms,
2166335c7cdaSCy Schubert 	struct query_info* qinfo)
2167335c7cdaSCy Schubert {
2168335c7cdaSCy Schubert 	struct dns_msg* msg = NULL;
2169335c7cdaSCy Schubert 	struct reply_info* new_reply_info;
2170335c7cdaSCy Schubert 	struct ub_packed_rrset_key* rp;
2171335c7cdaSCy Schubert 
2172335c7cdaSCy Schubert 	msg = rpz_dns_msg_new(ms->region);
2173335c7cdaSCy Schubert 	if(msg == NULL) { return NULL; }
2174335c7cdaSCy Schubert 
2175335c7cdaSCy Schubert 	msg->qinfo = *qinfo;
2176335c7cdaSCy Schubert         new_reply_info = construct_reply_info_base(ms->region,
2177335c7cdaSCy Schubert                                                    LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
2178335c7cdaSCy Schubert                                                    1, /* qd */
2179335c7cdaSCy Schubert                                                    0, /* ttl */
2180335c7cdaSCy Schubert                                                    0, /* prettl */
2181335c7cdaSCy Schubert                                                    0, /* expttl */
2182*46d2f618SCy Schubert                                                    0, /* norecttl */
2183335c7cdaSCy Schubert                                                    1, /* an */
2184335c7cdaSCy Schubert                                                    0, /* ns */
2185335c7cdaSCy Schubert                                                    0, /* ar */
2186335c7cdaSCy Schubert                                                    1, /* total */
2187335c7cdaSCy Schubert                                                    sec_status_insecure,
2188335c7cdaSCy Schubert                                                    LDNS_EDE_NONE);
2189335c7cdaSCy Schubert 	if(new_reply_info == NULL) {
2190335c7cdaSCy Schubert 		log_err("out of memory");
2191335c7cdaSCy Schubert 		return NULL;
2192335c7cdaSCy Schubert 	}
2193335c7cdaSCy Schubert 	new_reply_info->authoritative = 1;
2194335c7cdaSCy Schubert 
2195335c7cdaSCy Schubert 	rp = respip_copy_rrset(r->cname_override, ms->region);
2196335c7cdaSCy Schubert 	if(rp == NULL) {
2197335c7cdaSCy Schubert 		log_err("out of memory");
2198335c7cdaSCy Schubert 		return NULL;
2199335c7cdaSCy Schubert 	}
2200335c7cdaSCy Schubert 	rp->rk.dname = qinfo->qname;
2201335c7cdaSCy Schubert 	rp->rk.dname_len = qinfo->qname_len;
2202335c7cdaSCy Schubert 	/* this rrset is from the rpz data, or synthesized.
2203335c7cdaSCy Schubert 	 * It is not actually from the network, so we flag it with this
2204335c7cdaSCy Schubert 	 * flags as a fake RRset. If later the cache is used to look up
2205335c7cdaSCy Schubert 	 * rrsets, then the fake ones are not returned (if you look without
2206335c7cdaSCy Schubert 	 * the flag). For like CNAME lookups from the iterator or A, AAAA
2207335c7cdaSCy Schubert 	 * lookups for nameserver targets, it would use the without flag
2208335c7cdaSCy Schubert 	 * actual data. So that the actual network data and fake data
2209335c7cdaSCy Schubert 	 * are kept track of separately. */
2210335c7cdaSCy Schubert 	rp->rk.flags |= PACKED_RRSET_RPZ;
2211335c7cdaSCy Schubert 	new_reply_info->rrsets[0] = rp;
2212335c7cdaSCy Schubert 
2213335c7cdaSCy Schubert 	msg->rep = new_reply_info;
2214335c7cdaSCy Schubert 	return msg;
2215335c7cdaSCy Schubert }
2216335c7cdaSCy Schubert 
221724e36522SCy Schubert static int
rpz_synthesize_qname_localdata(struct module_env * env,struct rpz * r,struct local_zone * z,enum localzone_type lzt,struct query_info * qinfo,struct edns_data * edns,sldns_buffer * buf,struct regional * temp,struct comm_reply * repinfo,struct ub_server_stats * stats)221824e36522SCy Schubert rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r,
221924e36522SCy Schubert 	struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo,
222024e36522SCy Schubert 	struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
222124e36522SCy Schubert 	struct comm_reply* repinfo, struct ub_server_stats* stats)
222224e36522SCy Schubert {
222324e36522SCy Schubert 	struct local_data* ld = NULL;
222424e36522SCy Schubert 	int ret = 0;
222524e36522SCy Schubert 	if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
2226335c7cdaSCy Schubert 		if(!rpz_apply_cname_override_action(r, qinfo, temp))
2227335c7cdaSCy Schubert 			return 0;
222824e36522SCy Schubert 		if(r->log) {
222924e36522SCy Schubert 			log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION,
223024e36522SCy Schubert 				      qinfo, repinfo, NULL, r->log_name);
223124e36522SCy Schubert 		}
2232091e9e46SCy Schubert 		stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
2233091e9e46SCy Schubert 		return 0;
2234091e9e46SCy Schubert 	}
2235091e9e46SCy Schubert 
2236091e9e46SCy Schubert 	if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo,
2237091e9e46SCy Schubert 		edns, repinfo, buf, temp, dname_count_labels(qinfo->qname),
2238091e9e46SCy Schubert 		&ld, lzt, -1, NULL, 0, NULL, 0)) {
223924e36522SCy Schubert 		if(r->log) {
224024e36522SCy Schubert 			log_rpz_apply("qname", z->name, NULL,
2241091e9e46SCy Schubert 				localzone_type_to_rpz_action(lzt), qinfo,
224224e36522SCy Schubert 				repinfo, NULL, r->log_name);
224324e36522SCy Schubert 		}
2244091e9e46SCy Schubert 		stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
2245091e9e46SCy Schubert 		return !qinfo->local_alias;
2246091e9e46SCy Schubert 	}
2247091e9e46SCy Schubert 
2248091e9e46SCy Schubert 	ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp,
2249091e9e46SCy Schubert 		0 /* no local data used */, lzt);
22509cf5bc93SCy Schubert 	if(r->signal_nxdomain_ra && LDNS_RCODE_WIRE(sldns_buffer_begin(buf))
22519cf5bc93SCy Schubert 		== LDNS_RCODE_NXDOMAIN)
22529cf5bc93SCy Schubert 		LDNS_RA_CLR(sldns_buffer_begin(buf));
225324e36522SCy Schubert 	if(r->log) {
225424e36522SCy Schubert 		log_rpz_apply("qname", z->name, NULL, localzone_type_to_rpz_action(lzt),
225524e36522SCy Schubert 			      qinfo, repinfo, NULL, r->log_name);
225624e36522SCy Schubert 	}
2257091e9e46SCy Schubert 	stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
225824e36522SCy Schubert 	return ret;
225924e36522SCy Schubert }
226024e36522SCy Schubert 
22619cf5bc93SCy Schubert static struct clientip_synthesized_rr*
rpz_delegation_point_ipbased_trigger_lookup(struct rpz * rpz,struct iter_qstate * is)226224e36522SCy Schubert rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* is)
226324e36522SCy Schubert {
226424e36522SCy Schubert 	struct delegpt_addr* cursor;
226524e36522SCy Schubert 	struct clientip_synthesized_rr* action = NULL;
226624e36522SCy Schubert 	if(is->dp == NULL) { return NULL; }
226724e36522SCy Schubert 	for(cursor = is->dp->target_list;
226824e36522SCy Schubert 	    cursor != NULL;
226924e36522SCy Schubert 	    cursor = cursor->next_target) {
227024e36522SCy Schubert 		if(cursor->bogus) { continue; }
227124e36522SCy Schubert 		action = rpz_ipbased_trigger_lookup(rpz->ns_set, &cursor->addr,
227224e36522SCy Schubert 						    cursor->addrlen, "nsip");
227324e36522SCy Schubert 		if(action != NULL) { return action; }
227424e36522SCy Schubert 	}
227524e36522SCy Schubert 	return NULL;
227624e36522SCy Schubert }
227724e36522SCy Schubert 
22789cf5bc93SCy Schubert static struct dns_msg*
rpz_apply_nsip_trigger(struct module_qstate * ms,struct query_info * qchase,struct rpz * r,struct clientip_synthesized_rr * raddr,struct auth_zone * az)2279335c7cdaSCy Schubert rpz_apply_nsip_trigger(struct module_qstate* ms, struct query_info* qchase,
2280335c7cdaSCy Schubert 	struct rpz* r, struct clientip_synthesized_rr* raddr,
2281335c7cdaSCy Schubert 	struct auth_zone* az)
228224e36522SCy Schubert {
228324e36522SCy Schubert 	enum rpz_action action = raddr->action;
228424e36522SCy Schubert 	struct dns_msg* ret = NULL;
228524e36522SCy Schubert 
228624e36522SCy Schubert 	if(r->action_override != RPZ_NO_OVERRIDE_ACTION) {
228724e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)",
228824e36522SCy Schubert 			rpz_action_to_string(r->action_override), rpz_action_to_string(action));
228924e36522SCy Schubert 		action = r->action_override;
229024e36522SCy Schubert 	}
229124e36522SCy Schubert 
229224e36522SCy Schubert 	if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) {
229324e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data");
2294335c7cdaSCy Schubert 		ret = rpz_synthesize_nodata(r, ms, qchase, az);
2295*46d2f618SCy Schubert 		ms->rpz_applied = 1;
229624e36522SCy Schubert 		goto done;
229724e36522SCy Schubert 	}
229824e36522SCy Schubert 
229924e36522SCy Schubert 	switch(action) {
230024e36522SCy Schubert 	case RPZ_NXDOMAIN_ACTION:
2301335c7cdaSCy Schubert 		ret = rpz_synthesize_nxdomain(r, ms, qchase, az);
2302*46d2f618SCy Schubert 		ms->rpz_applied = 1;
230324e36522SCy Schubert 		break;
230424e36522SCy Schubert 	case RPZ_NODATA_ACTION:
2305335c7cdaSCy Schubert 		ret = rpz_synthesize_nodata(r, ms, qchase, az);
2306*46d2f618SCy Schubert 		ms->rpz_applied = 1;
230724e36522SCy Schubert 		break;
230824e36522SCy Schubert 	case RPZ_TCP_ONLY_ACTION:
230924e36522SCy Schubert 		/* basically a passthru here but the tcp-only will be
231024e36522SCy Schubert 		 * honored before the query gets sent. */
2311103ba509SCy Schubert 		ms->tcp_required = 1;
231224e36522SCy Schubert 		ret = NULL;
231324e36522SCy Schubert 		break;
231424e36522SCy Schubert 	case RPZ_DROP_ACTION:
2315335c7cdaSCy Schubert 		ret = rpz_synthesize_nodata(r, ms, qchase, az);
2316*46d2f618SCy Schubert 		ms->rpz_applied = 1;
231724e36522SCy Schubert 		ms->is_drop = 1;
231824e36522SCy Schubert 		break;
231924e36522SCy Schubert 	case RPZ_LOCAL_DATA_ACTION:
2320335c7cdaSCy Schubert 		ret = rpz_synthesize_nsip_localdata(r, ms, qchase, raddr, az);
2321335c7cdaSCy Schubert 		if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); }
2322*46d2f618SCy Schubert 		ms->rpz_applied = 1;
232324e36522SCy Schubert 		break;
232424e36522SCy Schubert 	case RPZ_PASSTHRU_ACTION:
232524e36522SCy Schubert 		ret = NULL;
2326a39a5a69SCy Schubert 		ms->rpz_passthru = 1;
232724e36522SCy Schubert 		break;
2328335c7cdaSCy Schubert 	case RPZ_CNAME_OVERRIDE_ACTION:
2329335c7cdaSCy Schubert 		ret = rpz_synthesize_cname_override_msg(r, ms, qchase);
2330*46d2f618SCy Schubert 		ms->rpz_applied = 1;
2331335c7cdaSCy Schubert 		break;
233224e36522SCy Schubert 	default:
233324e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'",
233424e36522SCy Schubert 			rpz_action_to_string(action));
233524e36522SCy Schubert 		ret = NULL;
233624e36522SCy Schubert 	}
233724e36522SCy Schubert 
233824e36522SCy Schubert done:
233924e36522SCy Schubert 	if(r->log)
234024e36522SCy Schubert 		log_rpz_apply("nsip", NULL, &raddr->node,
234124e36522SCy Schubert 			action, &ms->qinfo, NULL, ms, r->log_name);
234224e36522SCy Schubert 	if(ms->env->worker)
234324e36522SCy Schubert 		ms->env->worker->stats.rpz_action[action]++;
234424e36522SCy Schubert 	lock_rw_unlock(&raddr->lock);
234524e36522SCy Schubert 	return ret;
234624e36522SCy Schubert }
234724e36522SCy Schubert 
23489cf5bc93SCy Schubert static struct dns_msg*
rpz_apply_nsdname_trigger(struct module_qstate * ms,struct query_info * qchase,struct rpz * r,struct local_zone * z,struct matched_delegation_point const * match,struct auth_zone * az)2349335c7cdaSCy Schubert rpz_apply_nsdname_trigger(struct module_qstate* ms, struct query_info* qchase,
2350335c7cdaSCy Schubert 	struct rpz* r, struct local_zone* z,
2351335c7cdaSCy Schubert 	struct matched_delegation_point const* match, struct auth_zone* az)
235224e36522SCy Schubert {
235324e36522SCy Schubert 	struct dns_msg* ret = NULL;
235424e36522SCy Schubert 	enum rpz_action action = localzone_type_to_rpz_action(z->type);
235524e36522SCy Schubert 
235624e36522SCy Schubert 	if(r->action_override != RPZ_NO_OVERRIDE_ACTION) {
235724e36522SCy Schubert 		verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)",
235824e36522SCy Schubert 			rpz_action_to_string(r->action_override), rpz_action_to_string(action));
235924e36522SCy Schubert 		action = r->action_override;
236024e36522SCy Schubert 	}
236124e36522SCy Schubert 
236224e36522SCy Schubert 	switch(action) {
236324e36522SCy Schubert 	case RPZ_NXDOMAIN_ACTION:
2364335c7cdaSCy Schubert 		ret = rpz_synthesize_nxdomain(r, ms, qchase, az);
2365*46d2f618SCy Schubert 		ms->rpz_applied = 1;
236624e36522SCy Schubert 		break;
236724e36522SCy Schubert 	case RPZ_NODATA_ACTION:
2368335c7cdaSCy Schubert 		ret = rpz_synthesize_nodata(r, ms, qchase, az);
2369*46d2f618SCy Schubert 		ms->rpz_applied = 1;
237024e36522SCy Schubert 		break;
237124e36522SCy Schubert 	case RPZ_TCP_ONLY_ACTION:
237224e36522SCy Schubert 		/* basically a passthru here but the tcp-only will be
237324e36522SCy Schubert 		 * honored before the query gets sent. */
2374103ba509SCy Schubert 		ms->tcp_required = 1;
237524e36522SCy Schubert 		ret = NULL;
237624e36522SCy Schubert 		break;
237724e36522SCy Schubert 	case RPZ_DROP_ACTION:
2378335c7cdaSCy Schubert 		ret = rpz_synthesize_nodata(r, ms, qchase, az);
2379*46d2f618SCy Schubert 		ms->rpz_applied = 1;
238024e36522SCy Schubert 		ms->is_drop = 1;
238124e36522SCy Schubert 		break;
238224e36522SCy Schubert 	case RPZ_LOCAL_DATA_ACTION:
2383335c7cdaSCy Schubert 		ret = rpz_synthesize_nsdname_localdata(r, ms, qchase, z, match, az);
2384335c7cdaSCy Schubert 		if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); }
2385*46d2f618SCy Schubert 		ms->rpz_applied = 1;
238624e36522SCy Schubert 		break;
238724e36522SCy Schubert 	case RPZ_PASSTHRU_ACTION:
238824e36522SCy Schubert 		ret = NULL;
2389a39a5a69SCy Schubert 		ms->rpz_passthru = 1;
239024e36522SCy Schubert 		break;
2391335c7cdaSCy Schubert 	case RPZ_CNAME_OVERRIDE_ACTION:
2392335c7cdaSCy Schubert 		ret = rpz_synthesize_cname_override_msg(r, ms, qchase);
2393*46d2f618SCy Schubert 		ms->rpz_applied = 1;
2394335c7cdaSCy Schubert 		break;
239524e36522SCy Schubert 	default:
2396335c7cdaSCy Schubert 		verbose(VERB_ALGO, "rpz: nsdname: bug: unhandled or invalid action: '%s'",
239724e36522SCy Schubert 			rpz_action_to_string(action));
239824e36522SCy Schubert 		ret = NULL;
239924e36522SCy Schubert 	}
240024e36522SCy Schubert 
240124e36522SCy Schubert 	if(r->log)
240224e36522SCy Schubert 		log_rpz_apply("nsdname", match->dname, NULL,
240324e36522SCy Schubert 			action, &ms->qinfo, NULL, ms, r->log_name);
240424e36522SCy Schubert 	if(ms->env->worker)
240524e36522SCy Schubert 		ms->env->worker->stats.rpz_action[action]++;
240624e36522SCy Schubert 	lock_rw_unlock(&z->lock);
240724e36522SCy Schubert 	return ret;
240824e36522SCy Schubert }
240924e36522SCy Schubert 
241024e36522SCy Schubert static struct local_zone*
rpz_delegation_point_zone_lookup(struct delegpt * dp,struct local_zones * zones,uint16_t qclass,struct matched_delegation_point * match)241124e36522SCy Schubert rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones,
241224e36522SCy Schubert 	uint16_t qclass,
241324e36522SCy Schubert 	/* output parameter */
241424e36522SCy Schubert 	struct matched_delegation_point* match)
241524e36522SCy Schubert {
241624e36522SCy Schubert 	struct delegpt_ns* nameserver;
241724e36522SCy Schubert 	struct local_zone* z = NULL;
241824e36522SCy Schubert 
241924e36522SCy Schubert 	/* the rpz specs match the nameserver names (NS records), not the
242024e36522SCy Schubert 	 * name of the delegation point itself, to the nsdname triggers */
242124e36522SCy Schubert 	for(nameserver = dp->nslist;
242224e36522SCy Schubert 	    nameserver != NULL;
242324e36522SCy Schubert 	    nameserver = nameserver->next) {
242424e36522SCy Schubert 		z = rpz_find_zone(zones, nameserver->name, nameserver->namelen,
242524e36522SCy Schubert 				  qclass, 0, 0, 0);
242624e36522SCy Schubert 		if(z != NULL) {
242724e36522SCy Schubert 			match->dname = nameserver->name;
242824e36522SCy Schubert 			match->dname_len = nameserver->namelen;
242924e36522SCy Schubert 			if(verbosity >= VERB_ALGO) {
243024e36522SCy Schubert 				char nm[255+1], zn[255+1];
243124e36522SCy Schubert 				dname_str(match->dname, nm);
243224e36522SCy Schubert 				dname_str(z->name, zn);
243324e36522SCy Schubert 				if(strcmp(nm, zn) != 0)
243424e36522SCy Schubert 					verbose(VERB_ALGO, "rpz: trigger nsdname %s on %s action=%s",
243524e36522SCy Schubert 						zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type)));
243624e36522SCy Schubert 				else
243724e36522SCy Schubert 					verbose(VERB_ALGO, "rpz: trigger nsdname %s action=%s",
243824e36522SCy Schubert 						nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type)));
243924e36522SCy Schubert 			}
244024e36522SCy Schubert 			break;
244124e36522SCy Schubert 		}
244224e36522SCy Schubert 	}
244324e36522SCy Schubert 
244424e36522SCy Schubert 	return z;
244524e36522SCy Schubert }
244624e36522SCy Schubert 
244724e36522SCy Schubert struct dns_msg*
rpz_callback_from_iterator_module(struct module_qstate * ms,struct iter_qstate * is)244824e36522SCy Schubert rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* is)
244924e36522SCy Schubert {
245024e36522SCy Schubert 	struct auth_zones* az;
245124e36522SCy Schubert 	struct auth_zone* a;
245224e36522SCy Schubert 	struct clientip_synthesized_rr* raddr = NULL;
245324e36522SCy Schubert 	struct rpz* r = NULL;
245424e36522SCy Schubert 	struct local_zone* z = NULL;
245524e36522SCy Schubert 	struct matched_delegation_point match = {0};
245624e36522SCy Schubert 
2457a39a5a69SCy Schubert 	if(ms->rpz_passthru) {
2458a39a5a69SCy Schubert 		verbose(VERB_ALGO, "query is rpz_passthru, no further processing");
2459a39a5a69SCy Schubert 		return NULL;
2460a39a5a69SCy Schubert 	}
2461a39a5a69SCy Schubert 
246224e36522SCy Schubert 	if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; }
246324e36522SCy Schubert 
246424e36522SCy Schubert 	az = ms->env->auth_zones;
246556850988SCy Schubert 	lock_rw_rdlock(&az->rpz_lock);
246624e36522SCy Schubert 
246724e36522SCy Schubert 	verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL);
246824e36522SCy Schubert 
246924e36522SCy Schubert 	/* precedence of RPZ works, loosely, like this:
247024e36522SCy Schubert 	 * CNAMEs in order of the CNAME chain. rpzs in the order they are
247124e36522SCy Schubert 	 * configured. In an RPZ: first client-IP addr, then QNAME, then
247224e36522SCy Schubert 	 * response IP, then NSDNAME, then NSIP. Longest match first. Smallest
247324e36522SCy Schubert 	 * one from a set. */
247424e36522SCy Schubert 	/* we use the precedence rules for the topics and triggers that
247524e36522SCy Schubert 	 * are pertinent at this stage of the resolve processing */
247624e36522SCy Schubert 	for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) {
247724e36522SCy Schubert 		lock_rw_rdlock(&a->lock);
247824e36522SCy Schubert 		r = a->rpz;
247924e36522SCy Schubert 		if(r->disabled) {
248024e36522SCy Schubert 			lock_rw_unlock(&a->lock);
248124e36522SCy Schubert 			continue;
248224e36522SCy Schubert 		}
248356850988SCy Schubert 		if(r->taglist && (!ms->client_info ||
248456850988SCy Schubert 			!taglist_intersect(r->taglist, r->taglistlen,
248556850988SCy Schubert 				ms->client_info->taglist,
248656850988SCy Schubert 				ms->client_info->taglen))) {
248756850988SCy Schubert 			lock_rw_unlock(&a->lock);
248856850988SCy Schubert 			continue;
248956850988SCy Schubert 		}
249024e36522SCy Schubert 
249124e36522SCy Schubert 		/* the nsdname has precedence over the nsip triggers */
249224e36522SCy Schubert 		z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones,
2493335c7cdaSCy Schubert 						     is->qchase.qclass, &match);
249424e36522SCy Schubert 		if(z != NULL) {
249524e36522SCy Schubert 			lock_rw_unlock(&a->lock);
249624e36522SCy Schubert 			break;
249724e36522SCy Schubert 		}
249824e36522SCy Schubert 
249924e36522SCy Schubert 		raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is);
250024e36522SCy Schubert 		if(raddr != NULL) {
250124e36522SCy Schubert 			lock_rw_unlock(&a->lock);
250224e36522SCy Schubert 			break;
250324e36522SCy Schubert 		}
250424e36522SCy Schubert 		lock_rw_unlock(&a->lock);
250524e36522SCy Schubert 	}
250624e36522SCy Schubert 
250724e36522SCy Schubert 	lock_rw_unlock(&az->rpz_lock);
250824e36522SCy Schubert 
2509865f46b2SCy Schubert 	if(raddr == NULL && z == NULL)
2510865f46b2SCy Schubert 		return NULL;
2511865f46b2SCy Schubert 
2512865f46b2SCy Schubert 	if(raddr != NULL) {
251324e36522SCy Schubert 		if(z) {
251424e36522SCy Schubert 			lock_rw_unlock(&z->lock);
251524e36522SCy Schubert 		}
2516335c7cdaSCy Schubert 		return rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a);
251724e36522SCy Schubert 	}
2518335c7cdaSCy Schubert 	return rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a);
251924e36522SCy Schubert }
252024e36522SCy Schubert 
rpz_callback_from_iterator_cname(struct module_qstate * ms,struct iter_qstate * is)252124e36522SCy Schubert struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
252224e36522SCy Schubert 	struct iter_qstate* is)
252324e36522SCy Schubert {
252424e36522SCy Schubert 	struct auth_zones* az;
252524e36522SCy Schubert 	struct auth_zone* a = NULL;
252624e36522SCy Schubert 	struct rpz* r = NULL;
252724e36522SCy Schubert 	struct local_zone* z = NULL;
252824e36522SCy Schubert 	enum localzone_type lzt;
252924e36522SCy Schubert 	struct dns_msg* ret = NULL;
253024e36522SCy Schubert 
2531a39a5a69SCy Schubert 	if(ms->rpz_passthru) {
2532a39a5a69SCy Schubert 		verbose(VERB_ALGO, "query is rpz_passthru, no further processing");
2533a39a5a69SCy Schubert 		return NULL;
2534a39a5a69SCy Schubert 	}
2535a39a5a69SCy Schubert 
253624e36522SCy Schubert 	if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; }
253724e36522SCy Schubert 	az = ms->env->auth_zones;
253824e36522SCy Schubert 
253924e36522SCy Schubert 	lock_rw_rdlock(&az->rpz_lock);
254024e36522SCy Schubert 
254124e36522SCy Schubert 	for(a = az->rpz_first; a; a = a->rpz_az_next) {
254224e36522SCy Schubert 		lock_rw_rdlock(&a->lock);
254324e36522SCy Schubert 		r = a->rpz;
254424e36522SCy Schubert 		if(r->disabled) {
254524e36522SCy Schubert 			lock_rw_unlock(&a->lock);
254624e36522SCy Schubert 			continue;
254724e36522SCy Schubert 		}
254856850988SCy Schubert 		if(r->taglist && (!ms->client_info ||
254956850988SCy Schubert 			!taglist_intersect(r->taglist, r->taglistlen,
255056850988SCy Schubert 				ms->client_info->taglist,
255156850988SCy Schubert 				ms->client_info->taglen))) {
255256850988SCy Schubert 			lock_rw_unlock(&a->lock);
255356850988SCy Schubert 			continue;
255456850988SCy Schubert 		}
255524e36522SCy Schubert 		z = rpz_find_zone(r->local_zones, is->qchase.qname,
255624e36522SCy Schubert 			is->qchase.qname_len, is->qchase.qclass, 0, 0, 0);
255724e36522SCy Schubert 		if(z && r->action_override == RPZ_DISABLED_ACTION) {
255824e36522SCy Schubert 			if(r->log)
255924e36522SCy Schubert 				log_rpz_apply("qname", z->name, NULL,
256024e36522SCy Schubert 					r->action_override,
256124e36522SCy Schubert 					&ms->qinfo, NULL, ms, r->log_name);
256224e36522SCy Schubert 			if(ms->env->worker)
256324e36522SCy Schubert 				ms->env->worker->stats.rpz_action[r->action_override]++;
256424e36522SCy Schubert 			lock_rw_unlock(&z->lock);
256524e36522SCy Schubert 			z = NULL;
256624e36522SCy Schubert 		}
256724e36522SCy Schubert 		if(z) {
256824e36522SCy Schubert 			break;
256924e36522SCy Schubert 		}
257024e36522SCy Schubert 		/* not found in this auth_zone */
257124e36522SCy Schubert 		lock_rw_unlock(&a->lock);
257224e36522SCy Schubert 	}
257324e36522SCy Schubert 	lock_rw_unlock(&az->rpz_lock);
257424e36522SCy Schubert 
257524e36522SCy Schubert 	if(z == NULL)
257624e36522SCy Schubert 		return NULL;
257724e36522SCy Schubert 	if(r->action_override == RPZ_NO_OVERRIDE_ACTION) {
257824e36522SCy Schubert 		lzt = z->type;
257924e36522SCy Schubert 	} else {
258024e36522SCy Schubert 		lzt = rpz_action_to_localzone_type(r->action_override);
258124e36522SCy Schubert 	}
258224e36522SCy Schubert 
258324e36522SCy Schubert 	if(verbosity >= VERB_ALGO) {
258424e36522SCy Schubert 		char nm[255+1], zn[255+1];
258524e36522SCy Schubert 		dname_str(is->qchase.qname, nm);
258624e36522SCy Schubert 		dname_str(z->name, zn);
258724e36522SCy Schubert 		if(strcmp(zn, nm) != 0)
2588335c7cdaSCy Schubert 			verbose(VERB_ALGO, "rpz: qname trigger %s on %s, with action=%s",
258924e36522SCy Schubert 				zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
259024e36522SCy Schubert 		else
2591335c7cdaSCy Schubert 			verbose(VERB_ALGO, "rpz: qname trigger %s, with action=%s",
259224e36522SCy Schubert 				nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
259324e36522SCy Schubert 	}
259424e36522SCy Schubert 	switch(localzone_type_to_rpz_action(lzt)) {
259524e36522SCy Schubert 	case RPZ_NXDOMAIN_ACTION:
259624e36522SCy Schubert 		ret = rpz_synthesize_nxdomain(r, ms, &is->qchase, a);
2597*46d2f618SCy Schubert 		ms->rpz_applied = 1;
259824e36522SCy Schubert 		break;
259924e36522SCy Schubert 	case RPZ_NODATA_ACTION:
260024e36522SCy Schubert 		ret = rpz_synthesize_nodata(r, ms, &is->qchase, a);
2601*46d2f618SCy Schubert 		ms->rpz_applied = 1;
260224e36522SCy Schubert 		break;
260324e36522SCy Schubert 	case RPZ_TCP_ONLY_ACTION:
260424e36522SCy Schubert 		/* basically a passthru here but the tcp-only will be
260524e36522SCy Schubert 		 * honored before the query gets sent. */
2606103ba509SCy Schubert 		ms->tcp_required = 1;
260724e36522SCy Schubert 		ret = NULL;
260824e36522SCy Schubert 		break;
260924e36522SCy Schubert 	case RPZ_DROP_ACTION:
261024e36522SCy Schubert 		ret = rpz_synthesize_nodata(r, ms, &is->qchase, a);
2611*46d2f618SCy Schubert 		ms->rpz_applied = 1;
261224e36522SCy Schubert 		ms->is_drop = 1;
261324e36522SCy Schubert 		break;
261424e36522SCy Schubert 	case RPZ_LOCAL_DATA_ACTION:
261524e36522SCy Schubert 		ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z, a);
261624e36522SCy Schubert 		if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); }
2617*46d2f618SCy Schubert 		ms->rpz_applied = 1;
261824e36522SCy Schubert 		break;
261924e36522SCy Schubert 	case RPZ_PASSTHRU_ACTION:
262024e36522SCy Schubert 		ret = NULL;
2621a39a5a69SCy Schubert 		ms->rpz_passthru = 1;
262224e36522SCy Schubert 		break;
262324e36522SCy Schubert 	default:
2624335c7cdaSCy Schubert 		verbose(VERB_ALGO, "rpz: qname trigger: bug: unhandled or invalid action: '%s'",
262524e36522SCy Schubert 			rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
262624e36522SCy Schubert 		ret = NULL;
262724e36522SCy Schubert 	}
2628103ba509SCy Schubert 	if(r->log)
2629103ba509SCy Schubert 		log_rpz_apply("qname", (z?z->name:NULL), NULL,
2630103ba509SCy Schubert 			localzone_type_to_rpz_action(lzt),
2631103ba509SCy Schubert 			&is->qchase, NULL, ms, r->log_name);
263224e36522SCy Schubert 	lock_rw_unlock(&z->lock);
263324e36522SCy Schubert 	lock_rw_unlock(&a->lock);
263424e36522SCy Schubert 	return ret;
263524e36522SCy Schubert }
263624e36522SCy Schubert 
263724e36522SCy Schubert static int
rpz_apply_maybe_clientip_trigger(struct auth_zones * az,struct module_env * env,struct query_info * qinfo,struct edns_data * edns,struct comm_reply * repinfo,uint8_t * taglist,size_t taglen,struct ub_server_stats * stats,sldns_buffer * buf,struct regional * temp,struct local_zone ** z_out,struct auth_zone ** a_out,struct rpz ** r_out,int * passthru)263824e36522SCy Schubert rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env,
263924e36522SCy Schubert 	struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo,
264024e36522SCy Schubert 	uint8_t* taglist, size_t taglen, struct ub_server_stats* stats,
264124e36522SCy Schubert 	sldns_buffer* buf, struct regional* temp,
264224e36522SCy Schubert 	/* output parameters */
2643a39a5a69SCy Schubert 	struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out,
2644a39a5a69SCy Schubert 	int* passthru)
264524e36522SCy Schubert {
264624e36522SCy Schubert 	int ret = 0;
264724e36522SCy Schubert 	enum rpz_action client_action;
264824e36522SCy Schubert 	struct clientip_synthesized_rr* node = rpz_resolve_client_action_and_zone(
264924e36522SCy Schubert 		az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out);
265024e36522SCy Schubert 
265124e36522SCy Schubert 	client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action);
2652335c7cdaSCy Schubert 	if(node != NULL && *r_out &&
2653335c7cdaSCy Schubert 		(*r_out)->action_override != RPZ_NO_OVERRIDE_ACTION) {
2654335c7cdaSCy Schubert 		client_action = (*r_out)->action_override;
2655335c7cdaSCy Schubert 	}
2656a39a5a69SCy Schubert 	if(client_action == RPZ_PASSTHRU_ACTION) {
2657335c7cdaSCy Schubert 		if(*r_out && (*r_out)->log)
2658335c7cdaSCy Schubert 			log_rpz_apply(
2659335c7cdaSCy Schubert 				(node?"clientip":"qname"),
2660335c7cdaSCy Schubert 				((*z_out)?(*z_out)->name:NULL),
2661335c7cdaSCy Schubert 				(node?&node->node:NULL),
2662335c7cdaSCy Schubert 				client_action, qinfo, repinfo, NULL,
2663335c7cdaSCy Schubert 				(*r_out)->log_name);
2664a39a5a69SCy Schubert 		*passthru = 1;
2665335c7cdaSCy Schubert 		ret = 0;
2666335c7cdaSCy Schubert 		goto done;
2667a39a5a69SCy Schubert 	}
266824e36522SCy Schubert 	if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION &&
266924e36522SCy Schubert 			      client_action != RPZ_PASSTHRU_ACTION)) {
267024e36522SCy Schubert 		if(client_action == RPZ_PASSTHRU_ACTION
267124e36522SCy Schubert 			|| client_action == RPZ_INVALID_ACTION
267224e36522SCy Schubert 			|| (client_action == RPZ_TCP_ONLY_ACTION
267324e36522SCy Schubert 				&& !rpz_is_udp_query(repinfo))) {
267424e36522SCy Schubert 			ret = 0;
267524e36522SCy Schubert 			goto done;
267624e36522SCy Schubert 		}
267724e36522SCy Schubert 		stats->rpz_action[client_action]++;
267824e36522SCy Schubert 		if(client_action == RPZ_LOCAL_DATA_ACTION) {
267924e36522SCy Schubert 			rpz_apply_clientip_localdata_action(node, env, qinfo,
268024e36522SCy Schubert 				edns, repinfo, buf, temp, *a_out);
2681335c7cdaSCy Schubert 			ret = 1;
2682335c7cdaSCy Schubert 		} else if(client_action == RPZ_CNAME_OVERRIDE_ACTION) {
2683335c7cdaSCy Schubert 			if(!rpz_apply_cname_override_action(*r_out, qinfo,
2684335c7cdaSCy Schubert 				temp)) {
2685335c7cdaSCy Schubert 				ret = 0;
2686335c7cdaSCy Schubert 				goto done;
2687335c7cdaSCy Schubert 			}
2688335c7cdaSCy Schubert 			ret = 0;
268924e36522SCy Schubert 		} else {
269024e36522SCy Schubert 			local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns,
269124e36522SCy Schubert 				repinfo, buf, temp, 0 /* no local data used */,
269224e36522SCy Schubert 				rpz_action_to_localzone_type(client_action));
26939cf5bc93SCy Schubert 			if(*r_out && (*r_out)->signal_nxdomain_ra &&
26949cf5bc93SCy Schubert 				LDNS_RCODE_WIRE(sldns_buffer_begin(buf))
26959cf5bc93SCy Schubert 				== LDNS_RCODE_NXDOMAIN)
26969cf5bc93SCy Schubert 				LDNS_RA_CLR(sldns_buffer_begin(buf));
269724e36522SCy Schubert 			ret = 1;
2698335c7cdaSCy Schubert 		}
2699335c7cdaSCy Schubert 		if(*r_out && (*r_out)->log)
2700335c7cdaSCy Schubert 			log_rpz_apply(
2701335c7cdaSCy Schubert 				(node?"clientip":"qname"),
2702335c7cdaSCy Schubert 				((*z_out)?(*z_out)->name:NULL),
2703335c7cdaSCy Schubert 				(node?&node->node:NULL),
2704335c7cdaSCy Schubert 				client_action, qinfo, repinfo, NULL,
2705335c7cdaSCy Schubert 				(*r_out)->log_name);
270624e36522SCy Schubert 		goto done;
270724e36522SCy Schubert 	}
270824e36522SCy Schubert 	ret = -1;
270924e36522SCy Schubert done:
271024e36522SCy Schubert 	if(node != NULL) {
271124e36522SCy Schubert 		lock_rw_unlock(&node->lock);
271224e36522SCy Schubert 	}
271324e36522SCy Schubert 	return ret;
271424e36522SCy Schubert }
271524e36522SCy Schubert 
271624e36522SCy Schubert int
rpz_callback_from_worker_request(struct auth_zones * az,struct module_env * env,struct query_info * qinfo,struct edns_data * edns,sldns_buffer * buf,struct regional * temp,struct comm_reply * repinfo,uint8_t * taglist,size_t taglen,struct ub_server_stats * stats,int * passthru)271724e36522SCy Schubert rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env,
271824e36522SCy Schubert 	struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
271924e36522SCy Schubert 	struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist,
2720a39a5a69SCy Schubert 	size_t taglen, struct ub_server_stats* stats, int* passthru)
272124e36522SCy Schubert {
272224e36522SCy Schubert 	struct rpz* r = NULL;
272324e36522SCy Schubert 	struct auth_zone* a = NULL;
272424e36522SCy Schubert 	struct local_zone* z = NULL;
272524e36522SCy Schubert 	int ret;
272624e36522SCy Schubert 	enum localzone_type lzt;
272724e36522SCy Schubert 
272824e36522SCy Schubert 	int clientip_trigger = rpz_apply_maybe_clientip_trigger(az, env, qinfo,
2729a39a5a69SCy Schubert 		edns, repinfo, taglist, taglen, stats, buf, temp, &z, &a, &r,
2730a39a5a69SCy Schubert 		passthru);
273124e36522SCy Schubert 	if(clientip_trigger >= 0) {
273224e36522SCy Schubert 		if(a) {
273324e36522SCy Schubert 			lock_rw_unlock(&a->lock);
273424e36522SCy Schubert 		}
273524e36522SCy Schubert 		if(z) {
273624e36522SCy Schubert 			lock_rw_unlock(&z->lock);
273724e36522SCy Schubert 		}
273824e36522SCy Schubert 		return clientip_trigger;
273924e36522SCy Schubert 	}
274024e36522SCy Schubert 
274124e36522SCy Schubert 	if(z == NULL) {
274224e36522SCy Schubert 		if(a) {
274324e36522SCy Schubert 			lock_rw_unlock(&a->lock);
274424e36522SCy Schubert 		}
274524e36522SCy Schubert 		return 0;
274624e36522SCy Schubert 	}
274724e36522SCy Schubert 
274824e36522SCy Schubert 	log_assert(r);
274924e36522SCy Schubert 
275024e36522SCy Schubert 	if(r->action_override == RPZ_NO_OVERRIDE_ACTION) {
275124e36522SCy Schubert 		lzt = z->type;
275224e36522SCy Schubert 	} else {
275324e36522SCy Schubert 		lzt = rpz_action_to_localzone_type(r->action_override);
275424e36522SCy Schubert 	}
2755a39a5a69SCy Schubert 	if(r->action_override == RPZ_PASSTHRU_ACTION ||
2756a39a5a69SCy Schubert 		lzt == local_zone_always_transparent /* RPZ_PASSTHRU_ACTION */) {
2757a39a5a69SCy Schubert 		*passthru = 1;
2758a39a5a69SCy Schubert 	}
275924e36522SCy Schubert 
276024e36522SCy Schubert 	if(verbosity >= VERB_ALGO) {
276124e36522SCy Schubert 		char nm[255+1], zn[255+1];
276224e36522SCy Schubert 		dname_str(qinfo->qname, nm);
276324e36522SCy Schubert 		dname_str(z->name, zn);
276424e36522SCy Schubert 		if(strcmp(zn, nm) != 0)
276524e36522SCy Schubert 			verbose(VERB_ALGO, "rpz: qname trigger %s on %s with action=%s",
276624e36522SCy Schubert 				zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
276724e36522SCy Schubert 		else
276824e36522SCy Schubert 			verbose(VERB_ALGO, "rpz: qname trigger %s with action=%s",
276924e36522SCy Schubert 				nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
277024e36522SCy Schubert 	}
277124e36522SCy Schubert 
277224e36522SCy Schubert 	ret = rpz_synthesize_qname_localdata(env, r, z, lzt, qinfo, edns, buf, temp,
277324e36522SCy Schubert 					     repinfo, stats);
277424e36522SCy Schubert 
2775091e9e46SCy Schubert 	lock_rw_unlock(&z->lock);
277625039b37SCy Schubert 	lock_rw_unlock(&a->lock);
2777091e9e46SCy Schubert 
2778091e9e46SCy Schubert 	return ret;
2779091e9e46SCy Schubert }
2780f44e67d1SCy Schubert 
rpz_enable(struct rpz * r)2781f44e67d1SCy Schubert void rpz_enable(struct rpz* r)
2782f44e67d1SCy Schubert {
2783f44e67d1SCy Schubert     if(!r)
2784f44e67d1SCy Schubert         return;
2785f44e67d1SCy Schubert     r->disabled = 0;
2786f44e67d1SCy Schubert }
2787f44e67d1SCy Schubert 
rpz_disable(struct rpz * r)2788f44e67d1SCy Schubert void rpz_disable(struct rpz* r)
2789f44e67d1SCy Schubert {
2790f44e67d1SCy Schubert     if(!r)
2791f44e67d1SCy Schubert         return;
2792f44e67d1SCy Schubert     r->disabled = 1;
2793f44e67d1SCy Schubert }
2794