xref: /openbsd-src/usr.sbin/unbound/testcode/unitneg.c (revision 8771e50fb10730acf466ba65697c8115815f4874)
1712b2f30Ssthen /*
2712b2f30Ssthen  * testcode/unitneg.c - unit test for negative cache routines.
3712b2f30Ssthen  *
4712b2f30Ssthen  * Copyright (c) 2008, NLnet Labs. All rights reserved.
5712b2f30Ssthen  *
6712b2f30Ssthen  * This software is open source.
7712b2f30Ssthen  *
8712b2f30Ssthen  * Redistribution and use in source and binary forms, with or without
9712b2f30Ssthen  * modification, are permitted provided that the following conditions
10712b2f30Ssthen  * are met:
11712b2f30Ssthen  *
12712b2f30Ssthen  * Redistributions of source code must retain the above copyright notice,
13712b2f30Ssthen  * this list of conditions and the following disclaimer.
14712b2f30Ssthen  *
15712b2f30Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16712b2f30Ssthen  * this list of conditions and the following disclaimer in the documentation
17712b2f30Ssthen  * and/or other materials provided with the distribution.
18712b2f30Ssthen  *
19712b2f30Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20712b2f30Ssthen  * be used to endorse or promote products derived from this software without
21712b2f30Ssthen  * specific prior written permission.
22712b2f30Ssthen  *
23712b2f30Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24712b2f30Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25712b2f30Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26712b2f30Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27712b2f30Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28712b2f30Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29712b2f30Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30712b2f30Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31712b2f30Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32712b2f30Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33712b2f30Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34712b2f30Ssthen  *
35712b2f30Ssthen  */
36712b2f30Ssthen /**
37712b2f30Ssthen  * \file
38712b2f30Ssthen  * Calls negative cache unit tests. Exits with code 1 on a failure.
39712b2f30Ssthen  */
40712b2f30Ssthen 
41712b2f30Ssthen #include "config.h"
42712b2f30Ssthen #include "util/log.h"
43712b2f30Ssthen #include "util/net_help.h"
44712b2f30Ssthen #include "util/data/packed_rrset.h"
45712b2f30Ssthen #include "util/data/dname.h"
46712b2f30Ssthen #include "testcode/unitmain.h"
47712b2f30Ssthen #include "validator/val_neg.h"
48712b2f30Ssthen #include "sldns/rrdef.h"
49712b2f30Ssthen 
50712b2f30Ssthen /** verbose unit test for negative cache */
51712b2f30Ssthen static int negverbose = 0;
52712b2f30Ssthen 
53712b2f30Ssthen /** debug printout of neg cache */
print_neg_cache(struct val_neg_cache * neg)54712b2f30Ssthen static void print_neg_cache(struct val_neg_cache* neg)
55712b2f30Ssthen {
56712b2f30Ssthen 	char buf[1024];
57712b2f30Ssthen 	struct val_neg_zone* z;
58712b2f30Ssthen 	struct val_neg_data* d;
59712b2f30Ssthen 	printf("neg_cache print\n");
60712b2f30Ssthen 	printf("memuse %d of %d\n", (int)neg->use, (int)neg->max);
61712b2f30Ssthen 	printf("maxiter %d\n", (int)neg->nsec3_max_iter);
62712b2f30Ssthen 	printf("%d zones\n", (int)neg->tree.count);
63712b2f30Ssthen 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
64712b2f30Ssthen 		dname_str(z->name, buf);
65712b2f30Ssthen 		printf("%24s", buf);
66712b2f30Ssthen 		printf(" len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
67712b2f30Ssthen 			(int)z->len, z->labs, (int)z->in_use, z->count,
68712b2f30Ssthen 			(int)z->tree.count);
69712b2f30Ssthen 	}
70712b2f30Ssthen 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
71712b2f30Ssthen 		printf("\n");
72712b2f30Ssthen 		dname_print(stdout, NULL, z->name);
73712b2f30Ssthen 		printf(" zone details\n");
74712b2f30Ssthen 		printf("len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
75712b2f30Ssthen 			(int)z->len, z->labs, (int)z->in_use, z->count,
76712b2f30Ssthen 			(int)z->tree.count);
77712b2f30Ssthen 		if(z->parent) {
78712b2f30Ssthen 			printf("parent=");
79712b2f30Ssthen 			dname_print(stdout, NULL, z->parent->name);
80712b2f30Ssthen 			printf("\n");
81712b2f30Ssthen 		} else {
82712b2f30Ssthen 			printf("parent=NULL\n");
83712b2f30Ssthen 		}
84712b2f30Ssthen 
85712b2f30Ssthen 		RBTREE_FOR(d, struct val_neg_data*, &z->tree) {
86712b2f30Ssthen 			dname_str(d->name, buf);
87712b2f30Ssthen 			printf("%24s", buf);
88712b2f30Ssthen 			printf(" len=%2.2d labs=%d inuse=%d count=%d\n",
89712b2f30Ssthen 				(int)d->len, d->labs, (int)d->in_use, d->count);
90712b2f30Ssthen 		}
91712b2f30Ssthen 	}
92712b2f30Ssthen }
93712b2f30Ssthen 
94712b2f30Ssthen /** get static pointer to random zone name */
get_random_zone(void)95712b2f30Ssthen static char* get_random_zone(void)
96712b2f30Ssthen {
97712b2f30Ssthen 	static char zname[36];
98712b2f30Ssthen 	int labels = random() % 3;
99712b2f30Ssthen 	int i;
100712b2f30Ssthen 	char* p = zname;
101712b2f30Ssthen 	int labnum;
102712b2f30Ssthen 
103712b2f30Ssthen 	for(i=0; i<labels; i++) {
104712b2f30Ssthen 		labnum = random()%10;
105712b2f30Ssthen 		snprintf(p, sizeof(zname)-(p-zname), "\003%3.3d", labnum);
106712b2f30Ssthen 		p+=4;
107712b2f30Ssthen 	}
108712b2f30Ssthen 	snprintf(p, sizeof(zname)-(p-zname), "\007example\003com");
109712b2f30Ssthen 	return zname;
110712b2f30Ssthen }
111712b2f30Ssthen 
112712b2f30Ssthen /** get static pointer to random data names from and to */
get_random_data(char ** fromp,char ** top,char * zname)113712b2f30Ssthen static void get_random_data(char** fromp, char** top, char* zname)
114712b2f30Ssthen {
115712b2f30Ssthen 	static char buf1[256], buf2[256];
116712b2f30Ssthen 	int type;
117712b2f30Ssthen 	int lab1, lab2;
118712b2f30Ssthen 	int labnum1[10], labnum2[10];
119712b2f30Ssthen 	int i;
120712b2f30Ssthen 	char* p;
121*8771e50fSsthen 	memset(labnum1, 0, sizeof(int)*10);
122*8771e50fSsthen 	memset(labnum2, 0, sizeof(int)*10);
123712b2f30Ssthen 
124712b2f30Ssthen 	*fromp = buf1;
125712b2f30Ssthen 	*top = buf2;
126712b2f30Ssthen 	type = random()%10;
127712b2f30Ssthen 
128712b2f30Ssthen 	if(type == 0) {
129712b2f30Ssthen 		/* ENT */
130712b2f30Ssthen 		lab1 = random() %3 + 1;
131712b2f30Ssthen 		lab2 = lab1 + random()%3 + 1;
132712b2f30Ssthen 		for(i=0; i<lab1; i++) {
133712b2f30Ssthen 			labnum1[i] = random()%100;
134712b2f30Ssthen 			labnum2[i] = labnum1[i];
135712b2f30Ssthen 		}
136712b2f30Ssthen 		for(i=lab1; i<lab2; i++) {
137712b2f30Ssthen 			labnum2[i] = random()%100;
138712b2f30Ssthen 		}
139712b2f30Ssthen 	} else if(type == 1) {
140712b2f30Ssthen 		/* end of zone */
141712b2f30Ssthen 		lab2 = 0;
142712b2f30Ssthen 		lab1 = random()%3 + 1;
143712b2f30Ssthen 		for(i=0; i<lab1; i++) {
144712b2f30Ssthen 			labnum1[i] = random()%100;
145712b2f30Ssthen 		}
146712b2f30Ssthen 	} else if(type == 2) {
147712b2f30Ssthen 		/* start of zone */
148712b2f30Ssthen 		lab1 = 0;
149712b2f30Ssthen 		lab2 = random()%3 + 1;
150712b2f30Ssthen 		for(i=0; i<lab2; i++) {
151712b2f30Ssthen 			labnum2[i] = random()%100;
152712b2f30Ssthen 		}
153712b2f30Ssthen 	} else {
154712b2f30Ssthen 		/* normal item */
155712b2f30Ssthen 		int common = random()%3;
156712b2f30Ssthen 		lab1 = random() %3 + 1;
157712b2f30Ssthen 		lab2 = random() %3 + 1;
158712b2f30Ssthen 		for(i=0; i<common; i++) {
159712b2f30Ssthen 			labnum1[i] = random()%100;
160712b2f30Ssthen 			labnum2[i] = labnum1[i];
161712b2f30Ssthen 		}
162712b2f30Ssthen 		labnum1[common] = random()%100;
163712b2f30Ssthen 		labnum2[common] = labnum1[common] + random()%20;
164712b2f30Ssthen 		for(i=common; i<lab1; i++)
165712b2f30Ssthen 			labnum1[i] = random()%100;
166712b2f30Ssthen 		for(i=common; i<lab2; i++)
167712b2f30Ssthen 			labnum2[i] = random()%100;
168712b2f30Ssthen 	}
169712b2f30Ssthen 
170712b2f30Ssthen 	/* construct first */
171712b2f30Ssthen 	p = buf1;
172712b2f30Ssthen 	for(i=0; i<lab1; i++) {
173712b2f30Ssthen 		snprintf(p, 256-(p-buf1), "\003%3.3d", labnum1[i]);
174712b2f30Ssthen 		p+=4;
175712b2f30Ssthen 	}
176712b2f30Ssthen 	snprintf(p, 256-(p-buf1), "%s", zname);
177712b2f30Ssthen 
178712b2f30Ssthen 	/* construct 2nd */
179712b2f30Ssthen 	p = buf2+2;
180712b2f30Ssthen 	for(i=0; i<lab2; i++) {
181712b2f30Ssthen 		snprintf(p, 256-(p-buf2)-3, "\003%3.3d", labnum2[i]);
182712b2f30Ssthen 		p+=4;
183712b2f30Ssthen 	}
184712b2f30Ssthen 	snprintf(p, 256-(p-buf2)-3, "%s", zname);
185712b2f30Ssthen 	buf2[0] = (char)(strlen(buf2+2)+1);
186712b2f30Ssthen 	buf2[1] = 0;
187712b2f30Ssthen 
188712b2f30Ssthen 	if(negverbose) {
189712b2f30Ssthen 		log_nametypeclass(0, "add from", (uint8_t*)buf1, 0, 0);
190712b2f30Ssthen 		log_nametypeclass(0, "add to  ", (uint8_t*)buf2+2, 0, 0);
191712b2f30Ssthen 	}
192712b2f30Ssthen }
193712b2f30Ssthen 
194712b2f30Ssthen /** add a random item */
add_item(struct val_neg_cache * neg)195712b2f30Ssthen static void add_item(struct val_neg_cache* neg)
196712b2f30Ssthen {
197712b2f30Ssthen 	struct val_neg_zone* z;
198712b2f30Ssthen 	struct packed_rrset_data rd;
199712b2f30Ssthen 	struct ub_packed_rrset_key nsec;
200712b2f30Ssthen 	size_t rr_len;
201712b2f30Ssthen 	time_t rr_ttl;
202712b2f30Ssthen 	uint8_t* rr_data;
203712b2f30Ssthen 	char* zname = get_random_zone();
204712b2f30Ssthen 	char* from, *to;
205712b2f30Ssthen 
206712b2f30Ssthen 	lock_basic_lock(&neg->lock);
207712b2f30Ssthen 	if(negverbose)
208712b2f30Ssthen 		log_nametypeclass(0, "add to zone", (uint8_t*)zname, 0, 0);
209712b2f30Ssthen 	z = neg_find_zone(neg, (uint8_t*)zname, strlen(zname)+1,
210712b2f30Ssthen 		LDNS_RR_CLASS_IN);
211712b2f30Ssthen 	if(!z) {
212712b2f30Ssthen 		z = neg_create_zone(neg,  (uint8_t*)zname, strlen(zname)+1,
213712b2f30Ssthen 		                LDNS_RR_CLASS_IN);
214712b2f30Ssthen 	}
215712b2f30Ssthen 	unit_assert(z);
216712b2f30Ssthen 	val_neg_zone_take_inuse(z);
217712b2f30Ssthen 
218712b2f30Ssthen 	/* construct random NSEC item */
219712b2f30Ssthen 	get_random_data(&from, &to, zname);
220712b2f30Ssthen 
221712b2f30Ssthen 	/* create nsec and insert it */
222712b2f30Ssthen 	memset(&rd, 0, sizeof(rd));
223712b2f30Ssthen 	memset(&nsec, 0, sizeof(nsec));
224712b2f30Ssthen 	nsec.rk.dname = (uint8_t*)from;
225712b2f30Ssthen 	nsec.rk.dname_len = strlen(from)+1;
226712b2f30Ssthen 	nsec.rk.type = htons(LDNS_RR_TYPE_NSEC);
227712b2f30Ssthen 	nsec.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
228712b2f30Ssthen 	nsec.entry.data = &rd;
229712b2f30Ssthen 	rd.security = sec_status_secure;
230712b2f30Ssthen 	rd.count = 1;
231712b2f30Ssthen 	rd.rr_len = &rr_len;
232712b2f30Ssthen 	rr_len = 19;
233712b2f30Ssthen 	rd.rr_ttl = &rr_ttl;
234712b2f30Ssthen 	rr_ttl = 0;
235712b2f30Ssthen 	rd.rr_data = &rr_data;
236712b2f30Ssthen 	rr_data = (uint8_t*)to;
237712b2f30Ssthen 
238712b2f30Ssthen 	neg_insert_data(neg, z, &nsec);
239712b2f30Ssthen 	lock_basic_unlock(&neg->lock);
240712b2f30Ssthen }
241712b2f30Ssthen 
242712b2f30Ssthen /** remove a random item */
remove_item(struct val_neg_cache * neg)243712b2f30Ssthen static void remove_item(struct val_neg_cache* neg)
244712b2f30Ssthen {
245712b2f30Ssthen 	int n, i;
246712b2f30Ssthen 	struct val_neg_data* d;
247712b2f30Ssthen 	rbnode_type* walk;
248712b2f30Ssthen 	struct val_neg_zone* z;
249712b2f30Ssthen 
250712b2f30Ssthen 	lock_basic_lock(&neg->lock);
251712b2f30Ssthen 	if(neg->tree.count == 0) {
252712b2f30Ssthen 		lock_basic_unlock(&neg->lock);
253712b2f30Ssthen 		return; /* nothing to delete */
254712b2f30Ssthen 	}
255712b2f30Ssthen 
256712b2f30Ssthen 	/* pick a random zone */
257712b2f30Ssthen 	walk = rbtree_first(&neg->tree); /* first highest parent, big count */
258712b2f30Ssthen 	z = (struct val_neg_zone*)walk;
259712b2f30Ssthen 	n = random() % (int)(z->count);
260712b2f30Ssthen 	if(negverbose)
261712b2f30Ssthen 		printf("neg stress delete zone %d\n", n);
262712b2f30Ssthen 	i=0;
263712b2f30Ssthen 	walk = rbtree_first(&neg->tree);
264712b2f30Ssthen 	z = (struct val_neg_zone*)walk;
265712b2f30Ssthen 	while(i!=n+1 && walk && walk != RBTREE_NULL && !z->in_use) {
266712b2f30Ssthen 		walk = rbtree_next(walk);
267712b2f30Ssthen 		z = (struct val_neg_zone*)walk;
268712b2f30Ssthen 		if(z->in_use)
269712b2f30Ssthen 			i++;
270712b2f30Ssthen 	}
271712b2f30Ssthen 	if(!walk || walk == RBTREE_NULL) {
272712b2f30Ssthen 		lock_basic_unlock(&neg->lock);
273712b2f30Ssthen 		return;
274712b2f30Ssthen 	}
275712b2f30Ssthen 	if(!z->in_use) {
276712b2f30Ssthen 		lock_basic_unlock(&neg->lock);
277712b2f30Ssthen 		return;
278712b2f30Ssthen 	}
279712b2f30Ssthen 	if(negverbose)
280712b2f30Ssthen 		log_nametypeclass(0, "delete zone", z->name, 0, 0);
281712b2f30Ssthen 
282712b2f30Ssthen 	/* pick a random nsec item. - that is in use */
283712b2f30Ssthen 	walk = rbtree_first(&z->tree); /* first is highest parent */
284712b2f30Ssthen 	d = (struct val_neg_data*)walk;
285712b2f30Ssthen 	n = random() % (int)(d->count);
286712b2f30Ssthen 	if(negverbose)
287712b2f30Ssthen 		printf("neg stress delete item %d\n", n);
288712b2f30Ssthen 	i=0;
289712b2f30Ssthen 	walk = rbtree_first(&z->tree);
290712b2f30Ssthen 	d = (struct val_neg_data*)walk;
291712b2f30Ssthen 	while(i!=n+1 && walk && walk != RBTREE_NULL && !d->in_use) {
292712b2f30Ssthen 		walk = rbtree_next(walk);
293712b2f30Ssthen 		d = (struct val_neg_data*)walk;
294712b2f30Ssthen 		if(d->in_use)
295712b2f30Ssthen 			i++;
296712b2f30Ssthen 	}
297712b2f30Ssthen 	if(!walk || walk == RBTREE_NULL) {
298712b2f30Ssthen 		lock_basic_unlock(&neg->lock);
299712b2f30Ssthen 		return;
300712b2f30Ssthen 	}
301712b2f30Ssthen 	if(d->in_use) {
302712b2f30Ssthen 		if(negverbose)
303712b2f30Ssthen 			log_nametypeclass(0, "neg delete item:", d->name, 0, 0);
304712b2f30Ssthen 		neg_delete_data(neg, d);
305712b2f30Ssthen 	}
306712b2f30Ssthen 	lock_basic_unlock(&neg->lock);
307712b2f30Ssthen }
308712b2f30Ssthen 
309712b2f30Ssthen /** sum up the zone trees */
sumtrees_all(struct val_neg_cache * neg)310712b2f30Ssthen static size_t sumtrees_all(struct val_neg_cache* neg)
311712b2f30Ssthen {
312712b2f30Ssthen 	size_t res = 0;
313712b2f30Ssthen 	struct val_neg_zone* z;
314712b2f30Ssthen 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
315712b2f30Ssthen 		res += z->tree.count;
316712b2f30Ssthen 	}
317712b2f30Ssthen 	return res;
318712b2f30Ssthen }
319712b2f30Ssthen 
320712b2f30Ssthen /** sum up the zone trees, in_use only */
sumtrees_inuse(struct val_neg_cache * neg)321712b2f30Ssthen static size_t sumtrees_inuse(struct val_neg_cache* neg)
322712b2f30Ssthen {
323712b2f30Ssthen 	size_t res = 0;
324712b2f30Ssthen 	struct val_neg_zone* z;
325712b2f30Ssthen 	struct val_neg_data* d;
326712b2f30Ssthen 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
327712b2f30Ssthen 		/* get count of highest parent for num in use */
328712b2f30Ssthen 		d = (struct val_neg_data*)rbtree_first(&z->tree);
329712b2f30Ssthen 		if(d && (rbnode_type*)d!=RBTREE_NULL)
330712b2f30Ssthen 			res += d->count;
331712b2f30Ssthen 	}
332712b2f30Ssthen 	return res;
333712b2f30Ssthen }
334712b2f30Ssthen 
335712b2f30Ssthen /** check if lru is still valid */
check_lru(struct val_neg_cache * neg)336712b2f30Ssthen static void check_lru(struct val_neg_cache* neg)
337712b2f30Ssthen {
338712b2f30Ssthen 	struct val_neg_data* p, *np;
339712b2f30Ssthen 	size_t num = 0;
340712b2f30Ssthen 	size_t inuse;
341712b2f30Ssthen 	p = neg->first;
342712b2f30Ssthen 	while(p) {
343712b2f30Ssthen 		if(!p->prev) {
344712b2f30Ssthen 			unit_assert(neg->first == p);
345712b2f30Ssthen 		}
346712b2f30Ssthen 		np = p->next;
347712b2f30Ssthen 		if(np) {
348712b2f30Ssthen 			unit_assert(np->prev == p);
349712b2f30Ssthen 		} else {
350712b2f30Ssthen 			unit_assert(neg->last == p);
351712b2f30Ssthen 		}
352712b2f30Ssthen 		num++;
353712b2f30Ssthen 		p = np;
354712b2f30Ssthen 	}
355712b2f30Ssthen 	inuse = sumtrees_inuse(neg);
356712b2f30Ssthen 	if(negverbose)
357712b2f30Ssthen 		printf("num lru %d, inuse %d, all %d\n",
358712b2f30Ssthen 			(int)num, (int)sumtrees_inuse(neg),
359712b2f30Ssthen 			(int)sumtrees_all(neg));
360712b2f30Ssthen 	unit_assert( num == inuse);
361712b2f30Ssthen 	unit_assert( inuse <= sumtrees_all(neg));
362712b2f30Ssthen }
363712b2f30Ssthen 
364712b2f30Ssthen /** sum up number of items inuse in subtree */
sum_subtree_inuse(struct val_neg_zone * zone,struct val_neg_data * data)365712b2f30Ssthen static int sum_subtree_inuse(struct val_neg_zone* zone,
366712b2f30Ssthen 	struct val_neg_data* data)
367712b2f30Ssthen {
368712b2f30Ssthen 	struct val_neg_data* d;
369712b2f30Ssthen 	int num = 0;
370712b2f30Ssthen 	RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
371712b2f30Ssthen 		if(dname_subdomain_c(d->name, data->name)) {
372712b2f30Ssthen 			if(d->in_use)
373712b2f30Ssthen 				num++;
374712b2f30Ssthen 		}
375712b2f30Ssthen 	}
376712b2f30Ssthen 	return num;
377712b2f30Ssthen }
378712b2f30Ssthen 
379712b2f30Ssthen /** sum up number of items inuse in subtree */
sum_zone_subtree_inuse(struct val_neg_cache * neg,struct val_neg_zone * zone)380712b2f30Ssthen static int sum_zone_subtree_inuse(struct val_neg_cache* neg,
381712b2f30Ssthen 	struct val_neg_zone* zone)
382712b2f30Ssthen {
383712b2f30Ssthen 	struct val_neg_zone* z;
384712b2f30Ssthen 	int num = 0;
385712b2f30Ssthen 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
386712b2f30Ssthen 		if(dname_subdomain_c(z->name, zone->name)) {
387712b2f30Ssthen 			if(z->in_use)
388712b2f30Ssthen 				num++;
389712b2f30Ssthen 		}
390712b2f30Ssthen 	}
391712b2f30Ssthen 	return num;
392712b2f30Ssthen }
393712b2f30Ssthen 
394712b2f30Ssthen /** check point in data tree */
check_data(struct val_neg_zone * zone,struct val_neg_data * data)395712b2f30Ssthen static void check_data(struct val_neg_zone* zone, struct val_neg_data* data)
396712b2f30Ssthen {
397712b2f30Ssthen 	unit_assert(data->count > 0);
398712b2f30Ssthen 	if(data->parent) {
399712b2f30Ssthen 		unit_assert(data->parent->count >= data->count);
400712b2f30Ssthen 		if(data->parent->in_use) {
401712b2f30Ssthen 			unit_assert(data->parent->count > data->count);
402712b2f30Ssthen 		}
403712b2f30Ssthen 		unit_assert(data->parent->labs == data->labs-1);
404712b2f30Ssthen 		/* and parent must be one label shorter */
405712b2f30Ssthen 		unit_assert(data->name[0] == (data->len-data->parent->len-1));
406712b2f30Ssthen 		unit_assert(query_dname_compare(data->name + data->name[0]+1,
407712b2f30Ssthen 			data->parent->name) == 0);
408712b2f30Ssthen 	} else {
409712b2f30Ssthen 		/* must be apex */
410712b2f30Ssthen 		unit_assert(dname_is_root(data->name));
411712b2f30Ssthen 	}
412712b2f30Ssthen 	/* tree property: */
413712b2f30Ssthen 	unit_assert(data->count == sum_subtree_inuse(zone, data));
414712b2f30Ssthen }
415712b2f30Ssthen 
416712b2f30Ssthen /** check if tree of data in zone is valid */
checkzonetree(struct val_neg_zone * zone)417712b2f30Ssthen static void checkzonetree(struct val_neg_zone* zone)
418712b2f30Ssthen {
419712b2f30Ssthen 	struct val_neg_data* d;
420712b2f30Ssthen 
421712b2f30Ssthen 	/* check all data in tree */
422712b2f30Ssthen 	RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
423712b2f30Ssthen 		check_data(zone, d);
424712b2f30Ssthen 	}
425712b2f30Ssthen }
426712b2f30Ssthen 
427712b2f30Ssthen /** check if negative cache is still valid */
check_zone_invariants(struct val_neg_cache * neg,struct val_neg_zone * zone)428712b2f30Ssthen static void check_zone_invariants(struct val_neg_cache* neg,
429712b2f30Ssthen 	struct val_neg_zone* zone)
430712b2f30Ssthen {
431712b2f30Ssthen 	unit_assert(zone->nsec3_hash == 0);
432712b2f30Ssthen 	unit_assert(zone->tree.cmp == &val_neg_data_compare);
433712b2f30Ssthen 	unit_assert(zone->count != 0);
434712b2f30Ssthen 
435712b2f30Ssthen 	if(zone->tree.count == 0)
436712b2f30Ssthen 		unit_assert(!zone->in_use);
437712b2f30Ssthen 	else {
438712b2f30Ssthen 		if(!zone->in_use) {
439712b2f30Ssthen 			/* details on error */
440712b2f30Ssthen 			log_nametypeclass(0, "zone", zone->name, 0, 0);
441712b2f30Ssthen 			log_err("inuse %d count=%d tree.count=%d",
442712b2f30Ssthen 				zone->in_use, zone->count,
443712b2f30Ssthen 				(int)zone->tree.count);
444712b2f30Ssthen 			if(negverbose)
445712b2f30Ssthen 				print_neg_cache(neg);
446712b2f30Ssthen 		}
447712b2f30Ssthen 		unit_assert(zone->in_use);
448712b2f30Ssthen 	}
449712b2f30Ssthen 
450712b2f30Ssthen 	if(zone->parent) {
451712b2f30Ssthen 		unit_assert(zone->parent->count >= zone->count);
452712b2f30Ssthen 		if(zone->parent->in_use) {
453712b2f30Ssthen 			unit_assert(zone->parent->count > zone->count);
454712b2f30Ssthen 		}
455712b2f30Ssthen 		unit_assert(zone->parent->labs == zone->labs-1);
456712b2f30Ssthen 		/* and parent must be one label shorter */
457712b2f30Ssthen 		unit_assert(zone->name[0] == (zone->len-zone->parent->len-1));
458712b2f30Ssthen 		unit_assert(query_dname_compare(zone->name + zone->name[0]+1,
459712b2f30Ssthen 			zone->parent->name) == 0);
460712b2f30Ssthen 	} else {
461712b2f30Ssthen 		/* must be apex */
462712b2f30Ssthen 		unit_assert(dname_is_root(zone->name));
463712b2f30Ssthen 	}
464712b2f30Ssthen 	/* tree property: */
465712b2f30Ssthen 	unit_assert(zone->count == sum_zone_subtree_inuse(neg, zone));
466712b2f30Ssthen 
467712b2f30Ssthen 	/* check structure of zone data tree */
468712b2f30Ssthen 	checkzonetree(zone);
469712b2f30Ssthen }
470712b2f30Ssthen 
471712b2f30Ssthen /** check if negative cache is still valid */
check_neg_invariants(struct val_neg_cache * neg)472712b2f30Ssthen static void check_neg_invariants(struct val_neg_cache* neg)
473712b2f30Ssthen {
474712b2f30Ssthen 	struct val_neg_zone* z;
475712b2f30Ssthen 	/* check structure of LRU list */
476712b2f30Ssthen 	lock_basic_lock(&neg->lock);
477712b2f30Ssthen 	check_lru(neg);
478712b2f30Ssthen 	unit_assert(neg->max == 1024*1024);
479712b2f30Ssthen 	unit_assert(neg->nsec3_max_iter == 1500);
480712b2f30Ssthen 	unit_assert(neg->tree.cmp == &val_neg_zone_compare);
481712b2f30Ssthen 
482712b2f30Ssthen 	if(neg->tree.count == 0) {
483712b2f30Ssthen 		/* empty */
484712b2f30Ssthen 		unit_assert(neg->tree.count == 0);
485712b2f30Ssthen 		unit_assert(neg->first == NULL);
486712b2f30Ssthen 		unit_assert(neg->last == NULL);
487712b2f30Ssthen 		unit_assert(neg->use == 0);
488712b2f30Ssthen 		lock_basic_unlock(&neg->lock);
489712b2f30Ssthen 		return;
490712b2f30Ssthen 	}
491712b2f30Ssthen 
492712b2f30Ssthen 	unit_assert(neg->first != NULL);
493712b2f30Ssthen 	unit_assert(neg->last != NULL);
494712b2f30Ssthen 
495712b2f30Ssthen 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
496712b2f30Ssthen 		check_zone_invariants(neg, z);
497712b2f30Ssthen 	}
498712b2f30Ssthen 	lock_basic_unlock(&neg->lock);
499712b2f30Ssthen }
500712b2f30Ssthen 
501712b2f30Ssthen /** perform stress test on insert and delete in neg cache */
stress_test(struct val_neg_cache * neg)502712b2f30Ssthen static void stress_test(struct val_neg_cache* neg)
503712b2f30Ssthen {
504712b2f30Ssthen 	int i;
505712b2f30Ssthen 	if(negverbose)
506712b2f30Ssthen 		printf("negcache test\n");
507712b2f30Ssthen 	for(i=0; i<100; i++) {
508712b2f30Ssthen 		if(random() % 10 < 8)
509712b2f30Ssthen 			add_item(neg);
510712b2f30Ssthen 		else	remove_item(neg);
511712b2f30Ssthen 		check_neg_invariants(neg);
512712b2f30Ssthen 	}
513712b2f30Ssthen 	/* empty it */
514712b2f30Ssthen 	if(negverbose)
515712b2f30Ssthen 		printf("neg stress empty\n");
516712b2f30Ssthen 	while(neg->first) {
517712b2f30Ssthen 		remove_item(neg);
518712b2f30Ssthen 		check_neg_invariants(neg);
519712b2f30Ssthen 	}
520712b2f30Ssthen 	if(negverbose)
521712b2f30Ssthen 		printf("neg stress emptied\n");
522712b2f30Ssthen 	unit_assert(neg->first == NULL);
523712b2f30Ssthen 	/* insert again */
524712b2f30Ssthen 	for(i=0; i<100; i++) {
525712b2f30Ssthen 		if(random() % 10 < 8)
526712b2f30Ssthen 			add_item(neg);
527712b2f30Ssthen 		else	remove_item(neg);
528712b2f30Ssthen 		check_neg_invariants(neg);
529712b2f30Ssthen 	}
530712b2f30Ssthen }
531712b2f30Ssthen 
neg_test(void)532712b2f30Ssthen void neg_test(void)
533712b2f30Ssthen {
534712b2f30Ssthen 	struct val_neg_cache* neg;
535712b2f30Ssthen 	srandom(48);
536712b2f30Ssthen 	unit_show_feature("negative cache");
537712b2f30Ssthen 
538712b2f30Ssthen 	/* create with defaults */
539712b2f30Ssthen 	neg = val_neg_create(NULL, 1500);
540712b2f30Ssthen 	unit_assert(neg);
541712b2f30Ssthen 
542712b2f30Ssthen 	stress_test(neg);
543712b2f30Ssthen 
544712b2f30Ssthen 	neg_cache_delete(neg);
545712b2f30Ssthen }
546