xref: /freebsd/contrib/unbound/testcode/unitneg.c (revision be771a7b7f4580a30d99e41a5bb1b93a385a119d)
1*be771a7bSCy Schubert /*
2*be771a7bSCy Schubert  * testcode/unitneg.c - unit test for negative cache routines.
3*be771a7bSCy Schubert  *
4*be771a7bSCy Schubert  * Copyright (c) 2008, NLnet Labs. All rights reserved.
5*be771a7bSCy Schubert  *
6*be771a7bSCy Schubert  * This software is open source.
7*be771a7bSCy Schubert  *
8*be771a7bSCy Schubert  * Redistribution and use in source and binary forms, with or without
9*be771a7bSCy Schubert  * modification, are permitted provided that the following conditions
10*be771a7bSCy Schubert  * are met:
11*be771a7bSCy Schubert  *
12*be771a7bSCy Schubert  * Redistributions of source code must retain the above copyright notice,
13*be771a7bSCy Schubert  * this list of conditions and the following disclaimer.
14*be771a7bSCy Schubert  *
15*be771a7bSCy Schubert  * Redistributions in binary form must reproduce the above copyright notice,
16*be771a7bSCy Schubert  * this list of conditions and the following disclaimer in the documentation
17*be771a7bSCy Schubert  * and/or other materials provided with the distribution.
18*be771a7bSCy Schubert  *
19*be771a7bSCy Schubert  * Neither the name of the NLNET LABS nor the names of its contributors may
20*be771a7bSCy Schubert  * be used to endorse or promote products derived from this software without
21*be771a7bSCy Schubert  * specific prior written permission.
22*be771a7bSCy Schubert  *
23*be771a7bSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24*be771a7bSCy Schubert  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25*be771a7bSCy Schubert  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26*be771a7bSCy Schubert  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27*be771a7bSCy Schubert  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28*be771a7bSCy Schubert  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29*be771a7bSCy Schubert  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30*be771a7bSCy Schubert  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31*be771a7bSCy Schubert  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32*be771a7bSCy Schubert  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33*be771a7bSCy Schubert  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*be771a7bSCy Schubert  *
35*be771a7bSCy Schubert  */
36*be771a7bSCy Schubert /**
37*be771a7bSCy Schubert  * \file
38*be771a7bSCy Schubert  * Calls negative cache unit tests. Exits with code 1 on a failure.
39*be771a7bSCy Schubert  */
40*be771a7bSCy Schubert 
41*be771a7bSCy Schubert #include "config.h"
42*be771a7bSCy Schubert #include "util/log.h"
43*be771a7bSCy Schubert #include "util/net_help.h"
44*be771a7bSCy Schubert #include "util/data/packed_rrset.h"
45*be771a7bSCy Schubert #include "util/data/dname.h"
46*be771a7bSCy Schubert #include "testcode/unitmain.h"
47*be771a7bSCy Schubert #include "validator/val_neg.h"
48*be771a7bSCy Schubert #include "sldns/rrdef.h"
49*be771a7bSCy Schubert 
50*be771a7bSCy Schubert /** verbose unit test for negative cache */
51*be771a7bSCy Schubert static int negverbose = 0;
52*be771a7bSCy Schubert 
53*be771a7bSCy Schubert /** debug printout of neg cache */
54*be771a7bSCy Schubert static void print_neg_cache(struct val_neg_cache* neg)
55*be771a7bSCy Schubert {
56*be771a7bSCy Schubert 	char buf[LDNS_MAX_DOMAINLEN];
57*be771a7bSCy Schubert 	struct val_neg_zone* z;
58*be771a7bSCy Schubert 	struct val_neg_data* d;
59*be771a7bSCy Schubert 	printf("neg_cache print\n");
60*be771a7bSCy Schubert 	printf("memuse %d of %d\n", (int)neg->use, (int)neg->max);
61*be771a7bSCy Schubert 	printf("maxiter %d\n", (int)neg->nsec3_max_iter);
62*be771a7bSCy Schubert 	printf("%d zones\n", (int)neg->tree.count);
63*be771a7bSCy Schubert 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
64*be771a7bSCy Schubert 		dname_str(z->name, buf);
65*be771a7bSCy Schubert 		printf("%24s", buf);
66*be771a7bSCy Schubert 		printf(" len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
67*be771a7bSCy Schubert 			(int)z->len, z->labs, (int)z->in_use, z->count,
68*be771a7bSCy Schubert 			(int)z->tree.count);
69*be771a7bSCy Schubert 	}
70*be771a7bSCy Schubert 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
71*be771a7bSCy Schubert 		printf("\n");
72*be771a7bSCy Schubert 		dname_print(stdout, NULL, z->name);
73*be771a7bSCy Schubert 		printf(" zone details\n");
74*be771a7bSCy Schubert 		printf("len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
75*be771a7bSCy Schubert 			(int)z->len, z->labs, (int)z->in_use, z->count,
76*be771a7bSCy Schubert 			(int)z->tree.count);
77*be771a7bSCy Schubert 		if(z->parent) {
78*be771a7bSCy Schubert 			printf("parent=");
79*be771a7bSCy Schubert 			dname_print(stdout, NULL, z->parent->name);
80*be771a7bSCy Schubert 			printf("\n");
81*be771a7bSCy Schubert 		} else {
82*be771a7bSCy Schubert 			printf("parent=NULL\n");
83*be771a7bSCy Schubert 		}
84*be771a7bSCy Schubert 
85*be771a7bSCy Schubert 		RBTREE_FOR(d, struct val_neg_data*, &z->tree) {
86*be771a7bSCy Schubert 			dname_str(d->name, buf);
87*be771a7bSCy Schubert 			printf("%24s", buf);
88*be771a7bSCy Schubert 			printf(" len=%2.2d labs=%d inuse=%d count=%d\n",
89*be771a7bSCy Schubert 				(int)d->len, d->labs, (int)d->in_use, d->count);
90*be771a7bSCy Schubert 		}
91*be771a7bSCy Schubert 	}
92*be771a7bSCy Schubert }
93*be771a7bSCy Schubert 
94*be771a7bSCy Schubert /** get static pointer to random zone name */
95*be771a7bSCy Schubert static char* get_random_zone(void)
96*be771a7bSCy Schubert {
97*be771a7bSCy Schubert 	static char zname[36];
98*be771a7bSCy Schubert 	int labels = random() % 3;
99*be771a7bSCy Schubert 	int i;
100*be771a7bSCy Schubert 	char* p = zname;
101*be771a7bSCy Schubert 	int labnum;
102*be771a7bSCy Schubert 
103*be771a7bSCy Schubert 	for(i=0; i<labels; i++) {
104*be771a7bSCy Schubert 		labnum = random()%10;
105*be771a7bSCy Schubert 		snprintf(p, sizeof(zname)-(p-zname), "\003%3.3d", labnum);
106*be771a7bSCy Schubert 		p+=4;
107*be771a7bSCy Schubert 	}
108*be771a7bSCy Schubert 	snprintf(p, sizeof(zname)-(p-zname), "\007example\003com");
109*be771a7bSCy Schubert 	return zname;
110*be771a7bSCy Schubert }
111*be771a7bSCy Schubert 
112*be771a7bSCy Schubert /** get static pointer to random data names from and to */
113*be771a7bSCy Schubert static void get_random_data(char** fromp, char** top, char* zname)
114*be771a7bSCy Schubert {
115*be771a7bSCy Schubert 	static char buf1[256], buf2[256];
116*be771a7bSCy Schubert 	int type;
117*be771a7bSCy Schubert 	int lab1, lab2;
118*be771a7bSCy Schubert 	int labnum1[10], labnum2[10];
119*be771a7bSCy Schubert 	int i;
120*be771a7bSCy Schubert 	char* p;
121*be771a7bSCy Schubert 	memset(labnum1, 0, sizeof(int)*10);
122*be771a7bSCy Schubert 	memset(labnum2, 0, sizeof(int)*10);
123*be771a7bSCy Schubert 
124*be771a7bSCy Schubert 	*fromp = buf1;
125*be771a7bSCy Schubert 	*top = buf2;
126*be771a7bSCy Schubert 	type = random()%10;
127*be771a7bSCy Schubert 
128*be771a7bSCy Schubert 	if(type == 0) {
129*be771a7bSCy Schubert 		/* ENT */
130*be771a7bSCy Schubert 		lab1 = random() %3 + 1;
131*be771a7bSCy Schubert 		lab2 = lab1 + random()%3 + 1;
132*be771a7bSCy Schubert 		for(i=0; i<lab1; i++) {
133*be771a7bSCy Schubert 			labnum1[i] = random()%100;
134*be771a7bSCy Schubert 			labnum2[i] = labnum1[i];
135*be771a7bSCy Schubert 		}
136*be771a7bSCy Schubert 		for(i=lab1; i<lab2; i++) {
137*be771a7bSCy Schubert 			labnum2[i] = random()%100;
138*be771a7bSCy Schubert 		}
139*be771a7bSCy Schubert 	} else if(type == 1) {
140*be771a7bSCy Schubert 		/* end of zone */
141*be771a7bSCy Schubert 		lab2 = 0;
142*be771a7bSCy Schubert 		lab1 = random()%3 + 1;
143*be771a7bSCy Schubert 		for(i=0; i<lab1; i++) {
144*be771a7bSCy Schubert 			labnum1[i] = random()%100;
145*be771a7bSCy Schubert 		}
146*be771a7bSCy Schubert 	} else if(type == 2) {
147*be771a7bSCy Schubert 		/* start of zone */
148*be771a7bSCy Schubert 		lab1 = 0;
149*be771a7bSCy Schubert 		lab2 = random()%3 + 1;
150*be771a7bSCy Schubert 		for(i=0; i<lab2; i++) {
151*be771a7bSCy Schubert 			labnum2[i] = random()%100;
152*be771a7bSCy Schubert 		}
153*be771a7bSCy Schubert 	} else {
154*be771a7bSCy Schubert 		/* normal item */
155*be771a7bSCy Schubert 		int common = random()%3;
156*be771a7bSCy Schubert 		lab1 = random() %3 + 1;
157*be771a7bSCy Schubert 		lab2 = random() %3 + 1;
158*be771a7bSCy Schubert 		for(i=0; i<common; i++) {
159*be771a7bSCy Schubert 			labnum1[i] = random()%100;
160*be771a7bSCy Schubert 			labnum2[i] = labnum1[i];
161*be771a7bSCy Schubert 		}
162*be771a7bSCy Schubert 		labnum1[common] = random()%100;
163*be771a7bSCy Schubert 		labnum2[common] = labnum1[common] + random()%20;
164*be771a7bSCy Schubert 		for(i=common; i<lab1; i++)
165*be771a7bSCy Schubert 			labnum1[i] = random()%100;
166*be771a7bSCy Schubert 		for(i=common; i<lab2; i++)
167*be771a7bSCy Schubert 			labnum2[i] = random()%100;
168*be771a7bSCy Schubert 	}
169*be771a7bSCy Schubert 
170*be771a7bSCy Schubert 	/* construct first */
171*be771a7bSCy Schubert 	p = buf1;
172*be771a7bSCy Schubert 	for(i=0; i<lab1; i++) {
173*be771a7bSCy Schubert 		snprintf(p, 256-(p-buf1), "\003%3.3d", labnum1[i]);
174*be771a7bSCy Schubert 		p+=4;
175*be771a7bSCy Schubert 	}
176*be771a7bSCy Schubert 	snprintf(p, 256-(p-buf1), "%s", zname);
177*be771a7bSCy Schubert 
178*be771a7bSCy Schubert 	/* construct 2nd */
179*be771a7bSCy Schubert 	p = buf2+2;
180*be771a7bSCy Schubert 	for(i=0; i<lab2; i++) {
181*be771a7bSCy Schubert 		snprintf(p, 256-(p-buf2)-3, "\003%3.3d", labnum2[i]);
182*be771a7bSCy Schubert 		p+=4;
183*be771a7bSCy Schubert 	}
184*be771a7bSCy Schubert 	snprintf(p, 256-(p-buf2)-3, "%s", zname);
185*be771a7bSCy Schubert 	buf2[0] = (char)(strlen(buf2+2)+1);
186*be771a7bSCy Schubert 	buf2[1] = 0;
187*be771a7bSCy Schubert 
188*be771a7bSCy Schubert 	if(negverbose) {
189*be771a7bSCy Schubert 		log_nametypeclass(0, "add from", (uint8_t*)buf1, 0, 0);
190*be771a7bSCy Schubert 		log_nametypeclass(0, "add to  ", (uint8_t*)buf2+2, 0, 0);
191*be771a7bSCy Schubert 	}
192*be771a7bSCy Schubert }
193*be771a7bSCy Schubert 
194*be771a7bSCy Schubert /** add a random item */
195*be771a7bSCy Schubert static void add_item(struct val_neg_cache* neg)
196*be771a7bSCy Schubert {
197*be771a7bSCy Schubert 	struct val_neg_zone* z;
198*be771a7bSCy Schubert 	struct packed_rrset_data rd;
199*be771a7bSCy Schubert 	struct ub_packed_rrset_key nsec;
200*be771a7bSCy Schubert 	size_t rr_len;
201*be771a7bSCy Schubert 	time_t rr_ttl;
202*be771a7bSCy Schubert 	uint8_t* rr_data;
203*be771a7bSCy Schubert 	char* zname = get_random_zone();
204*be771a7bSCy Schubert 	char* from, *to;
205*be771a7bSCy Schubert 
206*be771a7bSCy Schubert 	lock_basic_lock(&neg->lock);
207*be771a7bSCy Schubert 	if(negverbose)
208*be771a7bSCy Schubert 		log_nametypeclass(0, "add to zone", (uint8_t*)zname, 0, 0);
209*be771a7bSCy Schubert 	z = neg_find_zone(neg, (uint8_t*)zname, strlen(zname)+1,
210*be771a7bSCy Schubert 		LDNS_RR_CLASS_IN);
211*be771a7bSCy Schubert 	if(!z) {
212*be771a7bSCy Schubert 		z = neg_create_zone(neg,  (uint8_t*)zname, strlen(zname)+1,
213*be771a7bSCy Schubert 		                LDNS_RR_CLASS_IN);
214*be771a7bSCy Schubert 	}
215*be771a7bSCy Schubert 	unit_assert(z);
216*be771a7bSCy Schubert 	val_neg_zone_take_inuse(z);
217*be771a7bSCy Schubert 
218*be771a7bSCy Schubert 	/* construct random NSEC item */
219*be771a7bSCy Schubert 	get_random_data(&from, &to, zname);
220*be771a7bSCy Schubert 
221*be771a7bSCy Schubert 	/* create nsec and insert it */
222*be771a7bSCy Schubert 	memset(&rd, 0, sizeof(rd));
223*be771a7bSCy Schubert 	memset(&nsec, 0, sizeof(nsec));
224*be771a7bSCy Schubert 	nsec.rk.dname = (uint8_t*)from;
225*be771a7bSCy Schubert 	nsec.rk.dname_len = strlen(from)+1;
226*be771a7bSCy Schubert 	nsec.rk.type = htons(LDNS_RR_TYPE_NSEC);
227*be771a7bSCy Schubert 	nsec.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
228*be771a7bSCy Schubert 	nsec.entry.data = &rd;
229*be771a7bSCy Schubert 	rd.security = sec_status_secure;
230*be771a7bSCy Schubert 	rd.count = 1;
231*be771a7bSCy Schubert 	rd.rr_len = &rr_len;
232*be771a7bSCy Schubert 	rr_len = 19;
233*be771a7bSCy Schubert 	rd.rr_ttl = &rr_ttl;
234*be771a7bSCy Schubert 	rr_ttl = 0;
235*be771a7bSCy Schubert 	rd.rr_data = &rr_data;
236*be771a7bSCy Schubert 	rr_data = (uint8_t*)to;
237*be771a7bSCy Schubert 
238*be771a7bSCy Schubert 	neg_insert_data(neg, z, &nsec);
239*be771a7bSCy Schubert 	lock_basic_unlock(&neg->lock);
240*be771a7bSCy Schubert }
241*be771a7bSCy Schubert 
242*be771a7bSCy Schubert /** remove a random item */
243*be771a7bSCy Schubert static void remove_item(struct val_neg_cache* neg)
244*be771a7bSCy Schubert {
245*be771a7bSCy Schubert 	int n, i;
246*be771a7bSCy Schubert 	struct val_neg_data* d;
247*be771a7bSCy Schubert 	rbnode_type* walk;
248*be771a7bSCy Schubert 	struct val_neg_zone* z;
249*be771a7bSCy Schubert 
250*be771a7bSCy Schubert 	lock_basic_lock(&neg->lock);
251*be771a7bSCy Schubert 	if(neg->tree.count == 0) {
252*be771a7bSCy Schubert 		lock_basic_unlock(&neg->lock);
253*be771a7bSCy Schubert 		return; /* nothing to delete */
254*be771a7bSCy Schubert 	}
255*be771a7bSCy Schubert 
256*be771a7bSCy Schubert 	/* pick a random zone */
257*be771a7bSCy Schubert 	walk = rbtree_first(&neg->tree); /* first highest parent, big count */
258*be771a7bSCy Schubert 	z = (struct val_neg_zone*)walk;
259*be771a7bSCy Schubert 	n = random() % (int)(z->count);
260*be771a7bSCy Schubert 	if(negverbose)
261*be771a7bSCy Schubert 		printf("neg stress delete zone %d\n", n);
262*be771a7bSCy Schubert 	i=0;
263*be771a7bSCy Schubert 	walk = rbtree_first(&neg->tree);
264*be771a7bSCy Schubert 	z = (struct val_neg_zone*)walk;
265*be771a7bSCy Schubert 	while(i!=n+1 && walk && walk != RBTREE_NULL && !z->in_use) {
266*be771a7bSCy Schubert 		walk = rbtree_next(walk);
267*be771a7bSCy Schubert 		z = (struct val_neg_zone*)walk;
268*be771a7bSCy Schubert 		if(z->in_use)
269*be771a7bSCy Schubert 			i++;
270*be771a7bSCy Schubert 	}
271*be771a7bSCy Schubert 	if(!walk || walk == RBTREE_NULL) {
272*be771a7bSCy Schubert 		lock_basic_unlock(&neg->lock);
273*be771a7bSCy Schubert 		return;
274*be771a7bSCy Schubert 	}
275*be771a7bSCy Schubert 	if(!z->in_use) {
276*be771a7bSCy Schubert 		lock_basic_unlock(&neg->lock);
277*be771a7bSCy Schubert 		return;
278*be771a7bSCy Schubert 	}
279*be771a7bSCy Schubert 	if(negverbose)
280*be771a7bSCy Schubert 		log_nametypeclass(0, "delete zone", z->name, 0, 0);
281*be771a7bSCy Schubert 
282*be771a7bSCy Schubert 	/* pick a random nsec item. - that is in use */
283*be771a7bSCy Schubert 	walk = rbtree_first(&z->tree); /* first is highest parent */
284*be771a7bSCy Schubert 	d = (struct val_neg_data*)walk;
285*be771a7bSCy Schubert 	n = random() % (int)(d->count);
286*be771a7bSCy Schubert 	if(negverbose)
287*be771a7bSCy Schubert 		printf("neg stress delete item %d\n", n);
288*be771a7bSCy Schubert 	i=0;
289*be771a7bSCy Schubert 	walk = rbtree_first(&z->tree);
290*be771a7bSCy Schubert 	d = (struct val_neg_data*)walk;
291*be771a7bSCy Schubert 	while(i!=n+1 && walk && walk != RBTREE_NULL && !d->in_use) {
292*be771a7bSCy Schubert 		walk = rbtree_next(walk);
293*be771a7bSCy Schubert 		d = (struct val_neg_data*)walk;
294*be771a7bSCy Schubert 		if(d->in_use)
295*be771a7bSCy Schubert 			i++;
296*be771a7bSCy Schubert 	}
297*be771a7bSCy Schubert 	if(!walk || walk == RBTREE_NULL) {
298*be771a7bSCy Schubert 		lock_basic_unlock(&neg->lock);
299*be771a7bSCy Schubert 		return;
300*be771a7bSCy Schubert 	}
301*be771a7bSCy Schubert 	if(d->in_use) {
302*be771a7bSCy Schubert 		if(negverbose)
303*be771a7bSCy Schubert 			log_nametypeclass(0, "neg delete item:", d->name, 0, 0);
304*be771a7bSCy Schubert 		neg_delete_data(neg, d);
305*be771a7bSCy Schubert 	}
306*be771a7bSCy Schubert 	lock_basic_unlock(&neg->lock);
307*be771a7bSCy Schubert }
308*be771a7bSCy Schubert 
309*be771a7bSCy Schubert /** sum up the zone trees */
310*be771a7bSCy Schubert static size_t sumtrees_all(struct val_neg_cache* neg)
311*be771a7bSCy Schubert {
312*be771a7bSCy Schubert 	size_t res = 0;
313*be771a7bSCy Schubert 	struct val_neg_zone* z;
314*be771a7bSCy Schubert 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
315*be771a7bSCy Schubert 		res += z->tree.count;
316*be771a7bSCy Schubert 	}
317*be771a7bSCy Schubert 	return res;
318*be771a7bSCy Schubert }
319*be771a7bSCy Schubert 
320*be771a7bSCy Schubert /** sum up the zone trees, in_use only */
321*be771a7bSCy Schubert static size_t sumtrees_inuse(struct val_neg_cache* neg)
322*be771a7bSCy Schubert {
323*be771a7bSCy Schubert 	size_t res = 0;
324*be771a7bSCy Schubert 	struct val_neg_zone* z;
325*be771a7bSCy Schubert 	struct val_neg_data* d;
326*be771a7bSCy Schubert 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
327*be771a7bSCy Schubert 		/* get count of highest parent for num in use */
328*be771a7bSCy Schubert 		d = (struct val_neg_data*)rbtree_first(&z->tree);
329*be771a7bSCy Schubert 		if(d && (rbnode_type*)d!=RBTREE_NULL)
330*be771a7bSCy Schubert 			res += d->count;
331*be771a7bSCy Schubert 	}
332*be771a7bSCy Schubert 	return res;
333*be771a7bSCy Schubert }
334*be771a7bSCy Schubert 
335*be771a7bSCy Schubert /** check if lru is still valid */
336*be771a7bSCy Schubert static void check_lru(struct val_neg_cache* neg)
337*be771a7bSCy Schubert {
338*be771a7bSCy Schubert 	struct val_neg_data* p, *np;
339*be771a7bSCy Schubert 	size_t num = 0;
340*be771a7bSCy Schubert 	size_t inuse;
341*be771a7bSCy Schubert 	p = neg->first;
342*be771a7bSCy Schubert 	while(p) {
343*be771a7bSCy Schubert 		if(!p->prev) {
344*be771a7bSCy Schubert 			unit_assert(neg->first == p);
345*be771a7bSCy Schubert 		}
346*be771a7bSCy Schubert 		np = p->next;
347*be771a7bSCy Schubert 		if(np) {
348*be771a7bSCy Schubert 			unit_assert(np->prev == p);
349*be771a7bSCy Schubert 		} else {
350*be771a7bSCy Schubert 			unit_assert(neg->last == p);
351*be771a7bSCy Schubert 		}
352*be771a7bSCy Schubert 		num++;
353*be771a7bSCy Schubert 		p = np;
354*be771a7bSCy Schubert 	}
355*be771a7bSCy Schubert 	inuse = sumtrees_inuse(neg);
356*be771a7bSCy Schubert 	if(negverbose)
357*be771a7bSCy Schubert 		printf("num lru %d, inuse %d, all %d\n",
358*be771a7bSCy Schubert 			(int)num, (int)sumtrees_inuse(neg),
359*be771a7bSCy Schubert 			(int)sumtrees_all(neg));
360*be771a7bSCy Schubert 	unit_assert( num == inuse);
361*be771a7bSCy Schubert 	unit_assert( inuse <= sumtrees_all(neg));
362*be771a7bSCy Schubert }
363*be771a7bSCy Schubert 
364*be771a7bSCy Schubert /** sum up number of items inuse in subtree */
365*be771a7bSCy Schubert static int sum_subtree_inuse(struct val_neg_zone* zone,
366*be771a7bSCy Schubert 	struct val_neg_data* data)
367*be771a7bSCy Schubert {
368*be771a7bSCy Schubert 	struct val_neg_data* d;
369*be771a7bSCy Schubert 	int num = 0;
370*be771a7bSCy Schubert 	RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
371*be771a7bSCy Schubert 		if(dname_subdomain_c(d->name, data->name)) {
372*be771a7bSCy Schubert 			if(d->in_use)
373*be771a7bSCy Schubert 				num++;
374*be771a7bSCy Schubert 		}
375*be771a7bSCy Schubert 	}
376*be771a7bSCy Schubert 	return num;
377*be771a7bSCy Schubert }
378*be771a7bSCy Schubert 
379*be771a7bSCy Schubert /** sum up number of items inuse in subtree */
380*be771a7bSCy Schubert static int sum_zone_subtree_inuse(struct val_neg_cache* neg,
381*be771a7bSCy Schubert 	struct val_neg_zone* zone)
382*be771a7bSCy Schubert {
383*be771a7bSCy Schubert 	struct val_neg_zone* z;
384*be771a7bSCy Schubert 	int num = 0;
385*be771a7bSCy Schubert 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
386*be771a7bSCy Schubert 		if(dname_subdomain_c(z->name, zone->name)) {
387*be771a7bSCy Schubert 			if(z->in_use)
388*be771a7bSCy Schubert 				num++;
389*be771a7bSCy Schubert 		}
390*be771a7bSCy Schubert 	}
391*be771a7bSCy Schubert 	return num;
392*be771a7bSCy Schubert }
393*be771a7bSCy Schubert 
394*be771a7bSCy Schubert /** check point in data tree */
395*be771a7bSCy Schubert static void check_data(struct val_neg_zone* zone, struct val_neg_data* data)
396*be771a7bSCy Schubert {
397*be771a7bSCy Schubert 	unit_assert(data->count > 0);
398*be771a7bSCy Schubert 	if(data->parent) {
399*be771a7bSCy Schubert 		unit_assert(data->parent->count >= data->count);
400*be771a7bSCy Schubert 		if(data->parent->in_use) {
401*be771a7bSCy Schubert 			unit_assert(data->parent->count > data->count);
402*be771a7bSCy Schubert 		}
403*be771a7bSCy Schubert 		unit_assert(data->parent->labs == data->labs-1);
404*be771a7bSCy Schubert 		/* and parent must be one label shorter */
405*be771a7bSCy Schubert 		unit_assert(data->name[0] == (data->len-data->parent->len-1));
406*be771a7bSCy Schubert 		unit_assert(query_dname_compare(data->name + data->name[0]+1,
407*be771a7bSCy Schubert 			data->parent->name) == 0);
408*be771a7bSCy Schubert 	} else {
409*be771a7bSCy Schubert 		/* must be apex */
410*be771a7bSCy Schubert 		unit_assert(dname_is_root(data->name));
411*be771a7bSCy Schubert 	}
412*be771a7bSCy Schubert 	/* tree property: */
413*be771a7bSCy Schubert 	unit_assert(data->count == sum_subtree_inuse(zone, data));
414*be771a7bSCy Schubert }
415*be771a7bSCy Schubert 
416*be771a7bSCy Schubert /** check if tree of data in zone is valid */
417*be771a7bSCy Schubert static void checkzonetree(struct val_neg_zone* zone)
418*be771a7bSCy Schubert {
419*be771a7bSCy Schubert 	struct val_neg_data* d;
420*be771a7bSCy Schubert 
421*be771a7bSCy Schubert 	/* check all data in tree */
422*be771a7bSCy Schubert 	RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
423*be771a7bSCy Schubert 		check_data(zone, d);
424*be771a7bSCy Schubert 	}
425*be771a7bSCy Schubert }
426*be771a7bSCy Schubert 
427*be771a7bSCy Schubert /** check if negative cache is still valid */
428*be771a7bSCy Schubert static void check_zone_invariants(struct val_neg_cache* neg,
429*be771a7bSCy Schubert 	struct val_neg_zone* zone)
430*be771a7bSCy Schubert {
431*be771a7bSCy Schubert 	unit_assert(zone->nsec3_hash == 0);
432*be771a7bSCy Schubert 	unit_assert(zone->tree.cmp == &val_neg_data_compare);
433*be771a7bSCy Schubert 	unit_assert(zone->count != 0);
434*be771a7bSCy Schubert 
435*be771a7bSCy Schubert 	if(zone->tree.count == 0)
436*be771a7bSCy Schubert 		unit_assert(!zone->in_use);
437*be771a7bSCy Schubert 	else {
438*be771a7bSCy Schubert 		if(!zone->in_use) {
439*be771a7bSCy Schubert 			/* details on error */
440*be771a7bSCy Schubert 			log_nametypeclass(0, "zone", zone->name, 0, 0);
441*be771a7bSCy Schubert 			log_err("inuse %d count=%d tree.count=%d",
442*be771a7bSCy Schubert 				zone->in_use, zone->count,
443*be771a7bSCy Schubert 				(int)zone->tree.count);
444*be771a7bSCy Schubert 			if(negverbose)
445*be771a7bSCy Schubert 				print_neg_cache(neg);
446*be771a7bSCy Schubert 		}
447*be771a7bSCy Schubert 		unit_assert(zone->in_use);
448*be771a7bSCy Schubert 	}
449*be771a7bSCy Schubert 
450*be771a7bSCy Schubert 	if(zone->parent) {
451*be771a7bSCy Schubert 		unit_assert(zone->parent->count >= zone->count);
452*be771a7bSCy Schubert 		if(zone->parent->in_use) {
453*be771a7bSCy Schubert 			unit_assert(zone->parent->count > zone->count);
454*be771a7bSCy Schubert 		}
455*be771a7bSCy Schubert 		unit_assert(zone->parent->labs == zone->labs-1);
456*be771a7bSCy Schubert 		/* and parent must be one label shorter */
457*be771a7bSCy Schubert 		unit_assert(zone->name[0] == (zone->len-zone->parent->len-1));
458*be771a7bSCy Schubert 		unit_assert(query_dname_compare(zone->name + zone->name[0]+1,
459*be771a7bSCy Schubert 			zone->parent->name) == 0);
460*be771a7bSCy Schubert 	} else {
461*be771a7bSCy Schubert 		/* must be apex */
462*be771a7bSCy Schubert 		unit_assert(dname_is_root(zone->name));
463*be771a7bSCy Schubert 	}
464*be771a7bSCy Schubert 	/* tree property: */
465*be771a7bSCy Schubert 	unit_assert(zone->count == sum_zone_subtree_inuse(neg, zone));
466*be771a7bSCy Schubert 
467*be771a7bSCy Schubert 	/* check structure of zone data tree */
468*be771a7bSCy Schubert 	checkzonetree(zone);
469*be771a7bSCy Schubert }
470*be771a7bSCy Schubert 
471*be771a7bSCy Schubert /** check if negative cache is still valid */
472*be771a7bSCy Schubert static void check_neg_invariants(struct val_neg_cache* neg)
473*be771a7bSCy Schubert {
474*be771a7bSCy Schubert 	struct val_neg_zone* z;
475*be771a7bSCy Schubert 	/* check structure of LRU list */
476*be771a7bSCy Schubert 	lock_basic_lock(&neg->lock);
477*be771a7bSCy Schubert 	check_lru(neg);
478*be771a7bSCy Schubert 	unit_assert(neg->max == 1024*1024);
479*be771a7bSCy Schubert 	unit_assert(neg->nsec3_max_iter == 1500);
480*be771a7bSCy Schubert 	unit_assert(neg->tree.cmp == &val_neg_zone_compare);
481*be771a7bSCy Schubert 
482*be771a7bSCy Schubert 	if(neg->tree.count == 0) {
483*be771a7bSCy Schubert 		/* empty */
484*be771a7bSCy Schubert 		unit_assert(neg->tree.count == 0);
485*be771a7bSCy Schubert 		unit_assert(neg->first == NULL);
486*be771a7bSCy Schubert 		unit_assert(neg->last == NULL);
487*be771a7bSCy Schubert 		unit_assert(neg->use == 0);
488*be771a7bSCy Schubert 		lock_basic_unlock(&neg->lock);
489*be771a7bSCy Schubert 		return;
490*be771a7bSCy Schubert 	}
491*be771a7bSCy Schubert 
492*be771a7bSCy Schubert 	unit_assert(neg->first != NULL);
493*be771a7bSCy Schubert 	unit_assert(neg->last != NULL);
494*be771a7bSCy Schubert 
495*be771a7bSCy Schubert 	RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
496*be771a7bSCy Schubert 		check_zone_invariants(neg, z);
497*be771a7bSCy Schubert 	}
498*be771a7bSCy Schubert 	lock_basic_unlock(&neg->lock);
499*be771a7bSCy Schubert }
500*be771a7bSCy Schubert 
501*be771a7bSCy Schubert /** perform stress test on insert and delete in neg cache */
502*be771a7bSCy Schubert static void stress_test(struct val_neg_cache* neg)
503*be771a7bSCy Schubert {
504*be771a7bSCy Schubert 	int i;
505*be771a7bSCy Schubert 	if(negverbose)
506*be771a7bSCy Schubert 		printf("negcache test\n");
507*be771a7bSCy Schubert 	for(i=0; i<100; i++) {
508*be771a7bSCy Schubert 		if(random() % 10 < 8)
509*be771a7bSCy Schubert 			add_item(neg);
510*be771a7bSCy Schubert 		else	remove_item(neg);
511*be771a7bSCy Schubert 		check_neg_invariants(neg);
512*be771a7bSCy Schubert 	}
513*be771a7bSCy Schubert 	/* empty it */
514*be771a7bSCy Schubert 	if(negverbose)
515*be771a7bSCy Schubert 		printf("neg stress empty\n");
516*be771a7bSCy Schubert 	while(neg->first) {
517*be771a7bSCy Schubert 		remove_item(neg);
518*be771a7bSCy Schubert 		check_neg_invariants(neg);
519*be771a7bSCy Schubert 	}
520*be771a7bSCy Schubert 	if(negverbose)
521*be771a7bSCy Schubert 		printf("neg stress emptied\n");
522*be771a7bSCy Schubert 	unit_assert(neg->first == NULL);
523*be771a7bSCy Schubert 	/* insert again */
524*be771a7bSCy Schubert 	for(i=0; i<100; i++) {
525*be771a7bSCy Schubert 		if(random() % 10 < 8)
526*be771a7bSCy Schubert 			add_item(neg);
527*be771a7bSCy Schubert 		else	remove_item(neg);
528*be771a7bSCy Schubert 		check_neg_invariants(neg);
529*be771a7bSCy Schubert 	}
530*be771a7bSCy Schubert }
531*be771a7bSCy Schubert 
532*be771a7bSCy Schubert void neg_test(void)
533*be771a7bSCy Schubert {
534*be771a7bSCy Schubert 	struct val_neg_cache* neg;
535*be771a7bSCy Schubert 	srandom(48);
536*be771a7bSCy Schubert 	unit_show_feature("negative cache");
537*be771a7bSCy Schubert 
538*be771a7bSCy Schubert 	/* create with defaults */
539*be771a7bSCy Schubert 	neg = val_neg_create(NULL, 1500);
540*be771a7bSCy Schubert 	unit_assert(neg);
541*be771a7bSCy Schubert 
542*be771a7bSCy Schubert 	stress_test(neg);
543*be771a7bSCy Schubert 
544*be771a7bSCy Schubert 	neg_cache_delete(neg);
545*be771a7bSCy Schubert }
546