xref: /openbsd-src/usr.sbin/unbound/services/localzone.c (revision 98bc733b08604094f4138174a0ee0bb9faaca4bd)
1933707f3Ssthen /*
2933707f3Ssthen  * services/localzone.c - local zones authority service.
3933707f3Ssthen  *
4933707f3Ssthen  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5933707f3Ssthen  *
6933707f3Ssthen  * This software is open source.
7933707f3Ssthen  *
8933707f3Ssthen  * Redistribution and use in source and binary forms, with or without
9933707f3Ssthen  * modification, are permitted provided that the following conditions
10933707f3Ssthen  * are met:
11933707f3Ssthen  *
12933707f3Ssthen  * Redistributions of source code must retain the above copyright notice,
13933707f3Ssthen  * this list of conditions and the following disclaimer.
14933707f3Ssthen  *
15933707f3Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16933707f3Ssthen  * this list of conditions and the following disclaimer in the documentation
17933707f3Ssthen  * and/or other materials provided with the distribution.
18933707f3Ssthen  *
19933707f3Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20933707f3Ssthen  * be used to endorse or promote products derived from this software without
21933707f3Ssthen  * specific prior written permission.
22933707f3Ssthen  *
23933707f3Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
245d76a658Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
255d76a658Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
265d76a658Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
275d76a658Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
285d76a658Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
295d76a658Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
305d76a658Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
315d76a658Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
325d76a658Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
335d76a658Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34933707f3Ssthen  */
35933707f3Ssthen 
36933707f3Ssthen /**
37933707f3Ssthen  * \file
38933707f3Ssthen  *
39933707f3Ssthen  * This file contains functions to enable local zone authority service.
40933707f3Ssthen  */
41933707f3Ssthen #include "config.h"
42933707f3Ssthen #include "services/localzone.h"
43fdfb4ba6Ssthen #include "sldns/str2wire.h"
44933707f3Ssthen #include "util/regional.h"
45933707f3Ssthen #include "util/config_file.h"
46933707f3Ssthen #include "util/data/dname.h"
47933707f3Ssthen #include "util/data/packed_rrset.h"
48933707f3Ssthen #include "util/data/msgencode.h"
49933707f3Ssthen #include "util/net_help.h"
50b2cdf21fSsthen #include "util/netevent.h"
51933707f3Ssthen #include "util/data/msgreply.h"
52933707f3Ssthen #include "util/data/msgparse.h"
5332e31f52Ssthen #include "util/as112.h"
5477079be7Ssthen 
5577079be7Ssthen /* maximum RRs in an RRset, to cap possible 'endless' list RRs.
5677079be7Ssthen  * with 16 bytes for an A record, a 64K packet has about 4000 max */
5777079be7Ssthen #define LOCALZONE_RRSET_COUNT_MAX 4096
58933707f3Ssthen 
59e21c60efSsthen /** print all RRsets in local zone */
60e21c60efSsthen static void
61e21c60efSsthen local_zone_out(struct local_zone* z)
62e21c60efSsthen {
63e21c60efSsthen 	struct local_data* d;
64e21c60efSsthen 	struct local_rrset* p;
65e21c60efSsthen 	RBTREE_FOR(d, struct local_data*, &z->data) {
66e21c60efSsthen 		for(p = d->rrsets; p; p = p->next) {
67e21c60efSsthen 			log_nametypeclass(NO_VERBOSE, "rrset", d->name,
68e21c60efSsthen 				ntohs(p->rrset->rk.type),
69e21c60efSsthen 				ntohs(p->rrset->rk.rrset_class));
70e21c60efSsthen 		}
71e21c60efSsthen 	}
72e21c60efSsthen }
73e21c60efSsthen 
74e21c60efSsthen static void
75e21c60efSsthen local_zone_print(struct local_zone* z)
76e21c60efSsthen {
77e21c60efSsthen 	char buf[64];
78e21c60efSsthen 	lock_rw_rdlock(&z->lock);
79e21c60efSsthen 	snprintf(buf, sizeof(buf), "%s zone",
80e21c60efSsthen 		local_zone_type2str(z->type));
81e21c60efSsthen 	log_nametypeclass(NO_VERBOSE, buf, z->name, 0, z->dclass);
82e21c60efSsthen 	local_zone_out(z);
83e21c60efSsthen 	lock_rw_unlock(&z->lock);
84e21c60efSsthen }
85e21c60efSsthen 
86e21c60efSsthen void local_zones_print(struct local_zones* zones)
87e21c60efSsthen {
88e21c60efSsthen 	struct local_zone* z;
89e21c60efSsthen 	lock_rw_rdlock(&zones->lock);
90e21c60efSsthen 	log_info("number of auth zones %u", (unsigned)zones->ztree.count);
91e21c60efSsthen 	RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
92e21c60efSsthen 		local_zone_print(z);
93e21c60efSsthen 	}
94e21c60efSsthen 	lock_rw_unlock(&zones->lock);
95e21c60efSsthen }
96e21c60efSsthen 
97933707f3Ssthen struct local_zones*
98933707f3Ssthen local_zones_create(void)
99933707f3Ssthen {
100933707f3Ssthen 	struct local_zones* zones = (struct local_zones*)calloc(1,
101933707f3Ssthen 		sizeof(*zones));
102933707f3Ssthen 	if(!zones)
103933707f3Ssthen 		return NULL;
104933707f3Ssthen 	rbtree_init(&zones->ztree, &local_zone_cmp);
1055d76a658Ssthen 	lock_rw_init(&zones->lock);
106933707f3Ssthen 	lock_protect(&zones->lock, &zones->ztree, sizeof(zones->ztree));
107933707f3Ssthen 	/* also lock protects the rbnode's in struct local_zone */
108933707f3Ssthen 	return zones;
109933707f3Ssthen }
110933707f3Ssthen 
111933707f3Ssthen /** helper traverse to delete zones */
112933707f3Ssthen static void
11377079be7Ssthen lzdel(rbnode_type* n, void* ATTR_UNUSED(arg))
114933707f3Ssthen {
115933707f3Ssthen 	struct local_zone* z = (struct local_zone*)n->key;
116933707f3Ssthen 	local_zone_delete(z);
117933707f3Ssthen }
118933707f3Ssthen 
119933707f3Ssthen void
120933707f3Ssthen local_zones_delete(struct local_zones* zones)
121933707f3Ssthen {
122933707f3Ssthen 	if(!zones)
123933707f3Ssthen 		return;
1245d76a658Ssthen 	lock_rw_destroy(&zones->lock);
125933707f3Ssthen 	/* walk through zones and delete them all */
126933707f3Ssthen 	traverse_postorder(&zones->ztree, lzdel, NULL);
127933707f3Ssthen 	free(zones);
128933707f3Ssthen }
129933707f3Ssthen 
130933707f3Ssthen void
131933707f3Ssthen local_zone_delete(struct local_zone* z)
132933707f3Ssthen {
133933707f3Ssthen 	if(!z)
134933707f3Ssthen 		return;
135933707f3Ssthen 	lock_rw_destroy(&z->lock);
136933707f3Ssthen 	regional_destroy(z->region);
137933707f3Ssthen 	free(z->name);
1382ee382b6Ssthen 	free(z->taglist);
139933707f3Ssthen 	free(z);
140933707f3Ssthen }
141933707f3Ssthen 
142933707f3Ssthen int
143933707f3Ssthen local_zone_cmp(const void* z1, const void* z2)
144933707f3Ssthen {
145933707f3Ssthen 	/* first sort on class, so that hierarchy can be maintained within
146933707f3Ssthen 	 * a class */
147933707f3Ssthen 	struct local_zone* a = (struct local_zone*)z1;
148933707f3Ssthen 	struct local_zone* b = (struct local_zone*)z2;
149933707f3Ssthen 	int m;
150933707f3Ssthen 	if(a->dclass != b->dclass) {
151933707f3Ssthen 		if(a->dclass < b->dclass)
152933707f3Ssthen 			return -1;
153933707f3Ssthen 		return 1;
154933707f3Ssthen 	}
155933707f3Ssthen 	return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m);
156933707f3Ssthen }
157933707f3Ssthen 
158933707f3Ssthen int
159933707f3Ssthen local_data_cmp(const void* d1, const void* d2)
160933707f3Ssthen {
161933707f3Ssthen 	struct local_data* a = (struct local_data*)d1;
162933707f3Ssthen 	struct local_data* b = (struct local_data*)d2;
163933707f3Ssthen 	int m;
164933707f3Ssthen 	return dname_canon_lab_cmp(a->name, a->namelabs, b->name,
165933707f3Ssthen 		b->namelabs, &m);
166933707f3Ssthen }
167933707f3Ssthen 
168933707f3Ssthen /* form wireformat from text format domain name */
169933707f3Ssthen int
170933707f3Ssthen parse_dname(const char* str, uint8_t** res, size_t* len, int* labs)
171933707f3Ssthen {
1725d76a658Ssthen 	*res = sldns_str2wire_dname(str, len);
173933707f3Ssthen 	*labs = 0;
174933707f3Ssthen 	if(!*res) {
1755d76a658Ssthen 		log_err("cannot parse name %s", str);
176933707f3Ssthen 		return 0;
177933707f3Ssthen 	}
178933707f3Ssthen 	*labs = dname_count_size_labels(*res, len);
179933707f3Ssthen 	return 1;
180933707f3Ssthen }
181933707f3Ssthen 
182933707f3Ssthen /** create a new localzone */
183933707f3Ssthen static struct local_zone*
184933707f3Ssthen local_zone_create(uint8_t* nm, size_t len, int labs,
185933707f3Ssthen 	enum localzone_type t, uint16_t dclass)
186933707f3Ssthen {
187933707f3Ssthen 	struct local_zone* z = (struct local_zone*)calloc(1, sizeof(*z));
188933707f3Ssthen 	if(!z) {
189933707f3Ssthen 		return NULL;
190933707f3Ssthen 	}
191933707f3Ssthen 	z->node.key = z;
192933707f3Ssthen 	z->dclass = dclass;
193933707f3Ssthen 	z->type = t;
194933707f3Ssthen 	z->name = nm;
195933707f3Ssthen 	z->namelen = len;
196933707f3Ssthen 	z->namelabs = labs;
197933707f3Ssthen 	lock_rw_init(&z->lock);
198eba819a2Ssthen 	z->region = regional_create_nochunk(sizeof(struct regional));
199933707f3Ssthen 	if(!z->region) {
200933707f3Ssthen 		free(z);
201933707f3Ssthen 		return NULL;
202933707f3Ssthen 	}
203933707f3Ssthen 	rbtree_init(&z->data, &local_data_cmp);
20477079be7Ssthen 	lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_type));
205933707f3Ssthen 	/* also the zones->lock protects node, parent, name*, class */
206933707f3Ssthen 	return z;
207933707f3Ssthen }
208933707f3Ssthen 
209933707f3Ssthen /** enter a new zone with allocated dname returns with WRlock */
210933707f3Ssthen static struct local_zone*
211933707f3Ssthen lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
212933707f3Ssthen 	int labs, enum localzone_type t, uint16_t c)
213933707f3Ssthen {
214933707f3Ssthen 	struct local_zone* z = local_zone_create(nm, len, labs, t, c);
215933707f3Ssthen 	if(!z) {
2162ee382b6Ssthen 		free(nm);
217933707f3Ssthen 		log_err("out of memory");
218933707f3Ssthen 		return NULL;
219933707f3Ssthen 	}
220933707f3Ssthen 
221933707f3Ssthen 	/* add to rbtree */
2225d76a658Ssthen 	lock_rw_wrlock(&zones->lock);
223933707f3Ssthen 	lock_rw_wrlock(&z->lock);
224933707f3Ssthen 	if(!rbtree_insert(&zones->ztree, &z->node)) {
22577079be7Ssthen 		struct local_zone* oldz;
2262be9e038Ssthen 		char str[256];
2272be9e038Ssthen 		dname_str(nm, str);
2282be9e038Ssthen 		log_warn("duplicate local-zone %s", str);
229933707f3Ssthen 		lock_rw_unlock(&z->lock);
23077079be7Ssthen 		/* save zone name locally before deallocation,
23177079be7Ssthen 		 * otherwise, nm is gone if we zone_delete now. */
23277079be7Ssthen 		oldz = z;
23377079be7Ssthen 		/* find the correct zone, so not an error for duplicate */
23477079be7Ssthen 		z = local_zones_find(zones, nm, len, labs, c);
23577079be7Ssthen 		lock_rw_wrlock(&z->lock);
2365d76a658Ssthen 		lock_rw_unlock(&zones->lock);
23777079be7Ssthen 		local_zone_delete(oldz);
23877079be7Ssthen 		return z;
239933707f3Ssthen 	}
2405d76a658Ssthen 	lock_rw_unlock(&zones->lock);
241933707f3Ssthen 	return z;
242933707f3Ssthen }
243933707f3Ssthen 
244933707f3Ssthen /** enter a new zone */
245*98bc733bSsthen struct local_zone*
246933707f3Ssthen lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
247933707f3Ssthen 	uint16_t dclass)
248933707f3Ssthen {
249933707f3Ssthen 	struct local_zone* z;
250933707f3Ssthen 	enum localzone_type t;
251933707f3Ssthen 	uint8_t* nm;
252933707f3Ssthen 	size_t len;
253933707f3Ssthen 	int labs;
254933707f3Ssthen 	if(!parse_dname(name, &nm, &len, &labs)) {
255933707f3Ssthen 		log_err("bad zone name %s %s", name, type);
256933707f3Ssthen 		return NULL;
257933707f3Ssthen 	}
258933707f3Ssthen 	if(!local_zone_str2type(type, &t)) {
259933707f3Ssthen 		log_err("bad lz_enter_zone type %s %s", name, type);
260933707f3Ssthen 		free(nm);
261933707f3Ssthen 		return NULL;
262933707f3Ssthen 	}
263933707f3Ssthen 	if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) {
264933707f3Ssthen 		log_err("could not enter zone %s %s", name, type);
265933707f3Ssthen 		return NULL;
266933707f3Ssthen 	}
267933707f3Ssthen 	return z;
268933707f3Ssthen }
269933707f3Ssthen 
2702be9e038Ssthen int
2712be9e038Ssthen rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
2725d76a658Ssthen 	uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len,
2735d76a658Ssthen 	uint8_t** rdata, size_t* rdata_len)
274933707f3Ssthen {
2755d76a658Ssthen 	size_t dname_len = 0;
2765d76a658Ssthen 	int e = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600,
2775d76a658Ssthen 		NULL, 0, NULL, 0);
2785d76a658Ssthen 	if(e) {
2795d76a658Ssthen 		log_err("error parsing local-data at %d: '%s': %s",
2805d76a658Ssthen 			LDNS_WIREPARSE_OFFSET(e), str,
2815d76a658Ssthen 			sldns_get_errorstr_parse(e));
282933707f3Ssthen 		return 0;
283933707f3Ssthen 	}
2845d76a658Ssthen 	*nm = memdup(rr, dname_len);
285933707f3Ssthen 	if(!*nm) {
286933707f3Ssthen 		log_err("out of memory");
287933707f3Ssthen 		return 0;
288933707f3Ssthen 	}
2895d76a658Ssthen 	*dclass = sldns_wirerr_get_class(rr, len, dname_len);
2905d76a658Ssthen 	*type = sldns_wirerr_get_type(rr, len, dname_len);
2915d76a658Ssthen 	*ttl = (time_t)sldns_wirerr_get_ttl(rr, len, dname_len);
2925d76a658Ssthen 	*rdata = sldns_wirerr_get_rdatawl(rr, len, dname_len);
2935d76a658Ssthen 	*rdata_len = sldns_wirerr_get_rdatalen(rr, len, dname_len)+2;
294933707f3Ssthen 	return 1;
295933707f3Ssthen }
296933707f3Ssthen 
297933707f3Ssthen /** return name and class of rr; parses string */
298933707f3Ssthen static int
2997191de28Ssthen get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass,
3007191de28Ssthen 	uint16_t* dtype)
301933707f3Ssthen {
3025d76a658Ssthen 	uint8_t rr[LDNS_RR_BUF_SIZE];
3035d76a658Ssthen 	size_t len = sizeof(rr), dname_len = 0;
3045d76a658Ssthen 	int s = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600,
3055d76a658Ssthen 		NULL, 0, NULL, 0);
3065d76a658Ssthen 	if(s != 0) {
3075d76a658Ssthen 		log_err("error parsing local-data at %d '%s': %s",
3085d76a658Ssthen 			LDNS_WIREPARSE_OFFSET(s), str,
3095d76a658Ssthen 			sldns_get_errorstr_parse(s));
310933707f3Ssthen 		return 0;
311933707f3Ssthen 	}
3125d76a658Ssthen 	*nm = memdup(rr, dname_len);
3135d76a658Ssthen 	*dclass = sldns_wirerr_get_class(rr, len, dname_len);
3147191de28Ssthen 	*dtype = sldns_wirerr_get_type(rr, len, dname_len);
315933707f3Ssthen 	if(!*nm) {
316933707f3Ssthen 		log_err("out of memory");
317933707f3Ssthen 		return 0;
318933707f3Ssthen 	}
319933707f3Ssthen 	return 1;
320933707f3Ssthen }
321933707f3Ssthen 
322933707f3Ssthen /**
323933707f3Ssthen  * Find an rrset in local data structure.
324933707f3Ssthen  * @param data: local data domain name structure.
325933707f3Ssthen  * @param type: type to look for (host order).
32677079be7Ssthen  * @param alias_ok: 1 if matching a non-exact, alias type such as CNAME is
32777079be7Ssthen  * allowed.  otherwise 0.
328933707f3Ssthen  * @return rrset pointer or NULL if not found.
329933707f3Ssthen  */
330933707f3Ssthen static struct local_rrset*
33177079be7Ssthen local_data_find_type(struct local_data* data, uint16_t type, int alias_ok)
332933707f3Ssthen {
3332bdc0ed1Ssthen 	struct local_rrset* p, *cname = NULL;
334933707f3Ssthen 	type = htons(type);
335933707f3Ssthen 	for(p = data->rrsets; p; p = p->next) {
336933707f3Ssthen 		if(p->rrset->rk.type == type)
337933707f3Ssthen 			return p;
33877079be7Ssthen 		if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME))
3392bdc0ed1Ssthen 			cname = p;
340933707f3Ssthen 	}
3412bdc0ed1Ssthen 	if(alias_ok)
3422bdc0ed1Ssthen 		return cname;
343933707f3Ssthen 	return NULL;
344933707f3Ssthen }
345933707f3Ssthen 
346933707f3Ssthen /** check for RR duplicates */
347933707f3Ssthen static int
3485d76a658Ssthen rr_is_duplicate(struct packed_rrset_data* pd, uint8_t* rdata, size_t rdata_len)
349933707f3Ssthen {
350933707f3Ssthen 	size_t i;
351933707f3Ssthen 	for(i=0; i<pd->count; i++) {
3525d76a658Ssthen 		if(pd->rr_len[i] == rdata_len &&
3535d76a658Ssthen 			memcmp(pd->rr_data[i], rdata, rdata_len) == 0)
354933707f3Ssthen 			return 1;
355933707f3Ssthen 	}
356933707f3Ssthen 	return 0;
357933707f3Ssthen }
358933707f3Ssthen 
359933707f3Ssthen /** new local_rrset */
360933707f3Ssthen static struct local_rrset*
361933707f3Ssthen new_local_rrset(struct regional* region, struct local_data* node,
362933707f3Ssthen 	uint16_t rrtype, uint16_t rrclass)
363933707f3Ssthen {
364933707f3Ssthen 	struct packed_rrset_data* pd;
365933707f3Ssthen 	struct local_rrset* rrset = (struct local_rrset*)
366933707f3Ssthen 		regional_alloc_zero(region, sizeof(*rrset));
367933707f3Ssthen 	if(!rrset) {
368933707f3Ssthen 		log_err("out of memory");
369933707f3Ssthen 		return NULL;
370933707f3Ssthen 	}
371933707f3Ssthen 	rrset->next = node->rrsets;
372933707f3Ssthen 	node->rrsets = rrset;
373933707f3Ssthen 	rrset->rrset = (struct ub_packed_rrset_key*)
374933707f3Ssthen 		regional_alloc_zero(region, sizeof(*rrset->rrset));
375933707f3Ssthen 	if(!rrset->rrset) {
376933707f3Ssthen 		log_err("out of memory");
377933707f3Ssthen 		return NULL;
378933707f3Ssthen 	}
379933707f3Ssthen 	rrset->rrset->entry.key = rrset->rrset;
380933707f3Ssthen 	pd = (struct packed_rrset_data*)regional_alloc_zero(region,
381933707f3Ssthen 		sizeof(*pd));
382933707f3Ssthen 	if(!pd) {
383933707f3Ssthen 		log_err("out of memory");
384933707f3Ssthen 		return NULL;
385933707f3Ssthen 	}
386933707f3Ssthen 	pd->trust = rrset_trust_prim_noglue;
387933707f3Ssthen 	pd->security = sec_status_insecure;
388933707f3Ssthen 	rrset->rrset->entry.data = pd;
389933707f3Ssthen 	rrset->rrset->rk.dname = node->name;
390933707f3Ssthen 	rrset->rrset->rk.dname_len = node->namelen;
391933707f3Ssthen 	rrset->rrset->rk.type = htons(rrtype);
392933707f3Ssthen 	rrset->rrset->rk.rrset_class = htons(rrclass);
393933707f3Ssthen 	return rrset;
394933707f3Ssthen }
395933707f3Ssthen 
396933707f3Ssthen /** insert RR into RRset data structure; Wastes a couple of bytes */
3972be9e038Ssthen int
3982be9e038Ssthen rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd,
39977079be7Ssthen 	uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr)
400933707f3Ssthen {
401933707f3Ssthen 	size_t* oldlen = pd->rr_len;
402229e174cSsthen 	time_t* oldttl = pd->rr_ttl;
403933707f3Ssthen 	uint8_t** olddata = pd->rr_data;
404933707f3Ssthen 
405933707f3Ssthen 	/* add RR to rrset */
40677079be7Ssthen 	if(pd->count > LOCALZONE_RRSET_COUNT_MAX) {
40777079be7Ssthen 		log_warn("RRset '%s' has more than %d records, record ignored",
40877079be7Ssthen 			rrstr, LOCALZONE_RRSET_COUNT_MAX);
40977079be7Ssthen 		return 1;
41077079be7Ssthen 	}
411933707f3Ssthen 	pd->count++;
412933707f3Ssthen 	pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count);
413933707f3Ssthen 	pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count);
414933707f3Ssthen 	pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count);
415933707f3Ssthen 	if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
416933707f3Ssthen 		log_err("out of memory");
417933707f3Ssthen 		return 0;
418933707f3Ssthen 	}
419933707f3Ssthen 	if(pd->count > 1) {
420933707f3Ssthen 		memcpy(pd->rr_len+1, oldlen,
421933707f3Ssthen 			sizeof(*pd->rr_len)*(pd->count-1));
422933707f3Ssthen 		memcpy(pd->rr_ttl+1, oldttl,
423933707f3Ssthen 			sizeof(*pd->rr_ttl)*(pd->count-1));
424933707f3Ssthen 		memcpy(pd->rr_data+1, olddata,
425933707f3Ssthen 			sizeof(*pd->rr_data)*(pd->count-1));
426933707f3Ssthen 	}
4275d76a658Ssthen 	pd->rr_len[0] = rdata_len;
428933707f3Ssthen 	pd->rr_ttl[0] = ttl;
4295d76a658Ssthen 	pd->rr_data[0] = regional_alloc_init(region, rdata, rdata_len);
430933707f3Ssthen 	if(!pd->rr_data[0]) {
431933707f3Ssthen 		log_err("out of memory");
432933707f3Ssthen 		return 0;
433933707f3Ssthen 	}
434933707f3Ssthen 	return 1;
435933707f3Ssthen }
436933707f3Ssthen 
437eaf2578eSsthen /** Delete RR from local-zone RRset, wastes memory as the deleted RRs cannot be
438eaf2578eSsthen  * free'd (regionally alloc'd) */
439eaf2578eSsthen int
440eaf2578eSsthen local_rrset_remove_rr(struct packed_rrset_data* pd, size_t index)
441eaf2578eSsthen {
442eaf2578eSsthen 	log_assert(pd->count > 0);
443eaf2578eSsthen 	if(index >= pd->count) {
444eaf2578eSsthen 		log_warn("Trying to remove RR with out of bound index");
445eaf2578eSsthen 		return 0;
446eaf2578eSsthen 	}
447eaf2578eSsthen 	if(index + 1 < pd->count) {
448eaf2578eSsthen 		/* not removing last element */
449eaf2578eSsthen 		size_t nexti = index + 1;
450eaf2578eSsthen 		size_t num = pd->count - nexti;
451eaf2578eSsthen 		memmove(pd->rr_len+index, pd->rr_len+nexti, sizeof(*pd->rr_len)*num);
452eaf2578eSsthen 		memmove(pd->rr_ttl+index, pd->rr_ttl+nexti, sizeof(*pd->rr_ttl)*num);
453eaf2578eSsthen 		memmove(pd->rr_data+index, pd->rr_data+nexti, sizeof(*pd->rr_data)*num);
454eaf2578eSsthen 	}
455eaf2578eSsthen 	pd->count--;
456eaf2578eSsthen 	return 1;
457eaf2578eSsthen }
458eaf2578eSsthen 
459eaf2578eSsthen struct local_data*
460eaf2578eSsthen local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs)
461933707f3Ssthen {
462933707f3Ssthen 	struct local_data key;
463933707f3Ssthen 	key.node.key = &key;
464933707f3Ssthen 	key.name = nm;
465933707f3Ssthen 	key.namelen = nmlen;
466933707f3Ssthen 	key.namelabs = nmlabs;
467933707f3Ssthen 	return (struct local_data*)rbtree_search(&z->data, &key.node);
468933707f3Ssthen }
469933707f3Ssthen 
470933707f3Ssthen /** find a node, create it if not and all its empty nonterminal parents */
471933707f3Ssthen static int
472933707f3Ssthen lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen,
473933707f3Ssthen 	int nmlabs, struct local_data** res)
474933707f3Ssthen {
475eaf2578eSsthen 	struct local_data* ld = local_zone_find_data(z, nm, nmlen, nmlabs);
476933707f3Ssthen 	if(!ld) {
477933707f3Ssthen 		/* create a domain name to store rr. */
478933707f3Ssthen 		ld = (struct local_data*)regional_alloc_zero(z->region,
479933707f3Ssthen 			sizeof(*ld));
480933707f3Ssthen 		if(!ld) {
481933707f3Ssthen 			log_err("out of memory adding local data");
482933707f3Ssthen 			return 0;
483933707f3Ssthen 		}
484933707f3Ssthen 		ld->node.key = ld;
485933707f3Ssthen 		ld->name = regional_alloc_init(z->region, nm, nmlen);
486933707f3Ssthen 		if(!ld->name) {
487933707f3Ssthen 			log_err("out of memory");
488933707f3Ssthen 			return 0;
489933707f3Ssthen 		}
490933707f3Ssthen 		ld->namelen = nmlen;
491933707f3Ssthen 		ld->namelabs = nmlabs;
492933707f3Ssthen 		if(!rbtree_insert(&z->data, &ld->node)) {
493933707f3Ssthen 			log_assert(0); /* duplicate name */
494933707f3Ssthen 		}
495933707f3Ssthen 		/* see if empty nonterminals need to be created */
496933707f3Ssthen 		if(nmlabs > z->namelabs) {
497933707f3Ssthen 			dname_remove_label(&nm, &nmlen);
498933707f3Ssthen 			if(!lz_find_create_node(z, nm, nmlen, nmlabs-1, res))
499933707f3Ssthen 				return 0;
500933707f3Ssthen 		}
501933707f3Ssthen 	}
502933707f3Ssthen 	*res = ld;
503933707f3Ssthen 	return 1;
504933707f3Ssthen }
505933707f3Ssthen 
5069982a05dSsthen /* Mark the SOA record for the zone. This only marks the SOA rrset; the data
5079982a05dSsthen  * for the RR is entered later on local_zone_enter_rr() as with the other
508e21c60efSsthen  * records. An artificial soa_negative record with a modified TTL (minimum of
5099982a05dSsthen  * the TTL and the SOA.MINIMUM) is also created and marked for usage with
5109982a05dSsthen  * negative answers and to avoid allocations during those answers. */
5119982a05dSsthen static int
5129982a05dSsthen lz_mark_soa_for_zone(struct local_zone* z, struct ub_packed_rrset_key* soa_rrset,
5139982a05dSsthen 	uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr)
5149982a05dSsthen {
5159982a05dSsthen 	struct packed_rrset_data* pd = (struct packed_rrset_data*)
5169982a05dSsthen 		regional_alloc_zero(z->region, sizeof(*pd));
5179982a05dSsthen 	struct ub_packed_rrset_key* rrset_negative = (struct ub_packed_rrset_key*)
5189982a05dSsthen 		regional_alloc_zero(z->region, sizeof(*rrset_negative));
5199982a05dSsthen 	time_t minimum;
5209982a05dSsthen 	if(!rrset_negative||!pd) {
5219982a05dSsthen 		log_err("out of memory");
5229982a05dSsthen 		return 0;
5239982a05dSsthen 	}
5249982a05dSsthen 	/* Mark the original SOA record and then continue with the negative one. */
5259982a05dSsthen 	z->soa = soa_rrset;
5269982a05dSsthen 	rrset_negative->entry.key = rrset_negative;
5279982a05dSsthen 	pd->trust = rrset_trust_prim_noglue;
5289982a05dSsthen 	pd->security = sec_status_insecure;
5299982a05dSsthen 	rrset_negative->entry.data = pd;
5309982a05dSsthen 	rrset_negative->rk.dname = soa_rrset->rk.dname;
5319982a05dSsthen 	rrset_negative->rk.dname_len = soa_rrset->rk.dname_len;
5329982a05dSsthen 	rrset_negative->rk.type = soa_rrset->rk.type;
5339982a05dSsthen 	rrset_negative->rk.rrset_class = soa_rrset->rk.rrset_class;
5349982a05dSsthen 	if(!rrset_insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr))
5359982a05dSsthen 		return 0;
5369982a05dSsthen 	/* last 4 bytes are minimum ttl in network format */
5379982a05dSsthen 	if(pd->count == 0 || pd->rr_len[0] < 2+4)
5389982a05dSsthen 		return 0;
5399982a05dSsthen 	minimum = (time_t)sldns_read_uint32(pd->rr_data[0]+(pd->rr_len[0]-4));
5409982a05dSsthen 	minimum = ttl<minimum?ttl:minimum;
5419982a05dSsthen 	pd->ttl = minimum;
5429982a05dSsthen 	pd->rr_ttl[0] = minimum;
5439982a05dSsthen 
5449982a05dSsthen 	z->soa_negative = rrset_negative;
5459982a05dSsthen 	return 1;
5469982a05dSsthen }
5479982a05dSsthen 
548eaf2578eSsthen int
549eaf2578eSsthen local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
550eaf2578eSsthen 	int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl,
551eaf2578eSsthen 	uint8_t* rdata, size_t rdata_len, const char* rrstr)
552933707f3Ssthen {
553933707f3Ssthen 	struct local_data* node;
554933707f3Ssthen 	struct local_rrset* rrset;
555933707f3Ssthen 	struct packed_rrset_data* pd;
556eaf2578eSsthen 
557933707f3Ssthen 	if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) {
558933707f3Ssthen 		return 0;
559933707f3Ssthen 	}
560933707f3Ssthen 	log_assert(node);
561933707f3Ssthen 
56277079be7Ssthen 	/* Reject it if we would end up having CNAME and other data (including
56377079be7Ssthen 	 * another CNAME) for a redirect zone. */
564c3b38330Ssthen 	if((z->type == local_zone_redirect ||
565c3b38330Ssthen 		z->type == local_zone_inform_redirect) && node->rrsets) {
56677079be7Ssthen 		const char* othertype = NULL;
56777079be7Ssthen 		if (rrtype == LDNS_RR_TYPE_CNAME)
56877079be7Ssthen 			othertype = "other";
56977079be7Ssthen 		else if (node->rrsets->rrset->rk.type ==
57077079be7Ssthen 			 htons(LDNS_RR_TYPE_CNAME)) {
57177079be7Ssthen 			othertype = "CNAME";
57277079be7Ssthen 		}
57377079be7Ssthen 		if(othertype) {
57477079be7Ssthen 			log_err("local-data '%s' in redirect zone must not "
57577079be7Ssthen 				"coexist with %s local-data", rrstr, othertype);
57677079be7Ssthen 			return 0;
57777079be7Ssthen 		}
57877079be7Ssthen 	}
57977079be7Ssthen 	rrset = local_data_find_type(node, rrtype, 0);
580933707f3Ssthen 	if(!rrset) {
581933707f3Ssthen 		rrset = new_local_rrset(z->region, node, rrtype, rrclass);
582933707f3Ssthen 		if(!rrset)
583933707f3Ssthen 			return 0;
584933707f3Ssthen 		if(query_dname_compare(node->name, z->name) == 0) {
585933707f3Ssthen 			if(rrtype == LDNS_RR_TYPE_NSEC)
586933707f3Ssthen 			  rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX;
5879982a05dSsthen 			if(rrtype == LDNS_RR_TYPE_SOA &&
5889982a05dSsthen 				!lz_mark_soa_for_zone(z, rrset->rrset, rdata, rdata_len, ttl,
5899982a05dSsthen 					rrstr))
5909982a05dSsthen 				return 0;
591933707f3Ssthen 		}
592933707f3Ssthen 	}
593933707f3Ssthen 	pd = (struct packed_rrset_data*)rrset->rrset->entry.data;
594933707f3Ssthen 	log_assert(rrset && pd);
595933707f3Ssthen 
596933707f3Ssthen 	/* check for duplicate RR */
5975d76a658Ssthen 	if(rr_is_duplicate(pd, rdata, rdata_len)) {
598933707f3Ssthen 		verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr);
599933707f3Ssthen 		return 1;
600933707f3Ssthen 	}
6012be9e038Ssthen 	return rrset_insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr);
602933707f3Ssthen }
603933707f3Ssthen 
604eaf2578eSsthen /** enter data RR into auth zone */
605a3167c07Ssthen static int
606eaf2578eSsthen lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr)
607eaf2578eSsthen {
608eaf2578eSsthen 	uint8_t* nm;
609eaf2578eSsthen 	size_t nmlen;
610eaf2578eSsthen 	int nmlabs, ret;
611eaf2578eSsthen 	uint16_t rrtype = 0, rrclass = 0;
612eaf2578eSsthen 	time_t ttl = 0;
613eaf2578eSsthen 	uint8_t rr[LDNS_RR_BUF_SIZE];
614eaf2578eSsthen 	uint8_t* rdata;
615eaf2578eSsthen 	size_t rdata_len;
616eaf2578eSsthen 	if(!rrstr_get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr,
617eaf2578eSsthen 		sizeof(rr), &rdata, &rdata_len)) {
618eaf2578eSsthen 		log_err("bad local-data: %s", rrstr);
619eaf2578eSsthen 		return 0;
620eaf2578eSsthen 	}
621eaf2578eSsthen 	log_assert(z->dclass == rrclass);
622eaf2578eSsthen 	if((z->type == local_zone_redirect ||
623eaf2578eSsthen 		z->type == local_zone_inform_redirect) &&
624eaf2578eSsthen 		query_dname_compare(z->name, nm) != 0) {
625eaf2578eSsthen 		log_err("local-data in redirect zone must reside at top of zone"
626eaf2578eSsthen 			", not at %s", rrstr);
627eaf2578eSsthen 		free(nm);
628eaf2578eSsthen 		return 0;
629eaf2578eSsthen 	}
630eaf2578eSsthen 	nmlabs = dname_count_size_labels(nm, &nmlen);
631eaf2578eSsthen 	ret = local_zone_enter_rr(z, nm, nmlen, nmlabs, rrtype, rrclass, ttl,
632eaf2578eSsthen 		rdata, rdata_len, rrstr);
633eaf2578eSsthen 	free(nm);
634eaf2578eSsthen 	return ret;
635eaf2578eSsthen }
636eaf2578eSsthen 
637933707f3Ssthen /** enter a data RR into auth data; a zone for it must exist */
638933707f3Ssthen static int
6395d76a658Ssthen lz_enter_rr_str(struct local_zones* zones, const char* rr)
640933707f3Ssthen {
641933707f3Ssthen 	uint8_t* rr_name;
6427191de28Ssthen 	uint16_t rr_class, rr_type;
643933707f3Ssthen 	size_t len;
644933707f3Ssthen 	int labs;
645933707f3Ssthen 	struct local_zone* z;
646933707f3Ssthen 	int r;
6477191de28Ssthen 	if(!get_rr_nameclass(rr, &rr_name, &rr_class, &rr_type)) {
648933707f3Ssthen 		log_err("bad rr %s", rr);
649933707f3Ssthen 		return 0;
650933707f3Ssthen 	}
651933707f3Ssthen 	labs = dname_count_size_labels(rr_name, &len);
6525d76a658Ssthen 	lock_rw_rdlock(&zones->lock);
6537191de28Ssthen 	z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type);
654933707f3Ssthen 	if(!z) {
6555d76a658Ssthen 		lock_rw_unlock(&zones->lock);
656933707f3Ssthen 		fatal_exit("internal error: no zone for rr %s", rr);
657933707f3Ssthen 	}
658933707f3Ssthen 	lock_rw_wrlock(&z->lock);
6595d76a658Ssthen 	lock_rw_unlock(&zones->lock);
660933707f3Ssthen 	free(rr_name);
6615d76a658Ssthen 	r = lz_enter_rr_into_zone(z, rr);
662933707f3Ssthen 	lock_rw_unlock(&z->lock);
663933707f3Ssthen 	return r;
664933707f3Ssthen }
665933707f3Ssthen 
6662ee382b6Ssthen /** enter tagstring into zone */
6672ee382b6Ssthen static int
6682ee382b6Ssthen lz_enter_zone_tag(struct local_zones* zones, char* zname, uint8_t* list,
6692ee382b6Ssthen 	size_t len, uint16_t rr_class)
6702ee382b6Ssthen {
6712ee382b6Ssthen 	uint8_t dname[LDNS_MAX_DOMAINLEN+1];
6722ee382b6Ssthen 	size_t dname_len = sizeof(dname);
6732ee382b6Ssthen 	int dname_labs, r = 0;
6742ee382b6Ssthen 	struct local_zone* z;
6752ee382b6Ssthen 
6762ee382b6Ssthen 	if(sldns_str2wire_dname_buf(zname, dname, &dname_len) != 0) {
6772ee382b6Ssthen 		log_err("cannot parse zone name in local-zone-tag: %s", zname);
6782ee382b6Ssthen 		return 0;
6792ee382b6Ssthen 	}
6802ee382b6Ssthen 	dname_labs = dname_count_labels(dname);
6812ee382b6Ssthen 
6822ee382b6Ssthen 	lock_rw_rdlock(&zones->lock);
68377079be7Ssthen 	z = local_zones_find(zones, dname, dname_len, dname_labs, rr_class);
6842ee382b6Ssthen 	if(!z) {
6852ee382b6Ssthen 		lock_rw_unlock(&zones->lock);
6862ee382b6Ssthen 		log_err("no local-zone for tag %s", zname);
6872ee382b6Ssthen 		return 0;
6882ee382b6Ssthen 	}
6892ee382b6Ssthen 	lock_rw_wrlock(&z->lock);
6902ee382b6Ssthen 	lock_rw_unlock(&zones->lock);
6912ee382b6Ssthen 	free(z->taglist);
6922ee382b6Ssthen 	z->taglist = memdup(list, len);
6932ee382b6Ssthen 	z->taglen = len;
6942ee382b6Ssthen 	if(z->taglist)
6952ee382b6Ssthen 		r = 1;
6962ee382b6Ssthen 	lock_rw_unlock(&z->lock);
6972ee382b6Ssthen 	return r;
6982ee382b6Ssthen }
6992ee382b6Ssthen 
70077079be7Ssthen /** enter override into zone */
70177079be7Ssthen static int
70277079be7Ssthen lz_enter_override(struct local_zones* zones, char* zname, char* netblock,
70377079be7Ssthen 	char* type, uint16_t rr_class)
70477079be7Ssthen {
70577079be7Ssthen 	uint8_t dname[LDNS_MAX_DOMAINLEN+1];
70677079be7Ssthen 	size_t dname_len = sizeof(dname);
70777079be7Ssthen 	int dname_labs;
70877079be7Ssthen 	struct sockaddr_storage addr;
70977079be7Ssthen 	int net;
71077079be7Ssthen 	socklen_t addrlen;
71177079be7Ssthen 	struct local_zone* z;
71277079be7Ssthen 	enum localzone_type t;
71377079be7Ssthen 
71477079be7Ssthen 	/* parse zone name */
71577079be7Ssthen 	if(sldns_str2wire_dname_buf(zname, dname, &dname_len) != 0) {
71677079be7Ssthen 		log_err("cannot parse zone name in local-zone-override: %s %s",
71777079be7Ssthen 			zname, netblock);
71877079be7Ssthen 		return 0;
71977079be7Ssthen 	}
72077079be7Ssthen 	dname_labs = dname_count_labels(dname);
72177079be7Ssthen 
72277079be7Ssthen 	/* parse netblock */
72377079be7Ssthen 	if(!netblockstrtoaddr(netblock, UNBOUND_DNS_PORT, &addr, &addrlen,
72477079be7Ssthen 		&net)) {
72577079be7Ssthen 		log_err("cannot parse netblock in local-zone-override: %s %s",
72677079be7Ssthen 			zname, netblock);
72777079be7Ssthen 		return 0;
72877079be7Ssthen 	}
72977079be7Ssthen 
73077079be7Ssthen 	/* parse zone type */
73177079be7Ssthen 	if(!local_zone_str2type(type, &t)) {
73277079be7Ssthen 		log_err("cannot parse type in local-zone-override: %s %s %s",
73377079be7Ssthen 			zname, netblock, type);
73477079be7Ssthen 		return 0;
73577079be7Ssthen 	}
73677079be7Ssthen 
73777079be7Ssthen 	/* find localzone entry */
73877079be7Ssthen 	lock_rw_rdlock(&zones->lock);
73977079be7Ssthen 	z = local_zones_find(zones, dname, dname_len, dname_labs, rr_class);
74077079be7Ssthen 	if(!z) {
74177079be7Ssthen 		lock_rw_unlock(&zones->lock);
74277079be7Ssthen 		log_err("no local-zone for local-zone-override %s", zname);
74377079be7Ssthen 		return 0;
74477079be7Ssthen 	}
74577079be7Ssthen 	lock_rw_wrlock(&z->lock);
74677079be7Ssthen 	lock_rw_unlock(&zones->lock);
74777079be7Ssthen 
74877079be7Ssthen 	/* create netblock addr_tree if not present yet */
74977079be7Ssthen 	if(!z->override_tree) {
75077079be7Ssthen 		z->override_tree = (struct rbtree_type*)regional_alloc_zero(
75177079be7Ssthen 			z->region, sizeof(*z->override_tree));
75277079be7Ssthen 		if(!z->override_tree) {
75377079be7Ssthen 			lock_rw_unlock(&z->lock);
75477079be7Ssthen 			log_err("out of memory");
75577079be7Ssthen 			return 0;
75677079be7Ssthen 		}
75777079be7Ssthen 		addr_tree_init(z->override_tree);
75877079be7Ssthen 	}
75977079be7Ssthen 	/* add new elem to tree */
76077079be7Ssthen 	if(z->override_tree) {
76177079be7Ssthen 		struct local_zone_override* n;
76277079be7Ssthen 		n = (struct local_zone_override*)regional_alloc_zero(
76377079be7Ssthen 			z->region, sizeof(*n));
76477079be7Ssthen 		if(!n) {
76577079be7Ssthen 			lock_rw_unlock(&z->lock);
76677079be7Ssthen 			log_err("out of memory");
76777079be7Ssthen 			return 0;
76877079be7Ssthen 		}
76977079be7Ssthen 		n->type = t;
77077079be7Ssthen 		if(!addr_tree_insert(z->override_tree,
77177079be7Ssthen 			(struct addr_tree_node*)n, &addr, addrlen, net)) {
77277079be7Ssthen 			lock_rw_unlock(&z->lock);
77377079be7Ssthen 			log_err("duplicate local-zone-override %s %s",
77477079be7Ssthen 				zname, netblock);
77577079be7Ssthen 			return 1;
77677079be7Ssthen 		}
77777079be7Ssthen 	}
77877079be7Ssthen 
77977079be7Ssthen 	lock_rw_unlock(&z->lock);
78077079be7Ssthen 	return 1;
78177079be7Ssthen }
78277079be7Ssthen 
783933707f3Ssthen /** parse local-zone: statements */
784933707f3Ssthen static int
785933707f3Ssthen lz_enter_zones(struct local_zones* zones, struct config_file* cfg)
786933707f3Ssthen {
787933707f3Ssthen 	struct config_str2list* p;
788191f22c6Ssthen #ifndef THREADS_DISABLED
789933707f3Ssthen 	struct local_zone* z;
790191f22c6Ssthen #endif
791933707f3Ssthen 	for(p = cfg->local_zones; p; p = p->next) {
792191f22c6Ssthen 		if(!(
793191f22c6Ssthen #ifndef THREADS_DISABLED
794191f22c6Ssthen 			z=
795191f22c6Ssthen #endif
796191f22c6Ssthen 			lz_enter_zone(zones, p->str, p->str2,
797933707f3Ssthen 			LDNS_RR_CLASS_IN)))
798933707f3Ssthen 			return 0;
799933707f3Ssthen 		lock_rw_unlock(&z->lock);
800933707f3Ssthen 	}
801933707f3Ssthen 	return 1;
802933707f3Ssthen }
803933707f3Ssthen 
804933707f3Ssthen /** lookup a zone in rbtree; exact match only; SLOW due to parse */
805933707f3Ssthen static int
806933707f3Ssthen lz_exists(struct local_zones* zones, const char* name)
807933707f3Ssthen {
808933707f3Ssthen 	struct local_zone z;
809933707f3Ssthen 	z.node.key = &z;
810933707f3Ssthen 	z.dclass = LDNS_RR_CLASS_IN;
811933707f3Ssthen 	if(!parse_dname(name, &z.name, &z.namelen, &z.namelabs)) {
812933707f3Ssthen 		log_err("bad name %s", name);
813933707f3Ssthen 		return 0;
814933707f3Ssthen 	}
8155d76a658Ssthen 	lock_rw_rdlock(&zones->lock);
816933707f3Ssthen 	if(rbtree_search(&zones->ztree, &z.node)) {
8175d76a658Ssthen 		lock_rw_unlock(&zones->lock);
818933707f3Ssthen 		free(z.name);
819933707f3Ssthen 		return 1;
820933707f3Ssthen 	}
8215d76a658Ssthen 	lock_rw_unlock(&zones->lock);
822933707f3Ssthen 	free(z.name);
823933707f3Ssthen 	return 0;
824933707f3Ssthen }
825933707f3Ssthen 
826933707f3Ssthen /** lookup a zone in cfg->nodefault list */
827933707f3Ssthen static int
828933707f3Ssthen lz_nodefault(struct config_file* cfg, const char* name)
829933707f3Ssthen {
830933707f3Ssthen 	struct config_strlist* p;
831933707f3Ssthen 	size_t len = strlen(name);
832933707f3Ssthen 	if(len == 0) return 0;
833933707f3Ssthen 	if(name[len-1] == '.') len--;
834933707f3Ssthen 
835933707f3Ssthen 	for(p = cfg->local_zones_nodefault; p; p = p->next) {
836933707f3Ssthen 		/* compare zone name, lowercase, compare without ending . */
837933707f3Ssthen 		if(strncasecmp(p->str, name, len) == 0 &&
838933707f3Ssthen 			(strlen(p->str) == len || (strlen(p->str)==len+1 &&
839933707f3Ssthen 			p->str[len] == '.')))
840933707f3Ssthen 			return 1;
841933707f3Ssthen 	}
842933707f3Ssthen 	return 0;
843933707f3Ssthen }
844933707f3Ssthen 
8457191de28Ssthen /** enter (AS112) empty default zone */
846933707f3Ssthen static int
8477191de28Ssthen add_empty_default(struct local_zones* zones, struct config_file* cfg,
8485d76a658Ssthen         const char* name)
849933707f3Ssthen {
850933707f3Ssthen 	struct local_zone* z;
851933707f3Ssthen 	char str[1024]; /* known long enough */
852933707f3Ssthen 	if(lz_exists(zones, name) || lz_nodefault(cfg, name))
853933707f3Ssthen 		return 1; /* do not enter default content */
854933707f3Ssthen 	if(!(z=lz_enter_zone(zones, name, "static", LDNS_RR_CLASS_IN)))
855933707f3Ssthen 		return 0;
856933707f3Ssthen 	snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. "
857933707f3Ssthen 		"nobody.invalid. 1 3600 1200 604800 10800", name);
8585d76a658Ssthen 	if(!lz_enter_rr_into_zone(z, str)) {
859933707f3Ssthen 		lock_rw_unlock(&z->lock);
860933707f3Ssthen 		return 0;
861933707f3Ssthen 	}
862933707f3Ssthen 	snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name);
8635d76a658Ssthen 	if(!lz_enter_rr_into_zone(z, str)) {
864933707f3Ssthen 		lock_rw_unlock(&z->lock);
865933707f3Ssthen 		return 0;
866933707f3Ssthen 	}
867933707f3Ssthen 	lock_rw_unlock(&z->lock);
868933707f3Ssthen 	return 1;
869933707f3Ssthen }
870933707f3Ssthen 
871933707f3Ssthen /** enter default zones */
8722be9e038Ssthen int local_zone_enter_defaults(struct local_zones* zones, struct config_file* cfg)
873933707f3Ssthen {
874933707f3Ssthen 	struct local_zone* z;
87532e31f52Ssthen 	const char** zstr;
876933707f3Ssthen 
8772be9e038Ssthen 	/* Do not add any default */
8782be9e038Ssthen 	if(cfg->local_zones_disable_default)
8792be9e038Ssthen 		return 1;
8802be9e038Ssthen 
88132e31f52Ssthen 	/* this list of zones is from RFC 6303 and RFC 7686 */
882933707f3Ssthen 
88332e31f52Ssthen 	/* block localhost level zones first, then onion and later the LAN zones */
88498f3ca02Sbrad 
885933707f3Ssthen 	/* localhost. zone */
886933707f3Ssthen 	if(!lz_exists(zones, "localhost.") &&
887933707f3Ssthen 		!lz_nodefault(cfg, "localhost.")) {
8887191de28Ssthen 		if(!(z=lz_enter_zone(zones, "localhost.", "redirect",
889933707f3Ssthen 			LDNS_RR_CLASS_IN)) ||
8905d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
891933707f3Ssthen 			"localhost. 10800 IN NS localhost.") ||
8925d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
893933707f3Ssthen 			"localhost. 10800 IN SOA localhost. nobody.invalid. "
894933707f3Ssthen 			"1 3600 1200 604800 10800") ||
8955d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
896933707f3Ssthen 			"localhost. 10800 IN A 127.0.0.1") ||
8975d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
898933707f3Ssthen 			"localhost. 10800 IN AAAA ::1")) {
899933707f3Ssthen 			log_err("out of memory adding default zone");
900933707f3Ssthen 			if(z) { lock_rw_unlock(&z->lock); }
901933707f3Ssthen 			return 0;
902933707f3Ssthen 		}
903933707f3Ssthen 		lock_rw_unlock(&z->lock);
904933707f3Ssthen 	}
905933707f3Ssthen 	/* reverse ip4 zone */
906933707f3Ssthen 	if(!lz_exists(zones, "127.in-addr.arpa.") &&
907933707f3Ssthen 		!lz_nodefault(cfg, "127.in-addr.arpa.")) {
908933707f3Ssthen 		if(!(z=lz_enter_zone(zones, "127.in-addr.arpa.", "static",
909933707f3Ssthen 			LDNS_RR_CLASS_IN)) ||
9105d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
911933707f3Ssthen 			"127.in-addr.arpa. 10800 IN NS localhost.") ||
9125d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
913933707f3Ssthen 			"127.in-addr.arpa. 10800 IN SOA localhost. "
914933707f3Ssthen 			"nobody.invalid. 1 3600 1200 604800 10800") ||
9155d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
916933707f3Ssthen 			"1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) {
917933707f3Ssthen 			log_err("out of memory adding default zone");
918933707f3Ssthen 			if(z) { lock_rw_unlock(&z->lock); }
919933707f3Ssthen 			return 0;
920933707f3Ssthen 		}
921933707f3Ssthen 		lock_rw_unlock(&z->lock);
922933707f3Ssthen 	}
923933707f3Ssthen 	/* reverse ip6 zone */
924933707f3Ssthen 	if(!lz_exists(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") &&
925933707f3Ssthen 		!lz_nodefault(cfg, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.")) {
926933707f3Ssthen 		if(!(z=lz_enter_zone(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", "static",
927933707f3Ssthen 			LDNS_RR_CLASS_IN)) ||
9285d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
929933707f3Ssthen 			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN NS localhost.") ||
9305d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
931933707f3Ssthen 			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN SOA localhost. "
932933707f3Ssthen 			"nobody.invalid. 1 3600 1200 604800 10800") ||
9335d76a658Ssthen 		   !lz_enter_rr_into_zone(z,
934933707f3Ssthen 			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN PTR localhost.")) {
935933707f3Ssthen 			log_err("out of memory adding default zone");
936933707f3Ssthen 			if(z) { lock_rw_unlock(&z->lock); }
937933707f3Ssthen 			return 0;
938933707f3Ssthen 		}
939933707f3Ssthen 		lock_rw_unlock(&z->lock);
940933707f3Ssthen 	}
941e21c60efSsthen 	/* home.arpa. zone (RFC 8375) */
942e21c60efSsthen 	if(!add_empty_default(zones, cfg, "home.arpa.")) {
943e21c60efSsthen 		log_err("out of memory adding default zone");
944e21c60efSsthen 		return 0;
945e21c60efSsthen 	}
94632e31f52Ssthen 	/* onion. zone (RFC 7686) */
9477191de28Ssthen 	if(!add_empty_default(zones, cfg, "onion.")) {
94832e31f52Ssthen 		log_err("out of memory adding default zone");
94932e31f52Ssthen 		return 0;
95032e31f52Ssthen 	}
951eaf2578eSsthen 	/* test. zone (RFC 6761) */
9527191de28Ssthen 	if(!add_empty_default(zones, cfg, "test.")) {
9537191de28Ssthen 		log_err("out of memory adding default zone");
9547191de28Ssthen 		return 0;
95532e31f52Ssthen 	}
956eaf2578eSsthen 	/* invalid. zone (RFC 6761) */
9577191de28Ssthen 	if(!add_empty_default(zones, cfg, "invalid.")) {
9587191de28Ssthen 		log_err("out of memory adding default zone");
9597191de28Ssthen 		return 0;
9607191de28Ssthen 	}
96132e31f52Ssthen 	/* block AS112 zones, unless asked not to */
96232e31f52Ssthen 	if(!cfg->unblock_lan_zones) {
96332e31f52Ssthen 		for(zstr = as112_zones; *zstr; zstr++) {
9647191de28Ssthen 			if(!add_empty_default(zones, cfg, *zstr)) {
965933707f3Ssthen 				log_err("out of memory adding default zone");
966933707f3Ssthen 				return 0;
967933707f3Ssthen 			}
96832e31f52Ssthen 		}
96932e31f52Ssthen 	}
970933707f3Ssthen 	return 1;
971933707f3Ssthen }
972933707f3Ssthen 
97377079be7Ssthen /** parse local-zone-override: statements */
97477079be7Ssthen static int
97577079be7Ssthen lz_enter_overrides(struct local_zones* zones, struct config_file* cfg)
97677079be7Ssthen {
97777079be7Ssthen 	struct config_str3list* p;
97877079be7Ssthen 	for(p = cfg->local_zone_overrides; p; p = p->next) {
97977079be7Ssthen 		if(!lz_enter_override(zones, p->str, p->str2, p->str3,
98077079be7Ssthen 			LDNS_RR_CLASS_IN))
98177079be7Ssthen 			return 0;
98277079be7Ssthen 	}
98377079be7Ssthen 	return 1;
98477079be7Ssthen }
98577079be7Ssthen 
986*98bc733bSsthen /* return closest parent in the tree, NULL if none */
987*98bc733bSsthen static struct local_zone* find_closest_parent(struct local_zone* curr,
988*98bc733bSsthen 	struct local_zone* prev)
989933707f3Ssthen {
990*98bc733bSsthen 	struct local_zone* p;
991933707f3Ssthen         int m;
992*98bc733bSsthen 	if(!prev || prev->dclass != curr->dclass) return NULL;
993*98bc733bSsthen 	(void)dname_lab_cmp(prev->name, prev->namelabs, curr->name,
994*98bc733bSsthen 		curr->namelabs, &m); /* we know prev is smaller */
995933707f3Ssthen                 /* sort order like: . com. bla.com. zwb.com. net. */
996933707f3Ssthen                 /* find the previous, or parent-parent-parent */
997*98bc733bSsthen 	for(p = prev; p; p = p->parent) {
998933707f3Ssthen                         /* looking for name with few labels, a parent */
999933707f3Ssthen                         if(p->namelabs <= m) {
1000933707f3Ssthen                                 /* ==: since prev matched m, this is closest*/
1001933707f3Ssthen                                 /* <: prev matches more, but is not a parent,
1002933707f3Ssthen                                  * this one is a (grand)parent */
1003*98bc733bSsthen 			return p;
1004933707f3Ssthen 		}
1005*98bc733bSsthen 	}
1006*98bc733bSsthen 	return NULL;
1007*98bc733bSsthen                         }
100877079be7Ssthen 
1009*98bc733bSsthen /** setup parent pointers, so that a lookup can be done for closest match */
1010*98bc733bSsthen void
1011*98bc733bSsthen lz_init_parents(struct local_zones* zones)
1012*98bc733bSsthen {
1013*98bc733bSsthen 	struct local_zone* node, *prev = NULL;
1014*98bc733bSsthen 	lock_rw_wrlock(&zones->lock);
1015*98bc733bSsthen 	RBTREE_FOR(node, struct local_zone*, &zones->ztree) {
1016*98bc733bSsthen 		lock_rw_wrlock(&node->lock);
1017*98bc733bSsthen 		node->parent = find_closest_parent(node, prev);
1018*98bc733bSsthen 		prev = node;
101977079be7Ssthen 		if(node->override_tree)
102077079be7Ssthen 			addr_tree_init_parents(node->override_tree);
1021933707f3Ssthen 		lock_rw_unlock(&node->lock);
1022933707f3Ssthen         }
10235d76a658Ssthen 	lock_rw_unlock(&zones->lock);
1024933707f3Ssthen }
1025933707f3Ssthen 
1026933707f3Ssthen /** enter implicit transparent zone for local-data: without local-zone: */
1027933707f3Ssthen static int
1028933707f3Ssthen lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
1029933707f3Ssthen {
1030933707f3Ssthen 	/* walk over all items that have no parent zone and find
1031933707f3Ssthen 	 * the name that covers them all (could be the root) and
1032933707f3Ssthen 	 * add that as a transparent zone */
1033933707f3Ssthen 	struct config_strlist* p;
1034933707f3Ssthen 	int have_name = 0;
1035933707f3Ssthen 	int have_other_classes = 0;
1036933707f3Ssthen 	uint16_t dclass = 0;
1037933707f3Ssthen 	uint8_t* nm = 0;
1038933707f3Ssthen 	size_t nmlen = 0;
1039933707f3Ssthen 	int nmlabs = 0;
1040933707f3Ssthen 	int match = 0; /* number of labels match count */
1041933707f3Ssthen 
1042*98bc733bSsthen 	lz_init_parents(zones); /* to enable local_zones_lookup() */
1043933707f3Ssthen 	for(p = cfg->local_data; p; p = p->next) {
1044933707f3Ssthen 		uint8_t* rr_name;
10457191de28Ssthen 		uint16_t rr_class, rr_type;
1046933707f3Ssthen 		size_t len;
1047933707f3Ssthen 		int labs;
10487191de28Ssthen 		if(!get_rr_nameclass(p->str, &rr_name, &rr_class, &rr_type)) {
1049933707f3Ssthen 			log_err("Bad local-data RR %s", p->str);
1050933707f3Ssthen 			return 0;
1051933707f3Ssthen 		}
1052933707f3Ssthen 		labs = dname_count_size_labels(rr_name, &len);
10535d76a658Ssthen 		lock_rw_rdlock(&zones->lock);
10547191de28Ssthen 		if(!local_zones_lookup(zones, rr_name, len, labs, rr_class,
10557191de28Ssthen 			rr_type)) {
1056e21c60efSsthen 			/* Check if there is a zone that this could go
1057e21c60efSsthen 			 * under but for different class; created zones are
1058e21c60efSsthen 			 * always for LDNS_RR_CLASS_IN. Create the zone with
1059e21c60efSsthen 			 * a different class but the same configured
1060e21c60efSsthen 			 * local_zone_type. */
1061e21c60efSsthen 			struct local_zone* z = local_zones_lookup(zones,
1062e21c60efSsthen 				rr_name, len, labs, LDNS_RR_CLASS_IN, rr_type);
1063e21c60efSsthen 			if(z) {
1064e21c60efSsthen 				uint8_t* name = memdup(z->name, z->namelen);
1065e21c60efSsthen 				size_t znamelen = z->namelen;
1066e21c60efSsthen 				int znamelabs = z->namelabs;
1067e21c60efSsthen 				enum localzone_type ztype = z->type;
1068e21c60efSsthen 				lock_rw_unlock(&zones->lock);
1069e21c60efSsthen 				if(!name) {
1070e21c60efSsthen 					log_err("out of memory");
1071e21c60efSsthen 					free(rr_name);
1072e21c60efSsthen 					return 0;
1073e21c60efSsthen 				}
1074e21c60efSsthen 				if(!(
1075e21c60efSsthen #ifndef THREADS_DISABLED
1076e21c60efSsthen 					z =
1077e21c60efSsthen #endif
1078e21c60efSsthen 					lz_enter_zone_dname(zones, name,
1079e21c60efSsthen 						znamelen, znamelabs,
1080e21c60efSsthen 						ztype, rr_class))) {
1081e21c60efSsthen 					free(rr_name);
1082e21c60efSsthen 					return 0;
1083e21c60efSsthen 				}
1084e21c60efSsthen 				lock_rw_unlock(&z->lock);
1085e21c60efSsthen 				free(rr_name);
1086e21c60efSsthen 				continue;
1087e21c60efSsthen 			}
1088933707f3Ssthen 			if(!have_name) {
1089933707f3Ssthen 				dclass = rr_class;
1090933707f3Ssthen 				nm = rr_name;
1091933707f3Ssthen 				nmlen = len;
1092933707f3Ssthen 				nmlabs = labs;
1093933707f3Ssthen 				match = labs;
1094933707f3Ssthen 				have_name = 1;
1095933707f3Ssthen 			} else {
1096933707f3Ssthen 				int m;
1097933707f3Ssthen 				if(rr_class != dclass) {
1098933707f3Ssthen 					/* process other classes later */
1099933707f3Ssthen 					free(rr_name);
1100933707f3Ssthen 					have_other_classes = 1;
11015d76a658Ssthen 					lock_rw_unlock(&zones->lock);
1102933707f3Ssthen 					continue;
1103933707f3Ssthen 				}
1104933707f3Ssthen 				/* find smallest shared topdomain */
1105933707f3Ssthen 				(void)dname_lab_cmp(nm, nmlabs,
1106933707f3Ssthen 					rr_name, labs, &m);
1107933707f3Ssthen 				free(rr_name);
1108933707f3Ssthen 				if(m < match)
1109933707f3Ssthen 					match = m;
1110933707f3Ssthen 			}
1111933707f3Ssthen 		} else free(rr_name);
11125d76a658Ssthen 		lock_rw_unlock(&zones->lock);
1113933707f3Ssthen 	}
1114933707f3Ssthen 	if(have_name) {
1115933707f3Ssthen 		uint8_t* n2;
1116191f22c6Ssthen #ifndef THREADS_DISABLED
1117933707f3Ssthen 		struct local_zone* z;
1118191f22c6Ssthen #endif
1119933707f3Ssthen 		/* allocate zone of smallest shared topdomain to contain em */
1120933707f3Ssthen 		n2 = nm;
1121933707f3Ssthen 		dname_remove_labels(&n2, &nmlen, nmlabs - match);
1122933707f3Ssthen 		n2 = memdup(n2, nmlen);
1123933707f3Ssthen 		free(nm);
1124933707f3Ssthen 		if(!n2) {
1125933707f3Ssthen 			log_err("out of memory");
1126933707f3Ssthen 			return 0;
1127933707f3Ssthen 		}
1128933707f3Ssthen 		log_nametypeclass(VERB_ALGO, "implicit transparent local-zone",
1129933707f3Ssthen 			n2, 0, dclass);
1130191f22c6Ssthen 		if(!(
1131191f22c6Ssthen #ifndef THREADS_DISABLED
1132191f22c6Ssthen 			z=
1133191f22c6Ssthen #endif
1134191f22c6Ssthen 			lz_enter_zone_dname(zones, n2, nmlen, match,
1135933707f3Ssthen 			local_zone_transparent, dclass))) {
1136933707f3Ssthen 			return 0;
1137933707f3Ssthen 		}
1138933707f3Ssthen 		lock_rw_unlock(&z->lock);
1139933707f3Ssthen 	}
1140933707f3Ssthen 	if(have_other_classes) {
1141933707f3Ssthen 		/* restart to setup other class */
1142933707f3Ssthen 		return lz_setup_implicit(zones, cfg);
1143933707f3Ssthen 	}
1144933707f3Ssthen 	return 1;
1145933707f3Ssthen }
1146933707f3Ssthen 
11472ee382b6Ssthen /** enter local-zone-tag info */
11482ee382b6Ssthen static int
11492ee382b6Ssthen lz_enter_zone_tags(struct local_zones* zones, struct config_file* cfg)
11502ee382b6Ssthen {
11512ee382b6Ssthen 	struct config_strbytelist* p;
11522ee382b6Ssthen 	int c = 0;
11532ee382b6Ssthen 	for(p = cfg->local_zone_tags; p; p = p->next) {
11542ee382b6Ssthen 		if(!lz_enter_zone_tag(zones, p->str, p->str2, p->str2len,
11552ee382b6Ssthen 			LDNS_RR_CLASS_IN))
11562ee382b6Ssthen 			return 0;
11572ee382b6Ssthen 		c++;
11582ee382b6Ssthen 	}
11592ee382b6Ssthen 	if(c) verbose(VERB_ALGO, "applied tags to %d local zones", c);
11602ee382b6Ssthen 	return 1;
11612ee382b6Ssthen }
11622ee382b6Ssthen 
1163933707f3Ssthen /** enter auth data */
1164933707f3Ssthen static int
11655d76a658Ssthen lz_enter_data(struct local_zones* zones, struct config_file* cfg)
1166933707f3Ssthen {
1167933707f3Ssthen 	struct config_strlist* p;
1168933707f3Ssthen 	for(p = cfg->local_data; p; p = p->next) {
11695d76a658Ssthen 		if(!lz_enter_rr_str(zones, p->str))
1170933707f3Ssthen 			return 0;
1171933707f3Ssthen 	}
1172933707f3Ssthen 	return 1;
1173933707f3Ssthen }
1174933707f3Ssthen 
1175933707f3Ssthen /** free memory from config */
1176933707f3Ssthen static void
1177933707f3Ssthen lz_freeup_cfg(struct config_file* cfg)
1178933707f3Ssthen {
1179933707f3Ssthen 	config_deldblstrlist(cfg->local_zones);
1180933707f3Ssthen 	cfg->local_zones = NULL;
1181933707f3Ssthen 	config_delstrlist(cfg->local_zones_nodefault);
1182933707f3Ssthen 	cfg->local_zones_nodefault = NULL;
1183933707f3Ssthen 	config_delstrlist(cfg->local_data);
1184933707f3Ssthen 	cfg->local_data = NULL;
1185933707f3Ssthen }
1186933707f3Ssthen 
1187933707f3Ssthen int
1188933707f3Ssthen local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
1189933707f3Ssthen {
1190933707f3Ssthen 	/* create zones from zone statements. */
1191933707f3Ssthen 	if(!lz_enter_zones(zones, cfg)) {
1192933707f3Ssthen 		return 0;
1193933707f3Ssthen 	}
1194933707f3Ssthen 	/* apply default zones+content (unless disabled, or overridden) */
11952be9e038Ssthen 	if(!local_zone_enter_defaults(zones, cfg)) {
1196933707f3Ssthen 		return 0;
1197933707f3Ssthen 	}
119877079be7Ssthen 	/* enter local zone overrides */
119977079be7Ssthen 	if(!lz_enter_overrides(zones, cfg)) {
120077079be7Ssthen 		return 0;
120177079be7Ssthen 	}
1202933707f3Ssthen 	/* create implicit transparent zone from data. */
1203933707f3Ssthen 	if(!lz_setup_implicit(zones, cfg)) {
1204933707f3Ssthen 		return 0;
1205933707f3Ssthen 	}
1206933707f3Ssthen 
1207933707f3Ssthen 	/* setup parent ptrs for lookup during data entry */
1208*98bc733bSsthen 	lz_init_parents(zones);
12092ee382b6Ssthen 	/* insert local zone tags */
12102ee382b6Ssthen 	if(!lz_enter_zone_tags(zones, cfg)) {
12112ee382b6Ssthen 		return 0;
12122ee382b6Ssthen 	}
1213933707f3Ssthen 	/* insert local data */
12145d76a658Ssthen 	if(!lz_enter_data(zones, cfg)) {
1215933707f3Ssthen 		return 0;
1216933707f3Ssthen 	}
1217933707f3Ssthen 	/* freeup memory from cfg struct. */
1218933707f3Ssthen 	lz_freeup_cfg(cfg);
1219933707f3Ssthen 	return 1;
1220933707f3Ssthen }
1221933707f3Ssthen 
1222933707f3Ssthen struct local_zone*
1223933707f3Ssthen local_zones_lookup(struct local_zones* zones,
12247191de28Ssthen         uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype)
1225933707f3Ssthen {
122677079be7Ssthen 	return local_zones_tags_lookup(zones, name, len, labs,
12277191de28Ssthen 		dclass, dtype, NULL, 0, 1);
122877079be7Ssthen }
122977079be7Ssthen 
123077079be7Ssthen struct local_zone*
123177079be7Ssthen local_zones_tags_lookup(struct local_zones* zones,
12327191de28Ssthen         uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype,
123377079be7Ssthen 	uint8_t* taglist, size_t taglen, int ignoretags)
123477079be7Ssthen {
123577079be7Ssthen 	rbnode_type* res = NULL;
1236933707f3Ssthen 	struct local_zone *result;
1237933707f3Ssthen 	struct local_zone key;
123877079be7Ssthen 	int m;
12397191de28Ssthen 	/* for type DS use a zone higher when on a zonecut */
12407191de28Ssthen 	if(dtype == LDNS_RR_TYPE_DS && !dname_is_root(name)) {
12417191de28Ssthen 		dname_remove_label(&name, &len);
12427191de28Ssthen 		labs--;
12437191de28Ssthen 	}
1244933707f3Ssthen 	key.node.key = &key;
1245933707f3Ssthen 	key.dclass = dclass;
1246933707f3Ssthen 	key.name = name;
1247933707f3Ssthen 	key.namelen = len;
1248933707f3Ssthen 	key.namelabs = labs;
124977079be7Ssthen 	rbtree_find_less_equal(&zones->ztree, &key, &res);
1250933707f3Ssthen 	result = (struct local_zone*)res;
125177079be7Ssthen 	/* exact or smaller element (or no element) */
1252933707f3Ssthen 	if(!result || result->dclass != dclass)
1253933707f3Ssthen 		return NULL;
1254933707f3Ssthen 	/* count number of labels matched */
1255933707f3Ssthen 	(void)dname_lab_cmp(result->name, result->namelabs, key.name,
1256933707f3Ssthen 		key.namelabs, &m);
125777079be7Ssthen 	while(result) { /* go up until qname is zone or subdomain of zone */
1258933707f3Ssthen 		if(result->namelabs <= m)
125977079be7Ssthen 			if(ignoretags || !result->taglist ||
126077079be7Ssthen 				taglist_intersect(result->taglist,
126177079be7Ssthen 				result->taglen, taglist, taglen))
1262933707f3Ssthen 				break;
1263933707f3Ssthen 		result = result->parent;
1264933707f3Ssthen 	}
1265933707f3Ssthen 	return result;
1266933707f3Ssthen }
1267933707f3Ssthen 
1268933707f3Ssthen struct local_zone*
1269933707f3Ssthen local_zones_find(struct local_zones* zones,
1270933707f3Ssthen         uint8_t* name, size_t len, int labs, uint16_t dclass)
1271933707f3Ssthen {
1272933707f3Ssthen 	struct local_zone key;
1273933707f3Ssthen 	key.node.key = &key;
1274933707f3Ssthen 	key.dclass = dclass;
1275933707f3Ssthen 	key.name = name;
1276933707f3Ssthen 	key.namelen = len;
1277933707f3Ssthen 	key.namelabs = labs;
1278933707f3Ssthen 	/* exact */
1279933707f3Ssthen 	return (struct local_zone*)rbtree_search(&zones->ztree, &key);
1280933707f3Ssthen }
1281933707f3Ssthen 
1282eaf2578eSsthen struct local_zone*
1283eaf2578eSsthen local_zones_find_le(struct local_zones* zones,
1284eaf2578eSsthen         uint8_t* name, size_t len, int labs, uint16_t dclass,
1285eaf2578eSsthen 	int* exact)
1286eaf2578eSsthen {
1287eaf2578eSsthen 	struct local_zone key;
1288eaf2578eSsthen 	rbnode_type *node;
1289eaf2578eSsthen 	key.node.key = &key;
1290eaf2578eSsthen 	key.dclass = dclass;
1291eaf2578eSsthen 	key.name = name;
1292eaf2578eSsthen 	key.namelen = len;
1293eaf2578eSsthen 	key.namelabs = labs;
1294eaf2578eSsthen 	*exact = rbtree_find_less_equal(&zones->ztree, &key, &node);
1295eaf2578eSsthen 	return (struct local_zone*)node;
1296eaf2578eSsthen }
1297eaf2578eSsthen 
1298933707f3Ssthen /** encode answer consisting of 1 rrset */
1299933707f3Ssthen static int
130077079be7Ssthen local_encode(struct query_info* qinfo, struct module_env* env,
13012308e98cSsthen 	struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
13022308e98cSsthen 	struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec,
13032308e98cSsthen 	int rcode)
1304933707f3Ssthen {
1305933707f3Ssthen 	struct reply_info rep;
1306933707f3Ssthen 	uint16_t udpsize;
1307933707f3Ssthen 	/* make answer with time=0 for fixed TTL values */
1308933707f3Ssthen 	memset(&rep, 0, sizeof(rep));
1309933707f3Ssthen 	rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode);
1310933707f3Ssthen 	rep.qdcount = 1;
1311933707f3Ssthen 	if(ansec)
1312933707f3Ssthen 		rep.an_numrrsets = 1;
1313933707f3Ssthen 	else	rep.ns_numrrsets = 1;
1314933707f3Ssthen 	rep.rrset_count = 1;
1315933707f3Ssthen 	rep.rrsets = &rrset;
13168b7325afSsthen 	rep.reason_bogus = LDNS_EDE_NONE;
1317933707f3Ssthen 	udpsize = edns->udp_size;
1318933707f3Ssthen 	edns->edns_version = EDNS_ADVERTISED_VERSION;
1319933707f3Ssthen 	edns->udp_size = EDNS_ADVERTISED_SIZE;
1320933707f3Ssthen 	edns->ext_rcode = 0;
1321933707f3Ssthen 	edns->bits &= EDNS_DO;
13222308e98cSsthen 	if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,
13239982a05dSsthen 		repinfo, temp, env->now_tv) || !reply_info_answer_encode(qinfo, &rep,
13242308e98cSsthen 		*(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2),
13252308e98cSsthen 		buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) {
1326933707f3Ssthen 		error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
13275d76a658Ssthen 			*(uint16_t*)sldns_buffer_begin(buf),
13285d76a658Ssthen 			sldns_buffer_read_u16_at(buf, 2), edns);
13292308e98cSsthen 	}
1330933707f3Ssthen 	return 1;
1331933707f3Ssthen }
1332933707f3Ssthen 
133377079be7Ssthen /** encode local error answer */
133477079be7Ssthen static void
133577079be7Ssthen local_error_encode(struct query_info* qinfo, struct module_env* env,
13362308e98cSsthen 	struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
13370bdb4f62Ssthen 	struct regional* temp, int rcode, int r, int ede_code,
13380bdb4f62Ssthen 	const char* ede_txt)
133977079be7Ssthen {
134077079be7Ssthen 	edns->edns_version = EDNS_ADVERTISED_VERSION;
134177079be7Ssthen 	edns->udp_size = EDNS_ADVERTISED_SIZE;
134277079be7Ssthen 	edns->ext_rcode = 0;
134377079be7Ssthen 	edns->bits &= EDNS_DO;
134477079be7Ssthen 
134577079be7Ssthen 	if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL,
13469982a05dSsthen 		rcode, edns, repinfo, temp, env->now_tv))
1347e21c60efSsthen 		edns->opt_list_inplace_cb_out = NULL;
13480bdb4f62Ssthen 
13490bdb4f62Ssthen 	if(ede_code != LDNS_EDE_NONE && env->cfg->ede) {
13500bdb4f62Ssthen 		edns_opt_list_append_ede(&edns->opt_list_out, temp,
13510bdb4f62Ssthen 			ede_code, ede_txt);
13520bdb4f62Ssthen 	}
13530bdb4f62Ssthen 
135477079be7Ssthen 	error_encode(buf, r, qinfo, *(uint16_t*)sldns_buffer_begin(buf),
135577079be7Ssthen 		sldns_buffer_read_u16_at(buf, 2), edns);
135677079be7Ssthen }
135777079be7Ssthen 
135877079be7Ssthen /** find local data tag string match for the given type in the list */
13592be9e038Ssthen int
13602be9e038Ssthen local_data_find_tag_datas(const struct query_info* qinfo,
13612be9e038Ssthen 	struct config_strlist* list, struct ub_packed_rrset_key* r,
13622be9e038Ssthen 	struct regional* temp)
136377079be7Ssthen {
136477079be7Ssthen 	struct config_strlist* p;
136577079be7Ssthen 	char buf[65536];
136677079be7Ssthen 	uint8_t rr[LDNS_RR_BUF_SIZE];
136777079be7Ssthen 	size_t len;
136877079be7Ssthen 	int res;
136977079be7Ssthen 	struct packed_rrset_data* d;
137077079be7Ssthen 	for(p=list; p; p=p->next) {
137177079be7Ssthen 		uint16_t rdr_type;
137277079be7Ssthen 
137377079be7Ssthen 		len = sizeof(rr);
137477079be7Ssthen 		/* does this element match the type? */
137577079be7Ssthen 		snprintf(buf, sizeof(buf), ". %s", p->str);
137677079be7Ssthen 		res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600,
137777079be7Ssthen 			NULL, 0, NULL, 0);
137877079be7Ssthen 		if(res != 0)
137977079be7Ssthen 			/* parse errors are already checked before, in
138077079be7Ssthen 			 * acllist check_data, skip this for robustness */
138177079be7Ssthen 			continue;
138277079be7Ssthen 		if(len < 1 /* . */ + 8 /* typeclassttl*/ + 2 /*rdatalen*/)
138377079be7Ssthen 			continue;
138477079be7Ssthen 		rdr_type = sldns_wirerr_get_type(rr, len, 1);
138577079be7Ssthen 		if(rdr_type != qinfo->qtype && rdr_type != LDNS_RR_TYPE_CNAME)
138677079be7Ssthen 			continue;
138777079be7Ssthen 
138877079be7Ssthen 		/* do we have entries already? if not setup key */
138977079be7Ssthen 		if(r->rk.dname == NULL) {
139077079be7Ssthen 			r->entry.key = r;
139177079be7Ssthen 			r->rk.dname = qinfo->qname;
139277079be7Ssthen 			r->rk.dname_len = qinfo->qname_len;
139377079be7Ssthen 			r->rk.type = htons(rdr_type);
139477079be7Ssthen 			r->rk.rrset_class = htons(qinfo->qclass);
139577079be7Ssthen 			r->rk.flags = 0;
139677079be7Ssthen 			d = (struct packed_rrset_data*)regional_alloc_zero(
139777079be7Ssthen 				temp, sizeof(struct packed_rrset_data)
139877079be7Ssthen 				+ sizeof(size_t) + sizeof(uint8_t*) +
139977079be7Ssthen 				sizeof(time_t));
140077079be7Ssthen 			if(!d) return 0; /* out of memory */
140177079be7Ssthen 			r->entry.data = d;
140277079be7Ssthen 			d->ttl = sldns_wirerr_get_ttl(rr, len, 1);
140377079be7Ssthen 			d->rr_len = (size_t*)((uint8_t*)d +
140477079be7Ssthen 				sizeof(struct packed_rrset_data));
140577079be7Ssthen 			d->rr_data = (uint8_t**)&(d->rr_len[1]);
140677079be7Ssthen 			d->rr_ttl = (time_t*)&(d->rr_data[1]);
140777079be7Ssthen 		}
140877079be7Ssthen 		d = (struct packed_rrset_data*)r->entry.data;
140977079be7Ssthen 		/* add entry to the data */
141077079be7Ssthen 		if(d->count != 0) {
141177079be7Ssthen 			size_t* oldlen = d->rr_len;
141277079be7Ssthen 			uint8_t** olddata = d->rr_data;
141377079be7Ssthen 			time_t* oldttl = d->rr_ttl;
141477079be7Ssthen 			/* increase arrays for lookup */
141577079be7Ssthen 			/* this is of course slow for very many records,
141677079be7Ssthen 			 * but most redirects are expected with few records */
141777079be7Ssthen 			d->rr_len = (size_t*)regional_alloc_zero(temp,
141877079be7Ssthen 				(d->count+1)*sizeof(size_t));
141977079be7Ssthen 			d->rr_data = (uint8_t**)regional_alloc_zero(temp,
142077079be7Ssthen 				(d->count+1)*sizeof(uint8_t*));
142177079be7Ssthen 			d->rr_ttl = (time_t*)regional_alloc_zero(temp,
142277079be7Ssthen 				(d->count+1)*sizeof(time_t));
142377079be7Ssthen 			if(!d->rr_len || !d->rr_data || !d->rr_ttl)
142477079be7Ssthen 				return 0; /* out of memory */
142577079be7Ssthen 			/* first one was allocated after struct d, but new
142677079be7Ssthen 			 * ones get their own array increment alloc, so
142777079be7Ssthen 			 * copy old content */
142877079be7Ssthen 			memmove(d->rr_len, oldlen, d->count*sizeof(size_t));
142977079be7Ssthen 			memmove(d->rr_data, olddata, d->count*sizeof(uint8_t*));
143077079be7Ssthen 			memmove(d->rr_ttl, oldttl, d->count*sizeof(time_t));
143177079be7Ssthen 		}
143277079be7Ssthen 
143377079be7Ssthen 		d->rr_len[d->count] = sldns_wirerr_get_rdatalen(rr, len, 1)+2;
143477079be7Ssthen 		d->rr_ttl[d->count] = sldns_wirerr_get_ttl(rr, len, 1);
143577079be7Ssthen 		d->rr_data[d->count] = regional_alloc_init(temp,
143677079be7Ssthen 			sldns_wirerr_get_rdatawl(rr, len, 1),
143777079be7Ssthen 			d->rr_len[d->count]);
143877079be7Ssthen 		if(!d->rr_data[d->count])
14392be9e038Ssthen 			return 0; /* out of memory */
144077079be7Ssthen 		d->count++;
144177079be7Ssthen 	}
14422be9e038Ssthen 	if(r->rk.dname)
14432be9e038Ssthen 		return 1;
14442be9e038Ssthen 	return 0;
14452be9e038Ssthen }
14462be9e038Ssthen 
14472be9e038Ssthen static int
14482be9e038Ssthen find_tag_datas(struct query_info* qinfo, struct config_strlist* list,
14492be9e038Ssthen 	struct ub_packed_rrset_key* r, struct regional* temp)
14502be9e038Ssthen {
14512be9e038Ssthen 	int result = local_data_find_tag_datas(qinfo, list, r, temp);
14522be9e038Ssthen 
145377079be7Ssthen 	/* If we've found a non-exact alias type of local data, make a shallow
145477079be7Ssthen 	 * copy of the RRset and remember it in qinfo to complete the alias
145577079be7Ssthen 	 * chain later. */
14562be9e038Ssthen 	if(result && qinfo->qtype != LDNS_RR_TYPE_CNAME &&
145777079be7Ssthen 		r->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
145877079be7Ssthen 		qinfo->local_alias =
145977079be7Ssthen 			regional_alloc_zero(temp, sizeof(struct local_rrset));
146077079be7Ssthen 		if(!qinfo->local_alias)
146177079be7Ssthen 			return 0; /* out of memory */
146277079be7Ssthen 		qinfo->local_alias->rrset =
146377079be7Ssthen 			regional_alloc_init(temp, r, sizeof(*r));
146477079be7Ssthen 		if(!qinfo->local_alias->rrset)
146577079be7Ssthen 			return 0; /* out of memory */
146677079be7Ssthen 	}
14672be9e038Ssthen 	return result;
146877079be7Ssthen }
146977079be7Ssthen 
1470eaf2578eSsthen int
147177079be7Ssthen local_data_answer(struct local_zone* z, struct module_env* env,
14722308e98cSsthen 	struct query_info* qinfo, struct edns_data* edns,
14732308e98cSsthen 	struct comm_reply* repinfo, sldns_buffer* buf,
147477079be7Ssthen 	struct regional* temp, int labs, struct local_data** ldp,
147577079be7Ssthen 	enum localzone_type lz_type, int tag, struct config_strlist** tag_datas,
147677079be7Ssthen 	size_t tag_datas_size, char** tagname, int num_tags)
1477933707f3Ssthen {
1478933707f3Ssthen 	struct local_data key;
1479933707f3Ssthen 	struct local_data* ld;
1480933707f3Ssthen 	struct local_rrset* lr;
1481933707f3Ssthen 	key.node.key = &key;
1482933707f3Ssthen 	key.name = qinfo->qname;
1483933707f3Ssthen 	key.namelen = qinfo->qname_len;
1484933707f3Ssthen 	key.namelabs = labs;
1485c3b38330Ssthen 	if(lz_type == local_zone_redirect ||
1486c3b38330Ssthen 		lz_type == local_zone_inform_redirect) {
1487933707f3Ssthen 		key.name = z->name;
1488933707f3Ssthen 		key.namelen = z->namelen;
1489933707f3Ssthen 		key.namelabs = z->namelabs;
149077079be7Ssthen 		if(tag != -1 && (size_t)tag<tag_datas_size && tag_datas[tag]) {
149177079be7Ssthen 			struct ub_packed_rrset_key r;
149277079be7Ssthen 			memset(&r, 0, sizeof(r));
149377079be7Ssthen 			if(find_tag_datas(qinfo, tag_datas[tag], &r, temp)) {
149477079be7Ssthen 				verbose(VERB_ALGO, "redirect with tag data [%d] %s",
149577079be7Ssthen 					tag, (tag<num_tags?tagname[tag]:"null"));
149677079be7Ssthen 
149777079be7Ssthen 				/* If we found a matching alias, we should
149877079be7Ssthen 				 * use it as part of the answer, but we can't
149977079be7Ssthen 				 * encode it until we complete the alias
150077079be7Ssthen 				 * chain. */
150177079be7Ssthen 				if(qinfo->local_alias)
150277079be7Ssthen 					return 1;
15032308e98cSsthen 				return local_encode(qinfo, env, edns, repinfo, buf, temp,
150477079be7Ssthen 					&r, 1, LDNS_RCODE_NOERROR);
150577079be7Ssthen 			}
150677079be7Ssthen 		}
1507933707f3Ssthen 	}
1508933707f3Ssthen 	ld = (struct local_data*)rbtree_search(&z->data, &key.node);
1509933707f3Ssthen 	*ldp = ld;
1510933707f3Ssthen 	if(!ld) {
1511933707f3Ssthen 		return 0;
1512933707f3Ssthen 	}
151377079be7Ssthen 	lr = local_data_find_type(ld, qinfo->qtype, 1);
1514933707f3Ssthen 	if(!lr)
1515933707f3Ssthen 		return 0;
151677079be7Ssthen 
151777079be7Ssthen 	/* Special case for alias matching.  See local_data_answer(). */
1518c3b38330Ssthen 	if((lz_type == local_zone_redirect ||
1519c3b38330Ssthen 		lz_type == local_zone_inform_redirect) &&
152077079be7Ssthen 		qinfo->qtype != LDNS_RR_TYPE_CNAME &&
152177079be7Ssthen 		lr->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) {
1522eaf2578eSsthen 		uint8_t* ctarget;
1523eaf2578eSsthen 		size_t ctargetlen = 0;
1524eaf2578eSsthen 
152577079be7Ssthen 		qinfo->local_alias =
152677079be7Ssthen 			regional_alloc_zero(temp, sizeof(struct local_rrset));
152777079be7Ssthen 		if(!qinfo->local_alias)
152877079be7Ssthen 			return 0; /* out of memory */
1529eaf2578eSsthen 		qinfo->local_alias->rrset = regional_alloc_init(
1530eaf2578eSsthen 			temp, lr->rrset, sizeof(*lr->rrset));
153177079be7Ssthen 		if(!qinfo->local_alias->rrset)
153277079be7Ssthen 			return 0; /* out of memory */
153377079be7Ssthen 		qinfo->local_alias->rrset->rk.dname = qinfo->qname;
153477079be7Ssthen 		qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
1535eaf2578eSsthen 		get_cname_target(lr->rrset, &ctarget, &ctargetlen);
1536eaf2578eSsthen 		if(!ctargetlen)
1537eaf2578eSsthen 			return 0; /* invalid cname */
1538eaf2578eSsthen 		if(dname_is_wild(ctarget)) {
1539eaf2578eSsthen 			/* synthesize cname target */
1540f46c52bfSsthen 			struct packed_rrset_data* d, *lr_d;
1541eaf2578eSsthen 			/* -3 for wildcard label and root label from qname */
1542eaf2578eSsthen 			size_t newtargetlen = qinfo->qname_len + ctargetlen - 3;
1543eaf2578eSsthen 
1544eaf2578eSsthen 			log_assert(ctargetlen >= 3);
1545eaf2578eSsthen 			log_assert(qinfo->qname_len >= 1);
1546eaf2578eSsthen 
1547eaf2578eSsthen 			if(newtargetlen > LDNS_MAX_DOMAINLEN) {
1548eaf2578eSsthen 				qinfo->local_alias = NULL;
1549eaf2578eSsthen 				local_error_encode(qinfo, env, edns, repinfo,
1550eaf2578eSsthen 					buf, temp, LDNS_RCODE_YXDOMAIN,
15510bdb4f62Ssthen 					(LDNS_RCODE_YXDOMAIN|BIT_AA),
15520bdb4f62Ssthen 					LDNS_EDE_OTHER,
15530bdb4f62Ssthen 					"DNAME expansion became too large");
1554eaf2578eSsthen 				return 1;
1555eaf2578eSsthen 			}
1556eaf2578eSsthen 			memset(&qinfo->local_alias->rrset->entry, 0,
1557eaf2578eSsthen 				sizeof(qinfo->local_alias->rrset->entry));
1558eaf2578eSsthen 			qinfo->local_alias->rrset->entry.key =
1559eaf2578eSsthen 				qinfo->local_alias->rrset;
1560eaf2578eSsthen 			qinfo->local_alias->rrset->entry.hash =
1561eaf2578eSsthen 				rrset_key_hash(&qinfo->local_alias->rrset->rk);
1562eaf2578eSsthen 			d = (struct packed_rrset_data*)regional_alloc_zero(temp,
1563eaf2578eSsthen 				sizeof(struct packed_rrset_data) + sizeof(size_t) +
1564eaf2578eSsthen 				sizeof(uint8_t*) + sizeof(time_t) + sizeof(uint16_t)
1565eaf2578eSsthen 				+ newtargetlen);
1566eaf2578eSsthen 			if(!d)
1567eaf2578eSsthen 				return 0; /* out of memory */
1568f46c52bfSsthen 			lr_d = (struct packed_rrset_data*)lr->rrset->entry.data;
1569eaf2578eSsthen 			qinfo->local_alias->rrset->entry.data = d;
1570f46c52bfSsthen 			d->ttl = lr_d->rr_ttl[0]; /* RFC6672-like behavior:
1571f46c52bfSsthen 					    synth CNAME TTL uses original TTL*/
1572eaf2578eSsthen 			d->count = 1;
1573eaf2578eSsthen 			d->rrsig_count = 0;
1574eaf2578eSsthen 			d->trust = rrset_trust_ans_noAA;
1575eaf2578eSsthen 			d->rr_len = (size_t*)((uint8_t*)d +
1576eaf2578eSsthen 				sizeof(struct packed_rrset_data));
1577eaf2578eSsthen 			d->rr_len[0] = newtargetlen + sizeof(uint16_t);
1578eaf2578eSsthen 			packed_rrset_ptr_fixup(d);
1579eaf2578eSsthen 			d->rr_ttl[0] = d->ttl;
1580eaf2578eSsthen 			sldns_write_uint16(d->rr_data[0], newtargetlen);
1581eaf2578eSsthen 			/* write qname */
1582eaf2578eSsthen 			memmove(d->rr_data[0] + sizeof(uint16_t), qinfo->qname,
1583eaf2578eSsthen 				qinfo->qname_len - 1);
1584e21c60efSsthen 			/* write cname target wildcard label */
1585eaf2578eSsthen 			memmove(d->rr_data[0] + sizeof(uint16_t) +
1586eaf2578eSsthen 				qinfo->qname_len - 1, ctarget + 2,
1587eaf2578eSsthen 				ctargetlen - 2);
1588eaf2578eSsthen 		}
158977079be7Ssthen 		return 1;
159077079be7Ssthen 	}
1591c3b38330Ssthen 	if(lz_type == local_zone_redirect ||
1592c3b38330Ssthen 		lz_type == local_zone_inform_redirect) {
1593933707f3Ssthen 		/* convert rrset name to query name; like a wildcard */
1594933707f3Ssthen 		struct ub_packed_rrset_key r = *lr->rrset;
1595933707f3Ssthen 		r.rk.dname = qinfo->qname;
1596933707f3Ssthen 		r.rk.dname_len = qinfo->qname_len;
15972308e98cSsthen 		return local_encode(qinfo, env, edns, repinfo, buf, temp, &r, 1,
1598933707f3Ssthen 			LDNS_RCODE_NOERROR);
1599933707f3Ssthen 	}
16002308e98cSsthen 	return local_encode(qinfo, env, edns, repinfo, buf, temp, lr->rrset, 1,
1601933707f3Ssthen 		LDNS_RCODE_NOERROR);
1602933707f3Ssthen }
1603933707f3Ssthen 
1604933707f3Ssthen /**
16052308e98cSsthen  * See if the local zone does not cover the name, eg. the name is not
16062308e98cSsthen  * in the zone and the zone is transparent */
16072308e98cSsthen static int
16082308e98cSsthen local_zone_does_not_cover(struct local_zone* z, struct query_info* qinfo,
16092308e98cSsthen 	int labs)
16102308e98cSsthen {
16112308e98cSsthen 	struct local_data key;
16122308e98cSsthen 	struct local_data* ld = NULL;
16132308e98cSsthen 	struct local_rrset* lr = NULL;
16148b7325afSsthen 	if(z->type == local_zone_always_transparent || z->type == local_zone_block_a)
16152308e98cSsthen 		return 1;
16162308e98cSsthen 	if(z->type != local_zone_transparent
16172308e98cSsthen 		&& z->type != local_zone_typetransparent
16182308e98cSsthen 		&& z->type != local_zone_inform)
16192308e98cSsthen 		return 0;
16202308e98cSsthen 	key.node.key = &key;
16212308e98cSsthen 	key.name = qinfo->qname;
16222308e98cSsthen 	key.namelen = qinfo->qname_len;
16232308e98cSsthen 	key.namelabs = labs;
16242308e98cSsthen 	ld = (struct local_data*)rbtree_search(&z->data, &key.node);
16252308e98cSsthen 	if(z->type == local_zone_transparent || z->type == local_zone_inform)
16262308e98cSsthen 		return (ld == NULL);
16272308e98cSsthen 	if(ld)
16282308e98cSsthen 		lr = local_data_find_type(ld, qinfo->qtype, 1);
16292308e98cSsthen 	/* local_zone_typetransparent */
16302308e98cSsthen 	return (lr == NULL);
16312308e98cSsthen }
16322308e98cSsthen 
1633e21c60efSsthen static inline int
1634e21c60efSsthen local_zone_is_udp_query(struct comm_reply* repinfo) {
1635e21c60efSsthen 	return repinfo != NULL
1636e21c60efSsthen 			? (repinfo->c != NULL
1637e21c60efSsthen 				? repinfo->c->type == comm_udp
1638e21c60efSsthen 				: 0)
1639e21c60efSsthen 			: 0;
1640e21c60efSsthen }
1641e21c60efSsthen 
1642eaf2578eSsthen int
1643eaf2578eSsthen local_zones_zone_answer(struct local_zone* z, struct module_env* env,
16442308e98cSsthen 	struct query_info* qinfo, struct edns_data* edns,
16452308e98cSsthen 	struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp,
16462308e98cSsthen 	struct local_data* ld, enum localzone_type lz_type)
1647933707f3Ssthen {
1648eaf2578eSsthen 	if(lz_type == local_zone_deny ||
1649eaf2578eSsthen 		lz_type == local_zone_always_deny ||
1650eaf2578eSsthen 		lz_type == local_zone_inform_deny) {
1651933707f3Ssthen 		/** no reply at all, signal caller by clearing buffer. */
16525d76a658Ssthen 		sldns_buffer_clear(buf);
16535d76a658Ssthen 		sldns_buffer_flip(buf);
1654933707f3Ssthen 		return 1;
165577079be7Ssthen 	} else if(lz_type == local_zone_refuse
165677079be7Ssthen 		|| lz_type == local_zone_always_refuse) {
16572308e98cSsthen 		local_error_encode(qinfo, env, edns, repinfo, buf, temp,
16580bdb4f62Ssthen 			LDNS_RCODE_REFUSED, (LDNS_RCODE_REFUSED|BIT_AA),
16590bdb4f62Ssthen 			LDNS_EDE_NONE, NULL);
1660933707f3Ssthen 		return 1;
166177079be7Ssthen 	} else if(lz_type == local_zone_static ||
166277079be7Ssthen 		lz_type == local_zone_redirect ||
1663c3b38330Ssthen 		lz_type == local_zone_inform_redirect ||
1664eaf2578eSsthen 		lz_type == local_zone_always_nxdomain ||
1665e21c60efSsthen 		lz_type == local_zone_always_nodata ||
1666e21c60efSsthen 		(lz_type == local_zone_truncate
1667e21c60efSsthen 			&& local_zone_is_udp_query(repinfo))) {
1668933707f3Ssthen 		/* for static, reply nodata or nxdomain
1669933707f3Ssthen 		 * for redirect, reply nodata */
1670933707f3Ssthen 		/* no additional section processing,
1671933707f3Ssthen 		 * cname, dname or wildcard processing,
1672933707f3Ssthen 		 * or using closest match for NSEC.
1673933707f3Ssthen 		 * or using closest match for returning delegation downwards
1674933707f3Ssthen 		 */
1675c3b38330Ssthen 		int rcode = (ld || lz_type == local_zone_redirect ||
1676eaf2578eSsthen 			lz_type == local_zone_inform_redirect ||
1677e21c60efSsthen 			lz_type == local_zone_always_nodata ||
1678e21c60efSsthen 			lz_type == local_zone_truncate)?
167977079be7Ssthen 			LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN;
1680e21c60efSsthen 		rcode = (lz_type == local_zone_truncate ? (rcode|BIT_TC) : rcode);
1681e21c60efSsthen 		if(z != NULL && z->soa && z->soa_negative)
16822308e98cSsthen 			return local_encode(qinfo, env, edns, repinfo, buf, temp,
16839982a05dSsthen 				z->soa_negative, 0, rcode);
16840bdb4f62Ssthen 		local_error_encode(qinfo, env, edns, repinfo, buf, temp,
16850bdb4f62Ssthen 			rcode, (rcode|BIT_AA), LDNS_EDE_NONE, NULL);
1686933707f3Ssthen 		return 1;
168777079be7Ssthen 	} else if(lz_type == local_zone_typetransparent
168877079be7Ssthen 		|| lz_type == local_zone_always_transparent) {
1689933707f3Ssthen 		/* no NODATA or NXDOMAINS for this zone type */
1690933707f3Ssthen 		return 0;
16918b7325afSsthen 	} else if(lz_type == local_zone_block_a) {
16928b7325afSsthen 		/* Return NODATA for all A queries */
16938b7325afSsthen 		if(qinfo->qtype == LDNS_RR_TYPE_A) {
16948b7325afSsthen 			local_error_encode(qinfo, env, edns, repinfo, buf, temp,
16958b7325afSsthen 				LDNS_RCODE_NOERROR, (LDNS_RCODE_NOERROR|BIT_AA),
16968b7325afSsthen 				LDNS_EDE_NONE, NULL);
16978b7325afSsthen 				return 1;
16988b7325afSsthen 		}
16998b7325afSsthen 
17008b7325afSsthen 		return 0;
17019982a05dSsthen 	} else if(lz_type == local_zone_always_null) {
17029982a05dSsthen 		/* 0.0.0.0 or ::0 or noerror/nodata for this zone type,
17039982a05dSsthen 		 * used for blocklists. */
17049982a05dSsthen 		if(qinfo->qtype == LDNS_RR_TYPE_A ||
17059982a05dSsthen 			qinfo->qtype == LDNS_RR_TYPE_AAAA) {
17069982a05dSsthen 			struct ub_packed_rrset_key lrr;
17079982a05dSsthen 			struct packed_rrset_data d;
17089982a05dSsthen 			time_t rr_ttl = 3600;
17099982a05dSsthen 			size_t rr_len = 0;
17109982a05dSsthen 			uint8_t rr_data[2+16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
17119982a05dSsthen 			uint8_t* rr_datas = rr_data;
17129982a05dSsthen 			memset(&lrr, 0, sizeof(lrr));
17139982a05dSsthen 			memset(&d, 0, sizeof(d));
17149982a05dSsthen 			lrr.entry.data = &d;
17159982a05dSsthen 			lrr.rk.dname = qinfo->qname;
17169982a05dSsthen 			lrr.rk.dname_len = qinfo->qname_len;
17179982a05dSsthen 			lrr.rk.type = htons(qinfo->qtype);
17189982a05dSsthen 			lrr.rk.rrset_class = htons(qinfo->qclass);
17199982a05dSsthen 			if(qinfo->qtype == LDNS_RR_TYPE_A) {
17209982a05dSsthen 				rr_len = 4;
17219982a05dSsthen 				sldns_write_uint16(rr_data, rr_len);
17229982a05dSsthen 				rr_len += 2;
17239982a05dSsthen 			} else {
17249982a05dSsthen 				rr_len = 16;
17259982a05dSsthen 				sldns_write_uint16(rr_data, rr_len);
17269982a05dSsthen 				rr_len += 2;
17279982a05dSsthen 			}
17289982a05dSsthen 			d.ttl = rr_ttl;
17299982a05dSsthen 			d.count = 1;
17309982a05dSsthen 			d.rr_len = &rr_len;
17319982a05dSsthen 			d.rr_data = &rr_datas;
17329982a05dSsthen 			d.rr_ttl = &rr_ttl;
17339982a05dSsthen 			return local_encode(qinfo, env, edns, repinfo, buf, temp,
17349982a05dSsthen 				&lrr, 1, LDNS_RCODE_NOERROR);
17359982a05dSsthen 		} else {
17360bdb4f62Ssthen 			/* NODATA: No EDE needed */
17379982a05dSsthen 			local_error_encode(qinfo, env, edns, repinfo, buf,
17389982a05dSsthen 				temp, LDNS_RCODE_NOERROR,
17390bdb4f62Ssthen 				(LDNS_RCODE_NOERROR|BIT_AA), -1, NULL);
17409982a05dSsthen 		}
17419982a05dSsthen 		return 1;
1742933707f3Ssthen 	}
174377079be7Ssthen 	/* else lz_type == local_zone_transparent */
1744933707f3Ssthen 
1745933707f3Ssthen 	/* if the zone is transparent and the name exists, but the type
1746933707f3Ssthen 	 * does not, then we should make this noerror/nodata */
1747933707f3Ssthen 	if(ld && ld->rrsets) {
1748933707f3Ssthen 		int rcode = LDNS_RCODE_NOERROR;
1749e21c60efSsthen 		if(z != NULL && z->soa && z->soa_negative)
17502308e98cSsthen 			return local_encode(qinfo, env, edns, repinfo, buf, temp,
17519982a05dSsthen 				z->soa_negative, 0, rcode);
17520bdb4f62Ssthen 		/* NODATA: No EDE needed */
17532308e98cSsthen 		local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode,
17540bdb4f62Ssthen 			(rcode|BIT_AA), LDNS_EDE_NONE, NULL);
1755933707f3Ssthen 		return 1;
1756933707f3Ssthen 	}
1757933707f3Ssthen 
1758933707f3Ssthen 	/* stop here, and resolve further on */
1759933707f3Ssthen 	return 0;
1760933707f3Ssthen }
1761933707f3Ssthen 
1762b2cdf21fSsthen /** print log information for an inform zone query */
1763b2cdf21fSsthen static void
1764b2cdf21fSsthen lz_inform_print(struct local_zone* z, struct query_info* qinfo,
176545872187Ssthen 	struct sockaddr_storage* addr, socklen_t addrlen)
1766b2cdf21fSsthen {
1767b2cdf21fSsthen 	char ip[128], txt[512];
1768b2cdf21fSsthen 	char zname[LDNS_MAX_DOMAINLEN+1];
176945872187Ssthen 	uint16_t port = ntohs(((struct sockaddr_in*)addr)->sin_port);
1770b2cdf21fSsthen 	dname_str(z->name, zname);
177145872187Ssthen 	addr_to_str(addr, addrlen, ip, sizeof(ip));
17722308e98cSsthen 	snprintf(txt, sizeof(txt), "%s %s %s@%u", zname, local_zone_type2str(z->type), ip,
1773b2cdf21fSsthen 		(unsigned)port);
1774ebf5bb73Ssthen 	log_nametypeclass(NO_VERBOSE, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
1775b2cdf21fSsthen }
1776b2cdf21fSsthen 
177777079be7Ssthen static enum localzone_type
177877079be7Ssthen lz_type(uint8_t *taglist, size_t taglen, uint8_t *taglist2, size_t taglen2,
177977079be7Ssthen 	uint8_t *tagactions, size_t tagactionssize, enum localzone_type lzt,
178077079be7Ssthen 	struct comm_reply* repinfo, struct rbtree_type* override_tree,
178177079be7Ssthen 	int* tag, char** tagname, int num_tags)
178277079be7Ssthen {
178377079be7Ssthen 	struct local_zone_override* lzo;
178477079be7Ssthen 	if(repinfo && override_tree) {
178577079be7Ssthen 		lzo = (struct local_zone_override*)addr_tree_lookup(
178645872187Ssthen 			override_tree, &repinfo->client_addr,
178745872187Ssthen 			repinfo->client_addrlen);
178877079be7Ssthen 		if(lzo && lzo->type) {
178977079be7Ssthen 			verbose(VERB_ALGO, "local zone override to type %s",
179077079be7Ssthen 				local_zone_type2str(lzo->type));
179177079be7Ssthen 			return lzo->type;
179277079be7Ssthen 		}
179377079be7Ssthen 	}
179477079be7Ssthen 	if(!taglist || !taglist2)
179577079be7Ssthen 		return lzt;
17962be9e038Ssthen 	return local_data_find_tag_action(taglist, taglen, taglist2, taglen2,
17972be9e038Ssthen 		tagactions, tagactionssize, lzt, tag, tagname, num_tags);
17982be9e038Ssthen }
17992be9e038Ssthen 
18002be9e038Ssthen enum localzone_type
18012be9e038Ssthen local_data_find_tag_action(const uint8_t* taglist, size_t taglen,
18022be9e038Ssthen 	const uint8_t* taglist2, size_t taglen2, const uint8_t* tagactions,
18032be9e038Ssthen 	size_t tagactionssize, enum localzone_type lzt, int* tag,
18042be9e038Ssthen 	char* const* tagname, int num_tags)
18052be9e038Ssthen {
18062be9e038Ssthen 	size_t i, j;
18072be9e038Ssthen 	uint8_t tagmatch;
18082be9e038Ssthen 
180977079be7Ssthen 	for(i=0; i<taglen && i<taglen2; i++) {
181077079be7Ssthen 		tagmatch = (taglist[i] & taglist2[i]);
181177079be7Ssthen 		for(j=0; j<8 && tagmatch>0; j++) {
181277079be7Ssthen 			if((tagmatch & 0x1)) {
181377079be7Ssthen 				*tag = (int)(i*8+j);
181477079be7Ssthen 				verbose(VERB_ALGO, "matched tag [%d] %s",
181577079be7Ssthen 					*tag, (*tag<num_tags?tagname[*tag]:"null"));
181677079be7Ssthen 				/* does this tag have a tag action? */
181777079be7Ssthen 				if(i*8+j < tagactionssize && tagactions
181877079be7Ssthen 				   && tagactions[i*8+j] != 0) {
181977079be7Ssthen 				  verbose(VERB_ALGO, "tag action [%d] %s to type %s",
182077079be7Ssthen 					*tag, (*tag<num_tags?tagname[*tag]:"null"),
182177079be7Ssthen 				  	local_zone_type2str(
182277079be7Ssthen 					(enum localzone_type)
182377079be7Ssthen 					tagactions[i*8+j]));
182477079be7Ssthen 				  return (enum localzone_type)tagactions[i*8+j];
182577079be7Ssthen 				}
182677079be7Ssthen 				return lzt;
182777079be7Ssthen 			}
182877079be7Ssthen 			tagmatch >>= 1;
182977079be7Ssthen 		}
183077079be7Ssthen 	}
183177079be7Ssthen 	return lzt;
183277079be7Ssthen }
183377079be7Ssthen 
1834933707f3Ssthen int
183577079be7Ssthen local_zones_answer(struct local_zones* zones, struct module_env* env,
183677079be7Ssthen 	struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
183777079be7Ssthen 	struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist,
183877079be7Ssthen 	size_t taglen, uint8_t* tagactions, size_t tagactionssize,
183977079be7Ssthen 	struct config_strlist** tag_datas, size_t tag_datas_size,
184077079be7Ssthen 	char** tagname, int num_tags, struct view* view)
1841933707f3Ssthen {
1842933707f3Ssthen 	/* see if query is covered by a zone,
1843933707f3Ssthen 	 * 	if so:	- try to match (exact) local data
1844933707f3Ssthen 	 * 		- look at zone type for negative response. */
1845933707f3Ssthen 	int labs = dname_count_labels(qinfo->qname);
184677079be7Ssthen 	struct local_data* ld = NULL;
184777079be7Ssthen 	struct local_zone* z = NULL;
184877079be7Ssthen 	enum localzone_type lzt = local_zone_transparent;
184977079be7Ssthen 	int r, tag = -1;
185077079be7Ssthen 
185177079be7Ssthen 	if(view) {
185277079be7Ssthen 		lock_rw_rdlock(&view->lock);
185377079be7Ssthen 		if(view->local_zones &&
185477079be7Ssthen 			(z = local_zones_lookup(view->local_zones,
185577079be7Ssthen 			qinfo->qname, qinfo->qname_len, labs,
18567191de28Ssthen 			qinfo->qclass, qinfo->qtype))) {
185777079be7Ssthen 			lock_rw_rdlock(&z->lock);
185877079be7Ssthen 			lzt = z->type;
185977079be7Ssthen 		}
1860938a3a5eSflorian 		if(lzt == local_zone_noview) {
1861938a3a5eSflorian 			lock_rw_unlock(&z->lock);
1862938a3a5eSflorian 			z = NULL;
1863938a3a5eSflorian 		}
18642308e98cSsthen 		if(z && (lzt == local_zone_transparent ||
18652308e98cSsthen 			lzt == local_zone_typetransparent ||
18662308e98cSsthen 			lzt == local_zone_inform ||
18678b7325afSsthen 			lzt == local_zone_always_transparent ||
18688b7325afSsthen 			lzt == local_zone_block_a) &&
18692308e98cSsthen 			local_zone_does_not_cover(z, qinfo, labs)) {
18702308e98cSsthen 			lock_rw_unlock(&z->lock);
18712308e98cSsthen 			z = NULL;
18722308e98cSsthen 		}
18732be9e038Ssthen 		if(view->local_zones && !z && !view->isfirst){
187477079be7Ssthen 			lock_rw_unlock(&view->lock);
187577079be7Ssthen 			return 0;
187677079be7Ssthen 		}
18772308e98cSsthen 		if(z && verbosity >= VERB_ALGO) {
18782308e98cSsthen 			char zname[255+1];
18792308e98cSsthen 			dname_str(z->name, zname);
18802308e98cSsthen 			verbose(VERB_ALGO, "using localzone %s %s from view %s",
18812308e98cSsthen 				zname, local_zone_type2str(lzt), view->name);
18822308e98cSsthen 		}
188377079be7Ssthen 		lock_rw_unlock(&view->lock);
188477079be7Ssthen 	}
1885933707f3Ssthen 	if(!z) {
188677079be7Ssthen 		/* try global local_zones tree */
188777079be7Ssthen 		lock_rw_rdlock(&zones->lock);
188877079be7Ssthen 		if(!(z = local_zones_tags_lookup(zones, qinfo->qname,
18897191de28Ssthen 			qinfo->qname_len, labs, qinfo->qclass, qinfo->qtype,
18907191de28Ssthen 			taglist, taglen, 0))) {
18915d76a658Ssthen 			lock_rw_unlock(&zones->lock);
1892933707f3Ssthen 			return 0;
1893933707f3Ssthen 		}
1894933707f3Ssthen 		lock_rw_rdlock(&z->lock);
189577079be7Ssthen 		lzt = lz_type(taglist, taglen, z->taglist, z->taglen,
189677079be7Ssthen 			tagactions, tagactionssize, z->type, repinfo,
189777079be7Ssthen 			z->override_tree, &tag, tagname, num_tags);
189877079be7Ssthen 		lock_rw_unlock(&zones->lock);
18992308e98cSsthen 		if(z && verbosity >= VERB_ALGO) {
19002308e98cSsthen 			char zname[255+1];
19012308e98cSsthen 			dname_str(z->name, zname);
19022308e98cSsthen 			verbose(VERB_ALGO, "using localzone %s %s", zname,
19032308e98cSsthen 				local_zone_type2str(lzt));
190477079be7Ssthen 		}
19052308e98cSsthen 	}
19062308e98cSsthen 	if((env->cfg->log_local_actions ||
1907c3b38330Ssthen 			lzt == local_zone_inform ||
1908c3b38330Ssthen 			lzt == local_zone_inform_deny ||
1909c3b38330Ssthen 			lzt == local_zone_inform_redirect)
1910fdfb4ba6Ssthen 			&& repinfo)
191145872187Ssthen 		lz_inform_print(z, qinfo, &repinfo->client_addr,
191245872187Ssthen 			repinfo->client_addrlen);
1913b2cdf21fSsthen 
191477079be7Ssthen 	if(lzt != local_zone_always_refuse
191577079be7Ssthen 		&& lzt != local_zone_always_transparent
19168b7325afSsthen 		&& lzt != local_zone_block_a
191777079be7Ssthen 		&& lzt != local_zone_always_nxdomain
1918eaf2578eSsthen 		&& lzt != local_zone_always_nodata
1919eaf2578eSsthen 		&& lzt != local_zone_always_deny
19202308e98cSsthen 		&& local_data_answer(z, env, qinfo, edns, repinfo, buf, temp, labs,
19212308e98cSsthen 			&ld, lzt, tag, tag_datas, tag_datas_size, tagname, num_tags)) {
1922933707f3Ssthen 		lock_rw_unlock(&z->lock);
192377079be7Ssthen 		/* We should tell the caller that encode is deferred if we found
192477079be7Ssthen 		 * a local alias. */
192577079be7Ssthen 		return !qinfo->local_alias;
1926933707f3Ssthen 	}
1927eaf2578eSsthen 	r = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, ld, lzt);
1928933707f3Ssthen 	lock_rw_unlock(&z->lock);
192977079be7Ssthen 	return r && !qinfo->local_alias; /* see above */
1930933707f3Ssthen }
1931933707f3Ssthen 
1932933707f3Ssthen const char* local_zone_type2str(enum localzone_type t)
1933933707f3Ssthen {
1934933707f3Ssthen 	switch(t) {
193577079be7Ssthen 		case local_zone_unset: return "unset";
1936933707f3Ssthen 		case local_zone_deny: return "deny";
1937933707f3Ssthen 		case local_zone_refuse: return "refuse";
1938933707f3Ssthen 		case local_zone_redirect: return "redirect";
1939933707f3Ssthen 		case local_zone_transparent: return "transparent";
1940933707f3Ssthen 		case local_zone_typetransparent: return "typetransparent";
1941933707f3Ssthen 		case local_zone_static: return "static";
1942933707f3Ssthen 		case local_zone_nodefault: return "nodefault";
1943b2cdf21fSsthen 		case local_zone_inform: return "inform";
1944fdfb4ba6Ssthen 		case local_zone_inform_deny: return "inform_deny";
1945c3b38330Ssthen 		case local_zone_inform_redirect: return "inform_redirect";
194677079be7Ssthen 		case local_zone_always_transparent: return "always_transparent";
19478b7325afSsthen 		case local_zone_block_a: return "block_a";
194877079be7Ssthen 		case local_zone_always_refuse: return "always_refuse";
194977079be7Ssthen 		case local_zone_always_nxdomain: return "always_nxdomain";
1950eaf2578eSsthen 		case local_zone_always_nodata: return "always_nodata";
1951eaf2578eSsthen 		case local_zone_always_deny: return "always_deny";
19529982a05dSsthen 		case local_zone_always_null: return "always_null";
1953938a3a5eSflorian 		case local_zone_noview: return "noview";
1954e21c60efSsthen 		case local_zone_truncate: return "truncate";
1955eaf2578eSsthen 		case local_zone_invalid: return "invalid";
1956933707f3Ssthen 	}
1957933707f3Ssthen 	return "badtyped";
1958933707f3Ssthen }
1959933707f3Ssthen 
1960933707f3Ssthen int local_zone_str2type(const char* type, enum localzone_type* t)
1961933707f3Ssthen {
1962933707f3Ssthen 	if(strcmp(type, "deny") == 0)
1963933707f3Ssthen 		*t = local_zone_deny;
1964933707f3Ssthen 	else if(strcmp(type, "refuse") == 0)
1965933707f3Ssthen 		*t = local_zone_refuse;
1966933707f3Ssthen 	else if(strcmp(type, "static") == 0)
1967933707f3Ssthen 		*t = local_zone_static;
1968933707f3Ssthen 	else if(strcmp(type, "transparent") == 0)
1969933707f3Ssthen 		*t = local_zone_transparent;
1970933707f3Ssthen 	else if(strcmp(type, "typetransparent") == 0)
1971933707f3Ssthen 		*t = local_zone_typetransparent;
1972933707f3Ssthen 	else if(strcmp(type, "redirect") == 0)
1973933707f3Ssthen 		*t = local_zone_redirect;
1974b2cdf21fSsthen 	else if(strcmp(type, "inform") == 0)
1975b2cdf21fSsthen 		*t = local_zone_inform;
1976fdfb4ba6Ssthen 	else if(strcmp(type, "inform_deny") == 0)
1977fdfb4ba6Ssthen 		*t = local_zone_inform_deny;
1978c3b38330Ssthen 	else if(strcmp(type, "inform_redirect") == 0)
1979c3b38330Ssthen 		*t = local_zone_inform_redirect;
198077079be7Ssthen 	else if(strcmp(type, "always_transparent") == 0)
198177079be7Ssthen 		*t = local_zone_always_transparent;
19828b7325afSsthen 	else if(strcmp(type, "block_a") == 0)
19838b7325afSsthen 		*t = local_zone_block_a;
198477079be7Ssthen 	else if(strcmp(type, "always_refuse") == 0)
198577079be7Ssthen 		*t = local_zone_always_refuse;
198677079be7Ssthen 	else if(strcmp(type, "always_nxdomain") == 0)
198777079be7Ssthen 		*t = local_zone_always_nxdomain;
1988eaf2578eSsthen 	else if(strcmp(type, "always_nodata") == 0)
1989eaf2578eSsthen 		*t = local_zone_always_nodata;
1990eaf2578eSsthen 	else if(strcmp(type, "always_deny") == 0)
1991eaf2578eSsthen 		*t = local_zone_always_deny;
19929982a05dSsthen 	else if(strcmp(type, "always_null") == 0)
19939982a05dSsthen 		*t = local_zone_always_null;
1994938a3a5eSflorian 	else if(strcmp(type, "noview") == 0)
1995938a3a5eSflorian 		*t = local_zone_noview;
1996e21c60efSsthen 	else if(strcmp(type, "truncate") == 0)
1997e21c60efSsthen 		*t = local_zone_truncate;
19982be9e038Ssthen 	else if(strcmp(type, "nodefault") == 0)
19992be9e038Ssthen 		*t = local_zone_nodefault;
2000933707f3Ssthen 	else return 0;
2001933707f3Ssthen 	return 1;
2002933707f3Ssthen }
2003933707f3Ssthen 
2004933707f3Ssthen /** iterate over the kiddies of the given name and set their parent ptr */
2005933707f3Ssthen static void
2006933707f3Ssthen set_kiddo_parents(struct local_zone* z, struct local_zone* match,
2007933707f3Ssthen 	struct local_zone* newp)
2008933707f3Ssthen {
2009933707f3Ssthen 	/* both zones and z are locked already */
2010933707f3Ssthen 	/* in the sorted rbtree, the kiddies of z are located after z */
2011933707f3Ssthen 	/* z must be present in the tree */
2012933707f3Ssthen 	struct local_zone* p = z;
2013933707f3Ssthen 	p = (struct local_zone*)rbtree_next(&p->node);
2014933707f3Ssthen 	while(p!=(struct local_zone*)RBTREE_NULL &&
2015933707f3Ssthen 		p->dclass == z->dclass && dname_strict_subdomain(p->name,
2016933707f3Ssthen 		p->namelabs, z->name, z->namelabs)) {
2017933707f3Ssthen 		/* update parent ptr */
2018933707f3Ssthen 		/* only when matches with existing parent pointer, so that
2019933707f3Ssthen 		 * deeper child structures are not touched, i.e.
2020933707f3Ssthen 		 * update of x, and a.x, b.x, f.b.x, g.b.x, c.x, y
2021933707f3Ssthen 		 * gets to update a.x, b.x and c.x */
2022933707f3Ssthen 		lock_rw_wrlock(&p->lock);
2023933707f3Ssthen 		if(p->parent == match)
2024933707f3Ssthen 			p->parent = newp;
2025933707f3Ssthen 		lock_rw_unlock(&p->lock);
2026933707f3Ssthen 		p = (struct local_zone*)rbtree_next(&p->node);
2027933707f3Ssthen 	}
2028933707f3Ssthen }
2029933707f3Ssthen 
2030933707f3Ssthen struct local_zone* local_zones_add_zone(struct local_zones* zones,
2031933707f3Ssthen 	uint8_t* name, size_t len, int labs, uint16_t dclass,
2032933707f3Ssthen 	enum localzone_type tp)
2033933707f3Ssthen {
2034*98bc733bSsthen 	int exact;
2035933707f3Ssthen 	/* create */
2036*98bc733bSsthen 	struct local_zone *prev;
2037933707f3Ssthen 	struct local_zone* z = local_zone_create(name, len, labs, tp, dclass);
20382ee382b6Ssthen 	if(!z) {
20392ee382b6Ssthen 		free(name);
20402ee382b6Ssthen 		return NULL;
20412ee382b6Ssthen 	}
2042933707f3Ssthen 	lock_rw_wrlock(&z->lock);
2043933707f3Ssthen 
2044933707f3Ssthen 	/* find the closest parent */
2045*98bc733bSsthen 	prev = local_zones_find_le(zones, name, len, labs, dclass, &exact);
2046*98bc733bSsthen 	if(!exact)
2047*98bc733bSsthen 		z->parent = find_closest_parent(z, prev);
2048933707f3Ssthen 
2049933707f3Ssthen 	/* insert into the tree */
2050*98bc733bSsthen 	if(exact||!rbtree_insert(&zones->ztree, &z->node)) {
2051933707f3Ssthen 		/* duplicate entry! */
2052933707f3Ssthen 		lock_rw_unlock(&z->lock);
2053933707f3Ssthen 		local_zone_delete(z);
2054933707f3Ssthen 		log_err("internal: duplicate entry in local_zones_add_zone");
2055933707f3Ssthen 		return NULL;
2056933707f3Ssthen 	}
2057933707f3Ssthen 
2058933707f3Ssthen 	/* set parent pointers right */
2059933707f3Ssthen 	set_kiddo_parents(z, z->parent, z);
2060933707f3Ssthen 
2061933707f3Ssthen 	lock_rw_unlock(&z->lock);
2062933707f3Ssthen 	return z;
2063933707f3Ssthen }
2064933707f3Ssthen 
2065933707f3Ssthen void local_zones_del_zone(struct local_zones* zones, struct local_zone* z)
2066933707f3Ssthen {
2067933707f3Ssthen 	/* fix up parents in tree */
2068933707f3Ssthen 	lock_rw_wrlock(&z->lock);
2069933707f3Ssthen 	set_kiddo_parents(z, z, z->parent);
2070933707f3Ssthen 
2071933707f3Ssthen 	/* remove from tree */
2072933707f3Ssthen 	(void)rbtree_delete(&zones->ztree, z);
2073933707f3Ssthen 
2074933707f3Ssthen 	/* delete the zone */
2075933707f3Ssthen 	lock_rw_unlock(&z->lock);
2076933707f3Ssthen 	local_zone_delete(z);
2077933707f3Ssthen }
2078933707f3Ssthen 
2079933707f3Ssthen int
20805d76a658Ssthen local_zones_add_RR(struct local_zones* zones, const char* rr)
2081933707f3Ssthen {
2082933707f3Ssthen 	uint8_t* rr_name;
20837191de28Ssthen 	uint16_t rr_class, rr_type;
2084933707f3Ssthen 	size_t len;
2085933707f3Ssthen 	int labs;
2086933707f3Ssthen 	struct local_zone* z;
2087933707f3Ssthen 	int r;
20887191de28Ssthen 	if(!get_rr_nameclass(rr, &rr_name, &rr_class, &rr_type)) {
2089933707f3Ssthen 		return 0;
2090933707f3Ssthen 	}
2091933707f3Ssthen 	labs = dname_count_size_labels(rr_name, &len);
20925d76a658Ssthen 	/* could first try readlock then get writelock if zone does not exist,
20935d76a658Ssthen 	 * but we do not add enough RRs (from multiple threads) to optimize */
20945d76a658Ssthen 	lock_rw_wrlock(&zones->lock);
20957191de28Ssthen 	z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type);
2096933707f3Ssthen 	if(!z) {
2097933707f3Ssthen 		z = local_zones_add_zone(zones, rr_name, len, labs, rr_class,
2098933707f3Ssthen 			local_zone_transparent);
2099933707f3Ssthen 		if(!z) {
21005d76a658Ssthen 			lock_rw_unlock(&zones->lock);
2101933707f3Ssthen 			return 0;
2102933707f3Ssthen 		}
2103933707f3Ssthen 	} else {
2104933707f3Ssthen 		free(rr_name);
2105933707f3Ssthen 	}
2106933707f3Ssthen 	lock_rw_wrlock(&z->lock);
21075d76a658Ssthen 	lock_rw_unlock(&zones->lock);
21085d76a658Ssthen 	r = lz_enter_rr_into_zone(z, rr);
2109933707f3Ssthen 	lock_rw_unlock(&z->lock);
2110933707f3Ssthen 	return r;
2111933707f3Ssthen }
2112933707f3Ssthen 
2113933707f3Ssthen /** returns true if the node is terminal so no deeper domain names exist */
2114933707f3Ssthen static int
2115933707f3Ssthen is_terminal(struct local_data* d)
2116933707f3Ssthen {
2117933707f3Ssthen 	/* for empty nonterminals, the deeper domain names are sorted
2118933707f3Ssthen 	 * right after them, so simply check the next name in the tree
2119933707f3Ssthen 	 */
2120933707f3Ssthen 	struct local_data* n = (struct local_data*)rbtree_next(&d->node);
2121933707f3Ssthen 	if(n == (struct local_data*)RBTREE_NULL)
2122933707f3Ssthen 		return 1; /* last in tree, no deeper node */
2123933707f3Ssthen 	if(dname_strict_subdomain(n->name, n->namelabs, d->name, d->namelabs))
2124933707f3Ssthen 		return 0; /* there is a deeper node */
2125933707f3Ssthen 	return 1;
2126933707f3Ssthen }
2127933707f3Ssthen 
2128933707f3Ssthen /** delete empty terminals from tree when final data is deleted */
2129933707f3Ssthen static void
2130933707f3Ssthen del_empty_term(struct local_zone* z, struct local_data* d,
2131933707f3Ssthen 	uint8_t* name, size_t len, int labs)
2132933707f3Ssthen {
2133933707f3Ssthen 	while(d && d->rrsets == NULL && is_terminal(d)) {
2134933707f3Ssthen 		/* is this empty nonterminal? delete */
2135933707f3Ssthen 		/* note, no memory recycling in zone region */
2136933707f3Ssthen 		(void)rbtree_delete(&z->data, d);
2137933707f3Ssthen 
2138933707f3Ssthen 		/* go up and to the next label */
2139933707f3Ssthen 		if(dname_is_root(name))
2140933707f3Ssthen 			return;
2141933707f3Ssthen 		dname_remove_label(&name, &len);
2142933707f3Ssthen 		labs--;
2143eaf2578eSsthen 		d = local_zone_find_data(z, name, len, labs);
2144933707f3Ssthen 	}
2145933707f3Ssthen }
2146933707f3Ssthen 
21477191de28Ssthen /** find and remove type from list in domain struct */
21487191de28Ssthen static void
21497191de28Ssthen del_local_rrset(struct local_data* d, uint16_t dtype)
21507191de28Ssthen {
21517191de28Ssthen 	struct local_rrset* prev=NULL, *p=d->rrsets;
21527191de28Ssthen 	while(p && ntohs(p->rrset->rk.type) != dtype) {
21537191de28Ssthen 		prev = p;
21547191de28Ssthen 		p = p->next;
21557191de28Ssthen 	}
21567191de28Ssthen 	if(!p)
21577191de28Ssthen 		return; /* rrset type not found */
21587191de28Ssthen 	/* unlink it */
21597191de28Ssthen 	if(prev) prev->next = p->next;
21607191de28Ssthen 	else d->rrsets = p->next;
21617191de28Ssthen 	/* no memory recycling for zone deletions ... */
21627191de28Ssthen }
21637191de28Ssthen 
2164933707f3Ssthen void local_zones_del_data(struct local_zones* zones,
2165933707f3Ssthen 	uint8_t* name, size_t len, int labs, uint16_t dclass)
2166933707f3Ssthen {
2167933707f3Ssthen 	/* find zone */
2168933707f3Ssthen 	struct local_zone* z;
2169933707f3Ssthen 	struct local_data* d;
21707191de28Ssthen 
21717191de28Ssthen 	/* remove DS */
21725d76a658Ssthen 	lock_rw_rdlock(&zones->lock);
21737191de28Ssthen 	z = local_zones_lookup(zones, name, len, labs, dclass, LDNS_RR_TYPE_DS);
21747191de28Ssthen 	if(z) {
21757191de28Ssthen 		lock_rw_wrlock(&z->lock);
2176eaf2578eSsthen 		d = local_zone_find_data(z, name, len, labs);
21777191de28Ssthen 		if(d) {
21787191de28Ssthen 			del_local_rrset(d, LDNS_RR_TYPE_DS);
21797191de28Ssthen 			del_empty_term(z, d, name, len, labs);
21807191de28Ssthen 		}
21817191de28Ssthen 		lock_rw_unlock(&z->lock);
21827191de28Ssthen 	}
21837191de28Ssthen 	lock_rw_unlock(&zones->lock);
21847191de28Ssthen 
21857191de28Ssthen 	/* remove other types */
21867191de28Ssthen 	lock_rw_rdlock(&zones->lock);
21877191de28Ssthen 	z = local_zones_lookup(zones, name, len, labs, dclass, 0);
2188933707f3Ssthen 	if(!z) {
2189933707f3Ssthen 		/* no such zone, we're done */
21905d76a658Ssthen 		lock_rw_unlock(&zones->lock);
2191933707f3Ssthen 		return;
2192933707f3Ssthen 	}
2193933707f3Ssthen 	lock_rw_wrlock(&z->lock);
21945d76a658Ssthen 	lock_rw_unlock(&zones->lock);
2195933707f3Ssthen 
2196933707f3Ssthen 	/* find the domain */
2197eaf2578eSsthen 	d = local_zone_find_data(z, name, len, labs);
2198933707f3Ssthen 	if(d) {
2199933707f3Ssthen 		/* no memory recycling for zone deletions ... */
2200933707f3Ssthen 		d->rrsets = NULL;
2201933707f3Ssthen 		/* did we delete the soa record ? */
22029982a05dSsthen 		if(query_dname_compare(d->name, z->name) == 0) {
2203933707f3Ssthen 			z->soa = NULL;
22049982a05dSsthen 			z->soa_negative = NULL;
22059982a05dSsthen 		}
2206933707f3Ssthen 
2207933707f3Ssthen 		/* cleanup the empty nonterminals for this name */
2208933707f3Ssthen 		del_empty_term(z, d, name, len, labs);
2209933707f3Ssthen 	}
2210933707f3Ssthen 
2211933707f3Ssthen 	lock_rw_unlock(&z->lock);
2212933707f3Ssthen }
2213