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