1*b7579f77SDag-Erling Smørgrav /* 2*b7579f77SDag-Erling Smørgrav * validator/val_neg.c - validator aggressive negative caching functions. 3*b7579f77SDag-Erling Smørgrav * 4*b7579f77SDag-Erling Smørgrav * Copyright (c) 2008, NLnet Labs. All rights reserved. 5*b7579f77SDag-Erling Smørgrav * 6*b7579f77SDag-Erling Smørgrav * This software is open source. 7*b7579f77SDag-Erling Smørgrav * 8*b7579f77SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 9*b7579f77SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 10*b7579f77SDag-Erling Smørgrav * are met: 11*b7579f77SDag-Erling Smørgrav * 12*b7579f77SDag-Erling Smørgrav * Redistributions of source code must retain the above copyright notice, 13*b7579f77SDag-Erling Smørgrav * this list of conditions and the following disclaimer. 14*b7579f77SDag-Erling Smørgrav * 15*b7579f77SDag-Erling Smørgrav * Redistributions in binary form must reproduce the above copyright notice, 16*b7579f77SDag-Erling Smørgrav * this list of conditions and the following disclaimer in the documentation 17*b7579f77SDag-Erling Smørgrav * and/or other materials provided with the distribution. 18*b7579f77SDag-Erling Smørgrav * 19*b7579f77SDag-Erling Smørgrav * Neither the name of the NLNET LABS nor the names of its contributors may 20*b7579f77SDag-Erling Smørgrav * be used to endorse or promote products derived from this software without 21*b7579f77SDag-Erling Smørgrav * specific prior written permission. 22*b7579f77SDag-Erling Smørgrav * 23*b7579f77SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24*b7579f77SDag-Erling Smørgrav * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25*b7579f77SDag-Erling Smørgrav * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26*b7579f77SDag-Erling Smørgrav * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 27*b7579f77SDag-Erling Smørgrav * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28*b7579f77SDag-Erling Smørgrav * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29*b7579f77SDag-Erling Smørgrav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30*b7579f77SDag-Erling Smørgrav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31*b7579f77SDag-Erling Smørgrav * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32*b7579f77SDag-Erling Smørgrav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33*b7579f77SDag-Erling Smørgrav * POSSIBILITY OF SUCH DAMAGE. 34*b7579f77SDag-Erling Smørgrav */ 35*b7579f77SDag-Erling Smørgrav 36*b7579f77SDag-Erling Smørgrav /** 37*b7579f77SDag-Erling Smørgrav * \file 38*b7579f77SDag-Erling Smørgrav * 39*b7579f77SDag-Erling Smørgrav * This file contains helper functions for the validator module. 40*b7579f77SDag-Erling Smørgrav * The functions help with aggressive negative caching. 41*b7579f77SDag-Erling Smørgrav * This creates new denials of existance, and proofs for absence of types 42*b7579f77SDag-Erling Smørgrav * from cached NSEC records. 43*b7579f77SDag-Erling Smørgrav */ 44*b7579f77SDag-Erling Smørgrav #include "config.h" 45*b7579f77SDag-Erling Smørgrav #ifdef HAVE_OPENSSL_SSL_H 46*b7579f77SDag-Erling Smørgrav #include "openssl/ssl.h" 47*b7579f77SDag-Erling Smørgrav #endif 48*b7579f77SDag-Erling Smørgrav #include "validator/val_neg.h" 49*b7579f77SDag-Erling Smørgrav #include "validator/val_nsec.h" 50*b7579f77SDag-Erling Smørgrav #include "validator/val_nsec3.h" 51*b7579f77SDag-Erling Smørgrav #include "validator/val_utils.h" 52*b7579f77SDag-Erling Smørgrav #include "util/data/dname.h" 53*b7579f77SDag-Erling Smørgrav #include "util/data/msgreply.h" 54*b7579f77SDag-Erling Smørgrav #include "util/log.h" 55*b7579f77SDag-Erling Smørgrav #include "util/net_help.h" 56*b7579f77SDag-Erling Smørgrav #include "util/config_file.h" 57*b7579f77SDag-Erling Smørgrav #include "services/cache/rrset.h" 58*b7579f77SDag-Erling Smørgrav #include "services/cache/dns.h" 59*b7579f77SDag-Erling Smørgrav 60*b7579f77SDag-Erling Smørgrav int val_neg_data_compare(const void* a, const void* b) 61*b7579f77SDag-Erling Smørgrav { 62*b7579f77SDag-Erling Smørgrav struct val_neg_data* x = (struct val_neg_data*)a; 63*b7579f77SDag-Erling Smørgrav struct val_neg_data* y = (struct val_neg_data*)b; 64*b7579f77SDag-Erling Smørgrav int m; 65*b7579f77SDag-Erling Smørgrav return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m); 66*b7579f77SDag-Erling Smørgrav } 67*b7579f77SDag-Erling Smørgrav 68*b7579f77SDag-Erling Smørgrav int val_neg_zone_compare(const void* a, const void* b) 69*b7579f77SDag-Erling Smørgrav { 70*b7579f77SDag-Erling Smørgrav struct val_neg_zone* x = (struct val_neg_zone*)a; 71*b7579f77SDag-Erling Smørgrav struct val_neg_zone* y = (struct val_neg_zone*)b; 72*b7579f77SDag-Erling Smørgrav int m; 73*b7579f77SDag-Erling Smørgrav if(x->dclass != y->dclass) { 74*b7579f77SDag-Erling Smørgrav if(x->dclass < y->dclass) 75*b7579f77SDag-Erling Smørgrav return -1; 76*b7579f77SDag-Erling Smørgrav return 1; 77*b7579f77SDag-Erling Smørgrav } 78*b7579f77SDag-Erling Smørgrav return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m); 79*b7579f77SDag-Erling Smørgrav } 80*b7579f77SDag-Erling Smørgrav 81*b7579f77SDag-Erling Smørgrav struct val_neg_cache* val_neg_create(struct config_file* cfg, size_t maxiter) 82*b7579f77SDag-Erling Smørgrav { 83*b7579f77SDag-Erling Smørgrav struct val_neg_cache* neg = (struct val_neg_cache*)calloc(1, 84*b7579f77SDag-Erling Smørgrav sizeof(*neg)); 85*b7579f77SDag-Erling Smørgrav if(!neg) { 86*b7579f77SDag-Erling Smørgrav log_err("Could not create neg cache: out of memory"); 87*b7579f77SDag-Erling Smørgrav return NULL; 88*b7579f77SDag-Erling Smørgrav } 89*b7579f77SDag-Erling Smørgrav neg->nsec3_max_iter = maxiter; 90*b7579f77SDag-Erling Smørgrav neg->max = 1024*1024; /* 1 M is thousands of entries */ 91*b7579f77SDag-Erling Smørgrav if(cfg) neg->max = cfg->neg_cache_size; 92*b7579f77SDag-Erling Smørgrav rbtree_init(&neg->tree, &val_neg_zone_compare); 93*b7579f77SDag-Erling Smørgrav lock_basic_init(&neg->lock); 94*b7579f77SDag-Erling Smørgrav lock_protect(&neg->lock, neg, sizeof(*neg)); 95*b7579f77SDag-Erling Smørgrav return neg; 96*b7579f77SDag-Erling Smørgrav } 97*b7579f77SDag-Erling Smørgrav 98*b7579f77SDag-Erling Smørgrav size_t val_neg_get_mem(struct val_neg_cache* neg) 99*b7579f77SDag-Erling Smørgrav { 100*b7579f77SDag-Erling Smørgrav size_t result; 101*b7579f77SDag-Erling Smørgrav lock_basic_lock(&neg->lock); 102*b7579f77SDag-Erling Smørgrav result = sizeof(*neg) + neg->use; 103*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 104*b7579f77SDag-Erling Smørgrav return result; 105*b7579f77SDag-Erling Smørgrav } 106*b7579f77SDag-Erling Smørgrav 107*b7579f77SDag-Erling Smørgrav /** clear datas on cache deletion */ 108*b7579f77SDag-Erling Smørgrav static void 109*b7579f77SDag-Erling Smørgrav neg_clear_datas(rbnode_t* n, void* ATTR_UNUSED(arg)) 110*b7579f77SDag-Erling Smørgrav { 111*b7579f77SDag-Erling Smørgrav struct val_neg_data* d = (struct val_neg_data*)n; 112*b7579f77SDag-Erling Smørgrav free(d->name); 113*b7579f77SDag-Erling Smørgrav free(d); 114*b7579f77SDag-Erling Smørgrav } 115*b7579f77SDag-Erling Smørgrav 116*b7579f77SDag-Erling Smørgrav /** clear zones on cache deletion */ 117*b7579f77SDag-Erling Smørgrav static void 118*b7579f77SDag-Erling Smørgrav neg_clear_zones(rbnode_t* n, void* ATTR_UNUSED(arg)) 119*b7579f77SDag-Erling Smørgrav { 120*b7579f77SDag-Erling Smørgrav struct val_neg_zone* z = (struct val_neg_zone*)n; 121*b7579f77SDag-Erling Smørgrav /* delete all the rrset entries in the tree */ 122*b7579f77SDag-Erling Smørgrav traverse_postorder(&z->tree, &neg_clear_datas, NULL); 123*b7579f77SDag-Erling Smørgrav free(z->nsec3_salt); 124*b7579f77SDag-Erling Smørgrav free(z->name); 125*b7579f77SDag-Erling Smørgrav free(z); 126*b7579f77SDag-Erling Smørgrav } 127*b7579f77SDag-Erling Smørgrav 128*b7579f77SDag-Erling Smørgrav void neg_cache_delete(struct val_neg_cache* neg) 129*b7579f77SDag-Erling Smørgrav { 130*b7579f77SDag-Erling Smørgrav if(!neg) return; 131*b7579f77SDag-Erling Smørgrav lock_basic_destroy(&neg->lock); 132*b7579f77SDag-Erling Smørgrav /* delete all the zones in the tree */ 133*b7579f77SDag-Erling Smørgrav traverse_postorder(&neg->tree, &neg_clear_zones, NULL); 134*b7579f77SDag-Erling Smørgrav free(neg); 135*b7579f77SDag-Erling Smørgrav } 136*b7579f77SDag-Erling Smørgrav 137*b7579f77SDag-Erling Smørgrav /** 138*b7579f77SDag-Erling Smørgrav * Put data element at the front of the LRU list. 139*b7579f77SDag-Erling Smørgrav * @param neg: negative cache with LRU start and end. 140*b7579f77SDag-Erling Smørgrav * @param data: this data is fronted. 141*b7579f77SDag-Erling Smørgrav */ 142*b7579f77SDag-Erling Smørgrav static void neg_lru_front(struct val_neg_cache* neg, 143*b7579f77SDag-Erling Smørgrav struct val_neg_data* data) 144*b7579f77SDag-Erling Smørgrav { 145*b7579f77SDag-Erling Smørgrav data->prev = NULL; 146*b7579f77SDag-Erling Smørgrav data->next = neg->first; 147*b7579f77SDag-Erling Smørgrav if(!neg->first) 148*b7579f77SDag-Erling Smørgrav neg->last = data; 149*b7579f77SDag-Erling Smørgrav else neg->first->prev = data; 150*b7579f77SDag-Erling Smørgrav neg->first = data; 151*b7579f77SDag-Erling Smørgrav } 152*b7579f77SDag-Erling Smørgrav 153*b7579f77SDag-Erling Smørgrav /** 154*b7579f77SDag-Erling Smørgrav * Remove data element from LRU list. 155*b7579f77SDag-Erling Smørgrav * @param neg: negative cache with LRU start and end. 156*b7579f77SDag-Erling Smørgrav * @param data: this data is removed from the list. 157*b7579f77SDag-Erling Smørgrav */ 158*b7579f77SDag-Erling Smørgrav static void neg_lru_remove(struct val_neg_cache* neg, 159*b7579f77SDag-Erling Smørgrav struct val_neg_data* data) 160*b7579f77SDag-Erling Smørgrav { 161*b7579f77SDag-Erling Smørgrav if(data->prev) 162*b7579f77SDag-Erling Smørgrav data->prev->next = data->next; 163*b7579f77SDag-Erling Smørgrav else neg->first = data->next; 164*b7579f77SDag-Erling Smørgrav if(data->next) 165*b7579f77SDag-Erling Smørgrav data->next->prev = data->prev; 166*b7579f77SDag-Erling Smørgrav else neg->last = data->prev; 167*b7579f77SDag-Erling Smørgrav } 168*b7579f77SDag-Erling Smørgrav 169*b7579f77SDag-Erling Smørgrav /** 170*b7579f77SDag-Erling Smørgrav * Touch LRU for data element, put it at the start of the LRU list. 171*b7579f77SDag-Erling Smørgrav * @param neg: negative cache with LRU start and end. 172*b7579f77SDag-Erling Smørgrav * @param data: this data is used. 173*b7579f77SDag-Erling Smørgrav */ 174*b7579f77SDag-Erling Smørgrav static void neg_lru_touch(struct val_neg_cache* neg, 175*b7579f77SDag-Erling Smørgrav struct val_neg_data* data) 176*b7579f77SDag-Erling Smørgrav { 177*b7579f77SDag-Erling Smørgrav if(data == neg->first) 178*b7579f77SDag-Erling Smørgrav return; /* nothing to do */ 179*b7579f77SDag-Erling Smørgrav /* remove from current lru position */ 180*b7579f77SDag-Erling Smørgrav neg_lru_remove(neg, data); 181*b7579f77SDag-Erling Smørgrav /* add at front */ 182*b7579f77SDag-Erling Smørgrav neg_lru_front(neg, data); 183*b7579f77SDag-Erling Smørgrav } 184*b7579f77SDag-Erling Smørgrav 185*b7579f77SDag-Erling Smørgrav /** 186*b7579f77SDag-Erling Smørgrav * Delete a zone element from the negative cache. 187*b7579f77SDag-Erling Smørgrav * May delete other zone elements to keep tree coherent, or 188*b7579f77SDag-Erling Smørgrav * only mark the element as 'not in use'. 189*b7579f77SDag-Erling Smørgrav * @param neg: negative cache. 190*b7579f77SDag-Erling Smørgrav * @param z: zone element to delete. 191*b7579f77SDag-Erling Smørgrav */ 192*b7579f77SDag-Erling Smørgrav static void neg_delete_zone(struct val_neg_cache* neg, struct val_neg_zone* z) 193*b7579f77SDag-Erling Smørgrav { 194*b7579f77SDag-Erling Smørgrav struct val_neg_zone* p, *np; 195*b7579f77SDag-Erling Smørgrav if(!z) return; 196*b7579f77SDag-Erling Smørgrav log_assert(z->in_use); 197*b7579f77SDag-Erling Smørgrav log_assert(z->count > 0); 198*b7579f77SDag-Erling Smørgrav z->in_use = 0; 199*b7579f77SDag-Erling Smørgrav 200*b7579f77SDag-Erling Smørgrav /* go up the tree and reduce counts */ 201*b7579f77SDag-Erling Smørgrav p = z; 202*b7579f77SDag-Erling Smørgrav while(p) { 203*b7579f77SDag-Erling Smørgrav log_assert(p->count > 0); 204*b7579f77SDag-Erling Smørgrav p->count --; 205*b7579f77SDag-Erling Smørgrav p = p->parent; 206*b7579f77SDag-Erling Smørgrav } 207*b7579f77SDag-Erling Smørgrav 208*b7579f77SDag-Erling Smørgrav /* remove zones with zero count */ 209*b7579f77SDag-Erling Smørgrav p = z; 210*b7579f77SDag-Erling Smørgrav while(p && p->count == 0) { 211*b7579f77SDag-Erling Smørgrav np = p->parent; 212*b7579f77SDag-Erling Smørgrav (void)rbtree_delete(&neg->tree, &p->node); 213*b7579f77SDag-Erling Smørgrav neg->use -= p->len + sizeof(*p); 214*b7579f77SDag-Erling Smørgrav free(p->nsec3_salt); 215*b7579f77SDag-Erling Smørgrav free(p->name); 216*b7579f77SDag-Erling Smørgrav free(p); 217*b7579f77SDag-Erling Smørgrav p = np; 218*b7579f77SDag-Erling Smørgrav } 219*b7579f77SDag-Erling Smørgrav } 220*b7579f77SDag-Erling Smørgrav 221*b7579f77SDag-Erling Smørgrav void neg_delete_data(struct val_neg_cache* neg, struct val_neg_data* el) 222*b7579f77SDag-Erling Smørgrav { 223*b7579f77SDag-Erling Smørgrav struct val_neg_zone* z; 224*b7579f77SDag-Erling Smørgrav struct val_neg_data* p, *np; 225*b7579f77SDag-Erling Smørgrav if(!el) return; 226*b7579f77SDag-Erling Smørgrav z = el->zone; 227*b7579f77SDag-Erling Smørgrav log_assert(el->in_use); 228*b7579f77SDag-Erling Smørgrav log_assert(el->count > 0); 229*b7579f77SDag-Erling Smørgrav el->in_use = 0; 230*b7579f77SDag-Erling Smørgrav 231*b7579f77SDag-Erling Smørgrav /* remove it from the lru list */ 232*b7579f77SDag-Erling Smørgrav neg_lru_remove(neg, el); 233*b7579f77SDag-Erling Smørgrav 234*b7579f77SDag-Erling Smørgrav /* go up the tree and reduce counts */ 235*b7579f77SDag-Erling Smørgrav p = el; 236*b7579f77SDag-Erling Smørgrav while(p) { 237*b7579f77SDag-Erling Smørgrav log_assert(p->count > 0); 238*b7579f77SDag-Erling Smørgrav p->count --; 239*b7579f77SDag-Erling Smørgrav p = p->parent; 240*b7579f77SDag-Erling Smørgrav } 241*b7579f77SDag-Erling Smørgrav 242*b7579f77SDag-Erling Smørgrav /* delete 0 count items from tree */ 243*b7579f77SDag-Erling Smørgrav p = el; 244*b7579f77SDag-Erling Smørgrav while(p && p->count == 0) { 245*b7579f77SDag-Erling Smørgrav np = p->parent; 246*b7579f77SDag-Erling Smørgrav (void)rbtree_delete(&z->tree, &p->node); 247*b7579f77SDag-Erling Smørgrav neg->use -= p->len + sizeof(*p); 248*b7579f77SDag-Erling Smørgrav free(p->name); 249*b7579f77SDag-Erling Smørgrav free(p); 250*b7579f77SDag-Erling Smørgrav p = np; 251*b7579f77SDag-Erling Smørgrav } 252*b7579f77SDag-Erling Smørgrav 253*b7579f77SDag-Erling Smørgrav /* check if the zone is now unused */ 254*b7579f77SDag-Erling Smørgrav if(z->tree.count == 0) { 255*b7579f77SDag-Erling Smørgrav neg_delete_zone(neg, z); 256*b7579f77SDag-Erling Smørgrav } 257*b7579f77SDag-Erling Smørgrav } 258*b7579f77SDag-Erling Smørgrav 259*b7579f77SDag-Erling Smørgrav /** 260*b7579f77SDag-Erling Smørgrav * Create more space in negative cache 261*b7579f77SDag-Erling Smørgrav * The oldest elements are deleted until enough space is present. 262*b7579f77SDag-Erling Smørgrav * Empty zones are deleted. 263*b7579f77SDag-Erling Smørgrav * @param neg: negative cache. 264*b7579f77SDag-Erling Smørgrav * @param need: how many bytes are needed. 265*b7579f77SDag-Erling Smørgrav */ 266*b7579f77SDag-Erling Smørgrav static void neg_make_space(struct val_neg_cache* neg, size_t need) 267*b7579f77SDag-Erling Smørgrav { 268*b7579f77SDag-Erling Smørgrav /* delete elements until enough space or its empty */ 269*b7579f77SDag-Erling Smørgrav while(neg->last && neg->max < neg->use + need) { 270*b7579f77SDag-Erling Smørgrav neg_delete_data(neg, neg->last); 271*b7579f77SDag-Erling Smørgrav } 272*b7579f77SDag-Erling Smørgrav } 273*b7579f77SDag-Erling Smørgrav 274*b7579f77SDag-Erling Smørgrav struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg, 275*b7579f77SDag-Erling Smørgrav uint8_t* nm, size_t len, uint16_t dclass) 276*b7579f77SDag-Erling Smørgrav { 277*b7579f77SDag-Erling Smørgrav struct val_neg_zone lookfor; 278*b7579f77SDag-Erling Smørgrav struct val_neg_zone* result; 279*b7579f77SDag-Erling Smørgrav lookfor.node.key = &lookfor; 280*b7579f77SDag-Erling Smørgrav lookfor.name = nm; 281*b7579f77SDag-Erling Smørgrav lookfor.len = len; 282*b7579f77SDag-Erling Smørgrav lookfor.labs = dname_count_labels(lookfor.name); 283*b7579f77SDag-Erling Smørgrav lookfor.dclass = dclass; 284*b7579f77SDag-Erling Smørgrav 285*b7579f77SDag-Erling Smørgrav result = (struct val_neg_zone*) 286*b7579f77SDag-Erling Smørgrav rbtree_search(&neg->tree, lookfor.node.key); 287*b7579f77SDag-Erling Smørgrav return result; 288*b7579f77SDag-Erling Smørgrav } 289*b7579f77SDag-Erling Smørgrav 290*b7579f77SDag-Erling Smørgrav /** 291*b7579f77SDag-Erling Smørgrav * Find the given data 292*b7579f77SDag-Erling Smørgrav * @param zone: negative zone 293*b7579f77SDag-Erling Smørgrav * @param nm: what to look for. 294*b7579f77SDag-Erling Smørgrav * @param len: length of nm 295*b7579f77SDag-Erling Smørgrav * @param labs: labels in nm 296*b7579f77SDag-Erling Smørgrav * @return data or NULL if not found. 297*b7579f77SDag-Erling Smørgrav */ 298*b7579f77SDag-Erling Smørgrav static struct val_neg_data* neg_find_data(struct val_neg_zone* zone, 299*b7579f77SDag-Erling Smørgrav uint8_t* nm, size_t len, int labs) 300*b7579f77SDag-Erling Smørgrav { 301*b7579f77SDag-Erling Smørgrav struct val_neg_data lookfor; 302*b7579f77SDag-Erling Smørgrav struct val_neg_data* result; 303*b7579f77SDag-Erling Smørgrav lookfor.node.key = &lookfor; 304*b7579f77SDag-Erling Smørgrav lookfor.name = nm; 305*b7579f77SDag-Erling Smørgrav lookfor.len = len; 306*b7579f77SDag-Erling Smørgrav lookfor.labs = labs; 307*b7579f77SDag-Erling Smørgrav 308*b7579f77SDag-Erling Smørgrav result = (struct val_neg_data*) 309*b7579f77SDag-Erling Smørgrav rbtree_search(&zone->tree, lookfor.node.key); 310*b7579f77SDag-Erling Smørgrav return result; 311*b7579f77SDag-Erling Smørgrav } 312*b7579f77SDag-Erling Smørgrav 313*b7579f77SDag-Erling Smørgrav /** 314*b7579f77SDag-Erling Smørgrav * Calculate space needed for the data and all its parents 315*b7579f77SDag-Erling Smørgrav * @param rep: NSEC entries. 316*b7579f77SDag-Erling Smørgrav * @return size. 317*b7579f77SDag-Erling Smørgrav */ 318*b7579f77SDag-Erling Smørgrav static size_t calc_data_need(struct reply_info* rep) 319*b7579f77SDag-Erling Smørgrav { 320*b7579f77SDag-Erling Smørgrav uint8_t* d; 321*b7579f77SDag-Erling Smørgrav size_t i, len, res = 0; 322*b7579f77SDag-Erling Smørgrav 323*b7579f77SDag-Erling Smørgrav for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { 324*b7579f77SDag-Erling Smørgrav if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) { 325*b7579f77SDag-Erling Smørgrav d = rep->rrsets[i]->rk.dname; 326*b7579f77SDag-Erling Smørgrav len = rep->rrsets[i]->rk.dname_len; 327*b7579f77SDag-Erling Smørgrav res = sizeof(struct val_neg_data) + len; 328*b7579f77SDag-Erling Smørgrav while(!dname_is_root(d)) { 329*b7579f77SDag-Erling Smørgrav log_assert(len > 1); /* not root label */ 330*b7579f77SDag-Erling Smørgrav dname_remove_label(&d, &len); 331*b7579f77SDag-Erling Smørgrav res += sizeof(struct val_neg_data) + len; 332*b7579f77SDag-Erling Smørgrav } 333*b7579f77SDag-Erling Smørgrav } 334*b7579f77SDag-Erling Smørgrav } 335*b7579f77SDag-Erling Smørgrav return res; 336*b7579f77SDag-Erling Smørgrav } 337*b7579f77SDag-Erling Smørgrav 338*b7579f77SDag-Erling Smørgrav /** 339*b7579f77SDag-Erling Smørgrav * Calculate space needed for zone and all its parents 340*b7579f77SDag-Erling Smørgrav * @param d: name of zone 341*b7579f77SDag-Erling Smørgrav * @param len: length of name 342*b7579f77SDag-Erling Smørgrav * @return size. 343*b7579f77SDag-Erling Smørgrav */ 344*b7579f77SDag-Erling Smørgrav static size_t calc_zone_need(uint8_t* d, size_t len) 345*b7579f77SDag-Erling Smørgrav { 346*b7579f77SDag-Erling Smørgrav size_t res = sizeof(struct val_neg_zone) + len; 347*b7579f77SDag-Erling Smørgrav while(!dname_is_root(d)) { 348*b7579f77SDag-Erling Smørgrav log_assert(len > 1); /* not root label */ 349*b7579f77SDag-Erling Smørgrav dname_remove_label(&d, &len); 350*b7579f77SDag-Erling Smørgrav res += sizeof(struct val_neg_zone) + len; 351*b7579f77SDag-Erling Smørgrav } 352*b7579f77SDag-Erling Smørgrav return res; 353*b7579f77SDag-Erling Smørgrav } 354*b7579f77SDag-Erling Smørgrav 355*b7579f77SDag-Erling Smørgrav /** 356*b7579f77SDag-Erling Smørgrav * Find closest existing parent zone of the given name. 357*b7579f77SDag-Erling Smørgrav * @param neg: negative cache. 358*b7579f77SDag-Erling Smørgrav * @param nm: name to look for 359*b7579f77SDag-Erling Smørgrav * @param nm_len: length of nm 360*b7579f77SDag-Erling Smørgrav * @param labs: labelcount of nm. 361*b7579f77SDag-Erling Smørgrav * @param qclass: class. 362*b7579f77SDag-Erling Smørgrav * @return the zone or NULL if none found. 363*b7579f77SDag-Erling Smørgrav */ 364*b7579f77SDag-Erling Smørgrav static struct val_neg_zone* neg_closest_zone_parent(struct val_neg_cache* neg, 365*b7579f77SDag-Erling Smørgrav uint8_t* nm, size_t nm_len, int labs, uint16_t qclass) 366*b7579f77SDag-Erling Smørgrav { 367*b7579f77SDag-Erling Smørgrav struct val_neg_zone key; 368*b7579f77SDag-Erling Smørgrav struct val_neg_zone* result; 369*b7579f77SDag-Erling Smørgrav rbnode_t* res = NULL; 370*b7579f77SDag-Erling Smørgrav key.node.key = &key; 371*b7579f77SDag-Erling Smørgrav key.name = nm; 372*b7579f77SDag-Erling Smørgrav key.len = nm_len; 373*b7579f77SDag-Erling Smørgrav key.labs = labs; 374*b7579f77SDag-Erling Smørgrav key.dclass = qclass; 375*b7579f77SDag-Erling Smørgrav if(rbtree_find_less_equal(&neg->tree, &key, &res)) { 376*b7579f77SDag-Erling Smørgrav /* exact match */ 377*b7579f77SDag-Erling Smørgrav result = (struct val_neg_zone*)res; 378*b7579f77SDag-Erling Smørgrav } else { 379*b7579f77SDag-Erling Smørgrav /* smaller element (or no element) */ 380*b7579f77SDag-Erling Smørgrav int m; 381*b7579f77SDag-Erling Smørgrav result = (struct val_neg_zone*)res; 382*b7579f77SDag-Erling Smørgrav if(!result || result->dclass != qclass) 383*b7579f77SDag-Erling Smørgrav return NULL; 384*b7579f77SDag-Erling Smørgrav /* count number of labels matched */ 385*b7579f77SDag-Erling Smørgrav (void)dname_lab_cmp(result->name, result->labs, key.name, 386*b7579f77SDag-Erling Smørgrav key.labs, &m); 387*b7579f77SDag-Erling Smørgrav while(result) { /* go up until qname is subdomain of stub */ 388*b7579f77SDag-Erling Smørgrav if(result->labs <= m) 389*b7579f77SDag-Erling Smørgrav break; 390*b7579f77SDag-Erling Smørgrav result = result->parent; 391*b7579f77SDag-Erling Smørgrav } 392*b7579f77SDag-Erling Smørgrav } 393*b7579f77SDag-Erling Smørgrav return result; 394*b7579f77SDag-Erling Smørgrav } 395*b7579f77SDag-Erling Smørgrav 396*b7579f77SDag-Erling Smørgrav /** 397*b7579f77SDag-Erling Smørgrav * Find closest existing parent data for the given name. 398*b7579f77SDag-Erling Smørgrav * @param zone: to look in. 399*b7579f77SDag-Erling Smørgrav * @param nm: name to look for 400*b7579f77SDag-Erling Smørgrav * @param nm_len: length of nm 401*b7579f77SDag-Erling Smørgrav * @param labs: labelcount of nm. 402*b7579f77SDag-Erling Smørgrav * @return the data or NULL if none found. 403*b7579f77SDag-Erling Smørgrav */ 404*b7579f77SDag-Erling Smørgrav static struct val_neg_data* neg_closest_data_parent( 405*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone, uint8_t* nm, size_t nm_len, int labs) 406*b7579f77SDag-Erling Smørgrav { 407*b7579f77SDag-Erling Smørgrav struct val_neg_data key; 408*b7579f77SDag-Erling Smørgrav struct val_neg_data* result; 409*b7579f77SDag-Erling Smørgrav rbnode_t* res = NULL; 410*b7579f77SDag-Erling Smørgrav key.node.key = &key; 411*b7579f77SDag-Erling Smørgrav key.name = nm; 412*b7579f77SDag-Erling Smørgrav key.len = nm_len; 413*b7579f77SDag-Erling Smørgrav key.labs = labs; 414*b7579f77SDag-Erling Smørgrav if(rbtree_find_less_equal(&zone->tree, &key, &res)) { 415*b7579f77SDag-Erling Smørgrav /* exact match */ 416*b7579f77SDag-Erling Smørgrav result = (struct val_neg_data*)res; 417*b7579f77SDag-Erling Smørgrav } else { 418*b7579f77SDag-Erling Smørgrav /* smaller element (or no element) */ 419*b7579f77SDag-Erling Smørgrav int m; 420*b7579f77SDag-Erling Smørgrav result = (struct val_neg_data*)res; 421*b7579f77SDag-Erling Smørgrav if(!result) 422*b7579f77SDag-Erling Smørgrav return NULL; 423*b7579f77SDag-Erling Smørgrav /* count number of labels matched */ 424*b7579f77SDag-Erling Smørgrav (void)dname_lab_cmp(result->name, result->labs, key.name, 425*b7579f77SDag-Erling Smørgrav key.labs, &m); 426*b7579f77SDag-Erling Smørgrav while(result) { /* go up until qname is subdomain of stub */ 427*b7579f77SDag-Erling Smørgrav if(result->labs <= m) 428*b7579f77SDag-Erling Smørgrav break; 429*b7579f77SDag-Erling Smørgrav result = result->parent; 430*b7579f77SDag-Erling Smørgrav } 431*b7579f77SDag-Erling Smørgrav } 432*b7579f77SDag-Erling Smørgrav return result; 433*b7579f77SDag-Erling Smørgrav } 434*b7579f77SDag-Erling Smørgrav 435*b7579f77SDag-Erling Smørgrav /** 436*b7579f77SDag-Erling Smørgrav * Create a single zone node 437*b7579f77SDag-Erling Smørgrav * @param nm: name for zone (copied) 438*b7579f77SDag-Erling Smørgrav * @param nm_len: length of name 439*b7579f77SDag-Erling Smørgrav * @param labs: labels in name. 440*b7579f77SDag-Erling Smørgrav * @param dclass: class of zone, host order. 441*b7579f77SDag-Erling Smørgrav * @return new zone or NULL on failure 442*b7579f77SDag-Erling Smørgrav */ 443*b7579f77SDag-Erling Smørgrav static struct val_neg_zone* neg_setup_zone_node( 444*b7579f77SDag-Erling Smørgrav uint8_t* nm, size_t nm_len, int labs, uint16_t dclass) 445*b7579f77SDag-Erling Smørgrav { 446*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone = 447*b7579f77SDag-Erling Smørgrav (struct val_neg_zone*)calloc(1, sizeof(*zone)); 448*b7579f77SDag-Erling Smørgrav if(!zone) { 449*b7579f77SDag-Erling Smørgrav return NULL; 450*b7579f77SDag-Erling Smørgrav } 451*b7579f77SDag-Erling Smørgrav zone->node.key = zone; 452*b7579f77SDag-Erling Smørgrav zone->name = memdup(nm, nm_len); 453*b7579f77SDag-Erling Smørgrav if(!zone->name) { 454*b7579f77SDag-Erling Smørgrav free(zone); 455*b7579f77SDag-Erling Smørgrav return NULL; 456*b7579f77SDag-Erling Smørgrav } 457*b7579f77SDag-Erling Smørgrav zone->len = nm_len; 458*b7579f77SDag-Erling Smørgrav zone->labs = labs; 459*b7579f77SDag-Erling Smørgrav zone->dclass = dclass; 460*b7579f77SDag-Erling Smørgrav 461*b7579f77SDag-Erling Smørgrav rbtree_init(&zone->tree, &val_neg_data_compare); 462*b7579f77SDag-Erling Smørgrav return zone; 463*b7579f77SDag-Erling Smørgrav } 464*b7579f77SDag-Erling Smørgrav 465*b7579f77SDag-Erling Smørgrav /** 466*b7579f77SDag-Erling Smørgrav * Create a linked list of parent zones, starting at longname ending on 467*b7579f77SDag-Erling Smørgrav * the parent (can be NULL, creates to the root). 468*b7579f77SDag-Erling Smørgrav * @param nm: name for lowest in chain 469*b7579f77SDag-Erling Smørgrav * @param nm_len: length of name 470*b7579f77SDag-Erling Smørgrav * @param labs: labels in name. 471*b7579f77SDag-Erling Smørgrav * @param dclass: class of zone. 472*b7579f77SDag-Erling Smørgrav * @param parent: NULL for to root, else so it fits under here. 473*b7579f77SDag-Erling Smørgrav * @return zone; a chain of zones and their parents up to the parent. 474*b7579f77SDag-Erling Smørgrav * or NULL on malloc failure 475*b7579f77SDag-Erling Smørgrav */ 476*b7579f77SDag-Erling Smørgrav static struct val_neg_zone* neg_zone_chain( 477*b7579f77SDag-Erling Smørgrav uint8_t* nm, size_t nm_len, int labs, uint16_t dclass, 478*b7579f77SDag-Erling Smørgrav struct val_neg_zone* parent) 479*b7579f77SDag-Erling Smørgrav { 480*b7579f77SDag-Erling Smørgrav int i; 481*b7579f77SDag-Erling Smørgrav int tolabs = parent?parent->labs:0; 482*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone, *prev = NULL, *first = NULL; 483*b7579f77SDag-Erling Smørgrav 484*b7579f77SDag-Erling Smørgrav /* create the new subtree, i is labelcount of current creation */ 485*b7579f77SDag-Erling Smørgrav /* this creates a 'first' to z->parent=NULL list of zones */ 486*b7579f77SDag-Erling Smørgrav for(i=labs; i!=tolabs; i--) { 487*b7579f77SDag-Erling Smørgrav /* create new item */ 488*b7579f77SDag-Erling Smørgrav zone = neg_setup_zone_node(nm, nm_len, i, dclass); 489*b7579f77SDag-Erling Smørgrav if(!zone) { 490*b7579f77SDag-Erling Smørgrav /* need to delete other allocations in this routine!*/ 491*b7579f77SDag-Erling Smørgrav struct val_neg_zone* p=first, *np; 492*b7579f77SDag-Erling Smørgrav while(p) { 493*b7579f77SDag-Erling Smørgrav np = p->parent; 494*b7579f77SDag-Erling Smørgrav free(p); 495*b7579f77SDag-Erling Smørgrav free(p->name); 496*b7579f77SDag-Erling Smørgrav p = np; 497*b7579f77SDag-Erling Smørgrav } 498*b7579f77SDag-Erling Smørgrav return NULL; 499*b7579f77SDag-Erling Smørgrav } 500*b7579f77SDag-Erling Smørgrav if(i == labs) { 501*b7579f77SDag-Erling Smørgrav first = zone; 502*b7579f77SDag-Erling Smørgrav } else { 503*b7579f77SDag-Erling Smørgrav prev->parent = zone; 504*b7579f77SDag-Erling Smørgrav } 505*b7579f77SDag-Erling Smørgrav /* prepare for next name */ 506*b7579f77SDag-Erling Smørgrav prev = zone; 507*b7579f77SDag-Erling Smørgrav dname_remove_label(&nm, &nm_len); 508*b7579f77SDag-Erling Smørgrav } 509*b7579f77SDag-Erling Smørgrav return first; 510*b7579f77SDag-Erling Smørgrav } 511*b7579f77SDag-Erling Smørgrav 512*b7579f77SDag-Erling Smørgrav void val_neg_zone_take_inuse(struct val_neg_zone* zone) 513*b7579f77SDag-Erling Smørgrav { 514*b7579f77SDag-Erling Smørgrav if(!zone->in_use) { 515*b7579f77SDag-Erling Smørgrav struct val_neg_zone* p; 516*b7579f77SDag-Erling Smørgrav zone->in_use = 1; 517*b7579f77SDag-Erling Smørgrav /* increase usage count of all parents */ 518*b7579f77SDag-Erling Smørgrav for(p=zone; p; p = p->parent) { 519*b7579f77SDag-Erling Smørgrav p->count++; 520*b7579f77SDag-Erling Smørgrav } 521*b7579f77SDag-Erling Smørgrav } 522*b7579f77SDag-Erling Smørgrav } 523*b7579f77SDag-Erling Smørgrav 524*b7579f77SDag-Erling Smørgrav struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg, 525*b7579f77SDag-Erling Smørgrav uint8_t* nm, size_t nm_len, uint16_t dclass) 526*b7579f77SDag-Erling Smørgrav { 527*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone; 528*b7579f77SDag-Erling Smørgrav struct val_neg_zone* parent; 529*b7579f77SDag-Erling Smørgrav struct val_neg_zone* p, *np; 530*b7579f77SDag-Erling Smørgrav int labs = dname_count_labels(nm); 531*b7579f77SDag-Erling Smørgrav 532*b7579f77SDag-Erling Smørgrav /* find closest enclosing parent zone that (still) exists */ 533*b7579f77SDag-Erling Smørgrav parent = neg_closest_zone_parent(neg, nm, nm_len, labs, dclass); 534*b7579f77SDag-Erling Smørgrav if(parent && query_dname_compare(parent->name, nm) == 0) 535*b7579f77SDag-Erling Smørgrav return parent; /* already exists, weird */ 536*b7579f77SDag-Erling Smørgrav /* if parent exists, it is in use */ 537*b7579f77SDag-Erling Smørgrav log_assert(!parent || parent->count > 0); 538*b7579f77SDag-Erling Smørgrav zone = neg_zone_chain(nm, nm_len, labs, dclass, parent); 539*b7579f77SDag-Erling Smørgrav if(!zone) { 540*b7579f77SDag-Erling Smørgrav return NULL; 541*b7579f77SDag-Erling Smørgrav } 542*b7579f77SDag-Erling Smørgrav 543*b7579f77SDag-Erling Smørgrav /* insert the list of zones into the tree */ 544*b7579f77SDag-Erling Smørgrav p = zone; 545*b7579f77SDag-Erling Smørgrav while(p) { 546*b7579f77SDag-Erling Smørgrav np = p->parent; 547*b7579f77SDag-Erling Smørgrav /* mem use */ 548*b7579f77SDag-Erling Smørgrav neg->use += sizeof(struct val_neg_zone) + p->len; 549*b7579f77SDag-Erling Smørgrav /* insert in tree */ 550*b7579f77SDag-Erling Smørgrav (void)rbtree_insert(&neg->tree, &p->node); 551*b7579f77SDag-Erling Smørgrav /* last one needs proper parent pointer */ 552*b7579f77SDag-Erling Smørgrav if(np == NULL) 553*b7579f77SDag-Erling Smørgrav p->parent = parent; 554*b7579f77SDag-Erling Smørgrav p = np; 555*b7579f77SDag-Erling Smørgrav } 556*b7579f77SDag-Erling Smørgrav return zone; 557*b7579f77SDag-Erling Smørgrav } 558*b7579f77SDag-Erling Smørgrav 559*b7579f77SDag-Erling Smørgrav /** find zone name of message, returns the SOA record */ 560*b7579f77SDag-Erling Smørgrav static struct ub_packed_rrset_key* reply_find_soa(struct reply_info* rep) 561*b7579f77SDag-Erling Smørgrav { 562*b7579f77SDag-Erling Smørgrav size_t i; 563*b7579f77SDag-Erling Smørgrav for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ 564*b7579f77SDag-Erling Smørgrav if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA) 565*b7579f77SDag-Erling Smørgrav return rep->rrsets[i]; 566*b7579f77SDag-Erling Smørgrav } 567*b7579f77SDag-Erling Smørgrav return NULL; 568*b7579f77SDag-Erling Smørgrav } 569*b7579f77SDag-Erling Smørgrav 570*b7579f77SDag-Erling Smørgrav /** see if the reply has NSEC records worthy of caching */ 571*b7579f77SDag-Erling Smørgrav static int reply_has_nsec(struct reply_info* rep) 572*b7579f77SDag-Erling Smørgrav { 573*b7579f77SDag-Erling Smørgrav size_t i; 574*b7579f77SDag-Erling Smørgrav struct packed_rrset_data* d; 575*b7579f77SDag-Erling Smørgrav if(rep->security != sec_status_secure) 576*b7579f77SDag-Erling Smørgrav return 0; 577*b7579f77SDag-Erling Smørgrav for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ 578*b7579f77SDag-Erling Smørgrav if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) { 579*b7579f77SDag-Erling Smørgrav d = (struct packed_rrset_data*)rep->rrsets[i]-> 580*b7579f77SDag-Erling Smørgrav entry.data; 581*b7579f77SDag-Erling Smørgrav if(d->security == sec_status_secure) 582*b7579f77SDag-Erling Smørgrav return 1; 583*b7579f77SDag-Erling Smørgrav } 584*b7579f77SDag-Erling Smørgrav } 585*b7579f77SDag-Erling Smørgrav return 0; 586*b7579f77SDag-Erling Smørgrav } 587*b7579f77SDag-Erling Smørgrav 588*b7579f77SDag-Erling Smørgrav 589*b7579f77SDag-Erling Smørgrav /** 590*b7579f77SDag-Erling Smørgrav * Create single node of data element. 591*b7579f77SDag-Erling Smørgrav * @param nm: name (copied) 592*b7579f77SDag-Erling Smørgrav * @param nm_len: length of name 593*b7579f77SDag-Erling Smørgrav * @param labs: labels in name. 594*b7579f77SDag-Erling Smørgrav * @return element with name nm, or NULL malloc failure. 595*b7579f77SDag-Erling Smørgrav */ 596*b7579f77SDag-Erling Smørgrav static struct val_neg_data* neg_setup_data_node( 597*b7579f77SDag-Erling Smørgrav uint8_t* nm, size_t nm_len, int labs) 598*b7579f77SDag-Erling Smørgrav { 599*b7579f77SDag-Erling Smørgrav struct val_neg_data* el; 600*b7579f77SDag-Erling Smørgrav el = (struct val_neg_data*)calloc(1, sizeof(*el)); 601*b7579f77SDag-Erling Smørgrav if(!el) { 602*b7579f77SDag-Erling Smørgrav return NULL; 603*b7579f77SDag-Erling Smørgrav } 604*b7579f77SDag-Erling Smørgrav el->node.key = el; 605*b7579f77SDag-Erling Smørgrav el->name = memdup(nm, nm_len); 606*b7579f77SDag-Erling Smørgrav if(!el->name) { 607*b7579f77SDag-Erling Smørgrav free(el); 608*b7579f77SDag-Erling Smørgrav return NULL; 609*b7579f77SDag-Erling Smørgrav } 610*b7579f77SDag-Erling Smørgrav el->len = nm_len; 611*b7579f77SDag-Erling Smørgrav el->labs = labs; 612*b7579f77SDag-Erling Smørgrav return el; 613*b7579f77SDag-Erling Smørgrav } 614*b7579f77SDag-Erling Smørgrav 615*b7579f77SDag-Erling Smørgrav /** 616*b7579f77SDag-Erling Smørgrav * Create chain of data element and parents 617*b7579f77SDag-Erling Smørgrav * @param nm: name 618*b7579f77SDag-Erling Smørgrav * @param nm_len: length of name 619*b7579f77SDag-Erling Smørgrav * @param labs: labels in name. 620*b7579f77SDag-Erling Smørgrav * @param parent: up to where to make, if NULL up to root label. 621*b7579f77SDag-Erling Smørgrav * @return lowest element with name nm, or NULL malloc failure. 622*b7579f77SDag-Erling Smørgrav */ 623*b7579f77SDag-Erling Smørgrav static struct val_neg_data* neg_data_chain( 624*b7579f77SDag-Erling Smørgrav uint8_t* nm, size_t nm_len, int labs, struct val_neg_data* parent) 625*b7579f77SDag-Erling Smørgrav { 626*b7579f77SDag-Erling Smørgrav int i; 627*b7579f77SDag-Erling Smørgrav int tolabs = parent?parent->labs:0; 628*b7579f77SDag-Erling Smørgrav struct val_neg_data* el, *first = NULL, *prev = NULL; 629*b7579f77SDag-Erling Smørgrav 630*b7579f77SDag-Erling Smørgrav /* create the new subtree, i is labelcount of current creation */ 631*b7579f77SDag-Erling Smørgrav /* this creates a 'first' to z->parent=NULL list of zones */ 632*b7579f77SDag-Erling Smørgrav for(i=labs; i!=tolabs; i--) { 633*b7579f77SDag-Erling Smørgrav /* create new item */ 634*b7579f77SDag-Erling Smørgrav el = neg_setup_data_node(nm, nm_len, i); 635*b7579f77SDag-Erling Smørgrav if(!el) { 636*b7579f77SDag-Erling Smørgrav /* need to delete other allocations in this routine!*/ 637*b7579f77SDag-Erling Smørgrav struct val_neg_data* p = first, *np; 638*b7579f77SDag-Erling Smørgrav while(p) { 639*b7579f77SDag-Erling Smørgrav np = p->parent; 640*b7579f77SDag-Erling Smørgrav free(p); 641*b7579f77SDag-Erling Smørgrav free(p->name); 642*b7579f77SDag-Erling Smørgrav p = np; 643*b7579f77SDag-Erling Smørgrav } 644*b7579f77SDag-Erling Smørgrav return NULL; 645*b7579f77SDag-Erling Smørgrav } 646*b7579f77SDag-Erling Smørgrav if(i == labs) { 647*b7579f77SDag-Erling Smørgrav first = el; 648*b7579f77SDag-Erling Smørgrav } else { 649*b7579f77SDag-Erling Smørgrav prev->parent = el; 650*b7579f77SDag-Erling Smørgrav } 651*b7579f77SDag-Erling Smørgrav 652*b7579f77SDag-Erling Smørgrav /* prepare for next name */ 653*b7579f77SDag-Erling Smørgrav prev = el; 654*b7579f77SDag-Erling Smørgrav dname_remove_label(&nm, &nm_len); 655*b7579f77SDag-Erling Smørgrav } 656*b7579f77SDag-Erling Smørgrav return first; 657*b7579f77SDag-Erling Smørgrav } 658*b7579f77SDag-Erling Smørgrav 659*b7579f77SDag-Erling Smørgrav /** 660*b7579f77SDag-Erling Smørgrav * Remove NSEC records between start and end points. 661*b7579f77SDag-Erling Smørgrav * By walking the tree, the tree is sorted canonically. 662*b7579f77SDag-Erling Smørgrav * @param neg: negative cache. 663*b7579f77SDag-Erling Smørgrav * @param zone: the zone 664*b7579f77SDag-Erling Smørgrav * @param el: element to start walking at. 665*b7579f77SDag-Erling Smørgrav * @param nsec: the nsec record with the end point 666*b7579f77SDag-Erling Smørgrav */ 667*b7579f77SDag-Erling Smørgrav static void wipeout(struct val_neg_cache* neg, struct val_neg_zone* zone, 668*b7579f77SDag-Erling Smørgrav struct val_neg_data* el, struct ub_packed_rrset_key* nsec) 669*b7579f77SDag-Erling Smørgrav { 670*b7579f77SDag-Erling Smørgrav struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> 671*b7579f77SDag-Erling Smørgrav entry.data; 672*b7579f77SDag-Erling Smørgrav uint8_t* end; 673*b7579f77SDag-Erling Smørgrav size_t end_len; 674*b7579f77SDag-Erling Smørgrav int end_labs, m; 675*b7579f77SDag-Erling Smørgrav rbnode_t* walk, *next; 676*b7579f77SDag-Erling Smørgrav struct val_neg_data* cur; 677*b7579f77SDag-Erling Smørgrav uint8_t buf[257]; 678*b7579f77SDag-Erling Smørgrav /* get endpoint */ 679*b7579f77SDag-Erling Smørgrav if(!d || d->count == 0 || d->rr_len[0] < 2+1) 680*b7579f77SDag-Erling Smørgrav return; 681*b7579f77SDag-Erling Smørgrav if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC) { 682*b7579f77SDag-Erling Smørgrav end = d->rr_data[0]+2; 683*b7579f77SDag-Erling Smørgrav end_len = dname_valid(end, d->rr_len[0]-2); 684*b7579f77SDag-Erling Smørgrav end_labs = dname_count_labels(end); 685*b7579f77SDag-Erling Smørgrav } else { 686*b7579f77SDag-Erling Smørgrav /* NSEC3 */ 687*b7579f77SDag-Erling Smørgrav if(!nsec3_get_nextowner_b32(nsec, 0, buf, sizeof(buf))) 688*b7579f77SDag-Erling Smørgrav return; 689*b7579f77SDag-Erling Smørgrav end = buf; 690*b7579f77SDag-Erling Smørgrav end_labs = dname_count_size_labels(end, &end_len); 691*b7579f77SDag-Erling Smørgrav } 692*b7579f77SDag-Erling Smørgrav 693*b7579f77SDag-Erling Smørgrav /* sanity check, both owner and end must be below the zone apex */ 694*b7579f77SDag-Erling Smørgrav if(!dname_subdomain_c(el->name, zone->name) || 695*b7579f77SDag-Erling Smørgrav !dname_subdomain_c(end, zone->name)) 696*b7579f77SDag-Erling Smørgrav return; 697*b7579f77SDag-Erling Smørgrav 698*b7579f77SDag-Erling Smørgrav /* detect end of zone NSEC ; wipe until the end of zone */ 699*b7579f77SDag-Erling Smørgrav if(query_dname_compare(end, zone->name) == 0) { 700*b7579f77SDag-Erling Smørgrav end = NULL; 701*b7579f77SDag-Erling Smørgrav } 702*b7579f77SDag-Erling Smørgrav 703*b7579f77SDag-Erling Smørgrav walk = rbtree_next(&el->node); 704*b7579f77SDag-Erling Smørgrav while(walk && walk != RBTREE_NULL) { 705*b7579f77SDag-Erling Smørgrav cur = (struct val_neg_data*)walk; 706*b7579f77SDag-Erling Smørgrav /* sanity check: must be larger than start */ 707*b7579f77SDag-Erling Smørgrav if(dname_canon_lab_cmp(cur->name, cur->labs, 708*b7579f77SDag-Erling Smørgrav el->name, el->labs, &m) <= 0) { 709*b7579f77SDag-Erling Smørgrav /* r == 0 skip original record. */ 710*b7579f77SDag-Erling Smørgrav /* r < 0 too small! */ 711*b7579f77SDag-Erling Smørgrav walk = rbtree_next(walk); 712*b7579f77SDag-Erling Smørgrav continue; 713*b7579f77SDag-Erling Smørgrav } 714*b7579f77SDag-Erling Smørgrav /* stop at endpoint, also data at empty nonterminals must be 715*b7579f77SDag-Erling Smørgrav * removed (no NSECs there) so everything between 716*b7579f77SDag-Erling Smørgrav * start and end */ 717*b7579f77SDag-Erling Smørgrav if(end && dname_canon_lab_cmp(cur->name, cur->labs, 718*b7579f77SDag-Erling Smørgrav end, end_labs, &m) >= 0) { 719*b7579f77SDag-Erling Smørgrav break; 720*b7579f77SDag-Erling Smørgrav } 721*b7579f77SDag-Erling Smørgrav /* this element has to be deleted, but we cannot do it 722*b7579f77SDag-Erling Smørgrav * now, because we are walking the tree still ... */ 723*b7579f77SDag-Erling Smørgrav /* get the next element: */ 724*b7579f77SDag-Erling Smørgrav next = rbtree_next(walk); 725*b7579f77SDag-Erling Smørgrav /* now delete the original element, this may trigger 726*b7579f77SDag-Erling Smørgrav * rbtree rebalances, but really, the next element is 727*b7579f77SDag-Erling Smørgrav * the one we need. 728*b7579f77SDag-Erling Smørgrav * But it may trigger delete of other data and the 729*b7579f77SDag-Erling Smørgrav * entire zone. However, if that happens, this is done 730*b7579f77SDag-Erling Smørgrav * by deleting the *parents* of the element for deletion, 731*b7579f77SDag-Erling Smørgrav * and maybe also the entire zone if it is empty. 732*b7579f77SDag-Erling Smørgrav * But parents are smaller in canonical compare, thus, 733*b7579f77SDag-Erling Smørgrav * if a larger element exists, then it is not a parent, 734*b7579f77SDag-Erling Smørgrav * it cannot get deleted, the zone cannot get empty. 735*b7579f77SDag-Erling Smørgrav * If the next==NULL, then zone can be empty. */ 736*b7579f77SDag-Erling Smørgrav if(cur->in_use) 737*b7579f77SDag-Erling Smørgrav neg_delete_data(neg, cur); 738*b7579f77SDag-Erling Smørgrav walk = next; 739*b7579f77SDag-Erling Smørgrav } 740*b7579f77SDag-Erling Smørgrav } 741*b7579f77SDag-Erling Smørgrav 742*b7579f77SDag-Erling Smørgrav void neg_insert_data(struct val_neg_cache* neg, 743*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone, struct ub_packed_rrset_key* nsec) 744*b7579f77SDag-Erling Smørgrav { 745*b7579f77SDag-Erling Smørgrav struct packed_rrset_data* d; 746*b7579f77SDag-Erling Smørgrav struct val_neg_data* parent; 747*b7579f77SDag-Erling Smørgrav struct val_neg_data* el; 748*b7579f77SDag-Erling Smørgrav uint8_t* nm = nsec->rk.dname; 749*b7579f77SDag-Erling Smørgrav size_t nm_len = nsec->rk.dname_len; 750*b7579f77SDag-Erling Smørgrav int labs = dname_count_labels(nsec->rk.dname); 751*b7579f77SDag-Erling Smørgrav 752*b7579f77SDag-Erling Smørgrav d = (struct packed_rrset_data*)nsec->entry.data; 753*b7579f77SDag-Erling Smørgrav if( !(d->security == sec_status_secure || 754*b7579f77SDag-Erling Smørgrav (d->security == sec_status_unchecked && d->rrsig_count > 0))) 755*b7579f77SDag-Erling Smørgrav return; 756*b7579f77SDag-Erling Smørgrav log_nametypeclass(VERB_ALGO, "negcache rr", 757*b7579f77SDag-Erling Smørgrav nsec->rk.dname, ntohs(nsec->rk.type), 758*b7579f77SDag-Erling Smørgrav ntohs(nsec->rk.rrset_class)); 759*b7579f77SDag-Erling Smørgrav 760*b7579f77SDag-Erling Smørgrav /* find closest enclosing parent data that (still) exists */ 761*b7579f77SDag-Erling Smørgrav parent = neg_closest_data_parent(zone, nm, nm_len, labs); 762*b7579f77SDag-Erling Smørgrav if(parent && query_dname_compare(parent->name, nm) == 0) { 763*b7579f77SDag-Erling Smørgrav /* perfect match already exists */ 764*b7579f77SDag-Erling Smørgrav log_assert(parent->count > 0); 765*b7579f77SDag-Erling Smørgrav el = parent; 766*b7579f77SDag-Erling Smørgrav } else { 767*b7579f77SDag-Erling Smørgrav struct val_neg_data* p, *np; 768*b7579f77SDag-Erling Smørgrav 769*b7579f77SDag-Erling Smørgrav /* create subtree for perfect match */ 770*b7579f77SDag-Erling Smørgrav /* if parent exists, it is in use */ 771*b7579f77SDag-Erling Smørgrav log_assert(!parent || parent->count > 0); 772*b7579f77SDag-Erling Smørgrav 773*b7579f77SDag-Erling Smørgrav el = neg_data_chain(nm, nm_len, labs, parent); 774*b7579f77SDag-Erling Smørgrav if(!el) { 775*b7579f77SDag-Erling Smørgrav log_err("out of memory inserting NSEC negative cache"); 776*b7579f77SDag-Erling Smørgrav return; 777*b7579f77SDag-Erling Smørgrav } 778*b7579f77SDag-Erling Smørgrav el->in_use = 0; /* set on below */ 779*b7579f77SDag-Erling Smørgrav 780*b7579f77SDag-Erling Smørgrav /* insert the list of zones into the tree */ 781*b7579f77SDag-Erling Smørgrav p = el; 782*b7579f77SDag-Erling Smørgrav while(p) { 783*b7579f77SDag-Erling Smørgrav np = p->parent; 784*b7579f77SDag-Erling Smørgrav /* mem use */ 785*b7579f77SDag-Erling Smørgrav neg->use += sizeof(struct val_neg_data) + p->len; 786*b7579f77SDag-Erling Smørgrav /* insert in tree */ 787*b7579f77SDag-Erling Smørgrav p->zone = zone; 788*b7579f77SDag-Erling Smørgrav (void)rbtree_insert(&zone->tree, &p->node); 789*b7579f77SDag-Erling Smørgrav /* last one needs proper parent pointer */ 790*b7579f77SDag-Erling Smørgrav if(np == NULL) 791*b7579f77SDag-Erling Smørgrav p->parent = parent; 792*b7579f77SDag-Erling Smørgrav p = np; 793*b7579f77SDag-Erling Smørgrav } 794*b7579f77SDag-Erling Smørgrav } 795*b7579f77SDag-Erling Smørgrav 796*b7579f77SDag-Erling Smørgrav if(!el->in_use) { 797*b7579f77SDag-Erling Smørgrav struct val_neg_data* p; 798*b7579f77SDag-Erling Smørgrav 799*b7579f77SDag-Erling Smørgrav el->in_use = 1; 800*b7579f77SDag-Erling Smørgrav /* increase usage count of all parents */ 801*b7579f77SDag-Erling Smørgrav for(p=el; p; p = p->parent) { 802*b7579f77SDag-Erling Smørgrav p->count++; 803*b7579f77SDag-Erling Smørgrav } 804*b7579f77SDag-Erling Smørgrav 805*b7579f77SDag-Erling Smørgrav neg_lru_front(neg, el); 806*b7579f77SDag-Erling Smørgrav } else { 807*b7579f77SDag-Erling Smørgrav /* in use, bring to front, lru */ 808*b7579f77SDag-Erling Smørgrav neg_lru_touch(neg, el); 809*b7579f77SDag-Erling Smørgrav } 810*b7579f77SDag-Erling Smørgrav 811*b7579f77SDag-Erling Smørgrav /* if nsec3 store last used parameters */ 812*b7579f77SDag-Erling Smørgrav if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC3) { 813*b7579f77SDag-Erling Smørgrav int h; 814*b7579f77SDag-Erling Smørgrav uint8_t* s; 815*b7579f77SDag-Erling Smørgrav size_t slen, it; 816*b7579f77SDag-Erling Smørgrav if(nsec3_get_params(nsec, 0, &h, &it, &s, &slen) && 817*b7579f77SDag-Erling Smørgrav it <= neg->nsec3_max_iter && 818*b7579f77SDag-Erling Smørgrav (h != zone->nsec3_hash || it != zone->nsec3_iter || 819*b7579f77SDag-Erling Smørgrav slen != zone->nsec3_saltlen || 820*b7579f77SDag-Erling Smørgrav memcmp(zone->nsec3_salt, s, slen) != 0)) { 821*b7579f77SDag-Erling Smørgrav uint8_t* sa = memdup(s, slen); 822*b7579f77SDag-Erling Smørgrav if(sa) { 823*b7579f77SDag-Erling Smørgrav free(zone->nsec3_salt); 824*b7579f77SDag-Erling Smørgrav zone->nsec3_salt = sa; 825*b7579f77SDag-Erling Smørgrav zone->nsec3_saltlen = slen; 826*b7579f77SDag-Erling Smørgrav zone->nsec3_hash = h; 827*b7579f77SDag-Erling Smørgrav zone->nsec3_iter = it; 828*b7579f77SDag-Erling Smørgrav } 829*b7579f77SDag-Erling Smørgrav } 830*b7579f77SDag-Erling Smørgrav } 831*b7579f77SDag-Erling Smørgrav 832*b7579f77SDag-Erling Smørgrav /* wipe out the cache items between NSEC start and end */ 833*b7579f77SDag-Erling Smørgrav wipeout(neg, zone, el, nsec); 834*b7579f77SDag-Erling Smørgrav } 835*b7579f77SDag-Erling Smørgrav 836*b7579f77SDag-Erling Smørgrav void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep) 837*b7579f77SDag-Erling Smørgrav { 838*b7579f77SDag-Erling Smørgrav size_t i, need; 839*b7579f77SDag-Erling Smørgrav struct ub_packed_rrset_key* soa; 840*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone; 841*b7579f77SDag-Erling Smørgrav /* see if secure nsecs inside */ 842*b7579f77SDag-Erling Smørgrav if(!reply_has_nsec(rep)) 843*b7579f77SDag-Erling Smørgrav return; 844*b7579f77SDag-Erling Smørgrav /* find the zone name in message */ 845*b7579f77SDag-Erling Smørgrav soa = reply_find_soa(rep); 846*b7579f77SDag-Erling Smørgrav if(!soa) 847*b7579f77SDag-Erling Smørgrav return; 848*b7579f77SDag-Erling Smørgrav 849*b7579f77SDag-Erling Smørgrav log_nametypeclass(VERB_ALGO, "negcache insert for zone", 850*b7579f77SDag-Erling Smørgrav soa->rk.dname, LDNS_RR_TYPE_SOA, ntohs(soa->rk.rrset_class)); 851*b7579f77SDag-Erling Smørgrav 852*b7579f77SDag-Erling Smørgrav /* ask for enough space to store all of it */ 853*b7579f77SDag-Erling Smørgrav need = calc_data_need(rep) + 854*b7579f77SDag-Erling Smørgrav calc_zone_need(soa->rk.dname, soa->rk.dname_len); 855*b7579f77SDag-Erling Smørgrav lock_basic_lock(&neg->lock); 856*b7579f77SDag-Erling Smørgrav neg_make_space(neg, need); 857*b7579f77SDag-Erling Smørgrav 858*b7579f77SDag-Erling Smørgrav /* find or create the zone entry */ 859*b7579f77SDag-Erling Smørgrav zone = neg_find_zone(neg, soa->rk.dname, soa->rk.dname_len, 860*b7579f77SDag-Erling Smørgrav ntohs(soa->rk.rrset_class)); 861*b7579f77SDag-Erling Smørgrav if(!zone) { 862*b7579f77SDag-Erling Smørgrav if(!(zone = neg_create_zone(neg, soa->rk.dname, 863*b7579f77SDag-Erling Smørgrav soa->rk.dname_len, ntohs(soa->rk.rrset_class)))) { 864*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 865*b7579f77SDag-Erling Smørgrav log_err("out of memory adding negative zone"); 866*b7579f77SDag-Erling Smørgrav return; 867*b7579f77SDag-Erling Smørgrav } 868*b7579f77SDag-Erling Smørgrav } 869*b7579f77SDag-Erling Smørgrav val_neg_zone_take_inuse(zone); 870*b7579f77SDag-Erling Smørgrav 871*b7579f77SDag-Erling Smørgrav /* insert the NSECs */ 872*b7579f77SDag-Erling Smørgrav for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ 873*b7579f77SDag-Erling Smørgrav if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC) 874*b7579f77SDag-Erling Smørgrav continue; 875*b7579f77SDag-Erling Smørgrav if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, 876*b7579f77SDag-Erling Smørgrav zone->name)) continue; 877*b7579f77SDag-Erling Smørgrav /* insert NSEC into this zone's tree */ 878*b7579f77SDag-Erling Smørgrav neg_insert_data(neg, zone, rep->rrsets[i]); 879*b7579f77SDag-Erling Smørgrav } 880*b7579f77SDag-Erling Smørgrav if(zone->tree.count == 0) { 881*b7579f77SDag-Erling Smørgrav /* remove empty zone if inserts failed */ 882*b7579f77SDag-Erling Smørgrav neg_delete_zone(neg, zone); 883*b7579f77SDag-Erling Smørgrav } 884*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 885*b7579f77SDag-Erling Smørgrav } 886*b7579f77SDag-Erling Smørgrav 887*b7579f77SDag-Erling Smørgrav /** 888*b7579f77SDag-Erling Smørgrav * Lookup closest data record. For NSEC denial. 889*b7579f77SDag-Erling Smørgrav * @param zone: zone to look in 890*b7579f77SDag-Erling Smørgrav * @param qname: name to look for. 891*b7579f77SDag-Erling Smørgrav * @param len: length of name 892*b7579f77SDag-Erling Smørgrav * @param labs: labels in name 893*b7579f77SDag-Erling Smørgrav * @param data: data element, exact or smaller or NULL 894*b7579f77SDag-Erling Smørgrav * @return true if exact match. 895*b7579f77SDag-Erling Smørgrav */ 896*b7579f77SDag-Erling Smørgrav static int neg_closest_data(struct val_neg_zone* zone, 897*b7579f77SDag-Erling Smørgrav uint8_t* qname, size_t len, int labs, struct val_neg_data** data) 898*b7579f77SDag-Erling Smørgrav { 899*b7579f77SDag-Erling Smørgrav struct val_neg_data key; 900*b7579f77SDag-Erling Smørgrav rbnode_t* r; 901*b7579f77SDag-Erling Smørgrav key.node.key = &key; 902*b7579f77SDag-Erling Smørgrav key.name = qname; 903*b7579f77SDag-Erling Smørgrav key.len = len; 904*b7579f77SDag-Erling Smørgrav key.labs = labs; 905*b7579f77SDag-Erling Smørgrav if(rbtree_find_less_equal(&zone->tree, &key, &r)) { 906*b7579f77SDag-Erling Smørgrav /* exact match */ 907*b7579f77SDag-Erling Smørgrav *data = (struct val_neg_data*)r; 908*b7579f77SDag-Erling Smørgrav return 1; 909*b7579f77SDag-Erling Smørgrav } else { 910*b7579f77SDag-Erling Smørgrav /* smaller match */ 911*b7579f77SDag-Erling Smørgrav *data = (struct val_neg_data*)r; 912*b7579f77SDag-Erling Smørgrav return 0; 913*b7579f77SDag-Erling Smørgrav } 914*b7579f77SDag-Erling Smørgrav } 915*b7579f77SDag-Erling Smørgrav 916*b7579f77SDag-Erling Smørgrav int val_neg_dlvlookup(struct val_neg_cache* neg, uint8_t* qname, size_t len, 917*b7579f77SDag-Erling Smørgrav uint16_t qclass, struct rrset_cache* rrset_cache, uint32_t now) 918*b7579f77SDag-Erling Smørgrav { 919*b7579f77SDag-Erling Smørgrav /* lookup closest zone */ 920*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone; 921*b7579f77SDag-Erling Smørgrav struct val_neg_data* data; 922*b7579f77SDag-Erling Smørgrav int labs; 923*b7579f77SDag-Erling Smørgrav struct ub_packed_rrset_key* nsec; 924*b7579f77SDag-Erling Smørgrav struct packed_rrset_data* d; 925*b7579f77SDag-Erling Smørgrav uint32_t flags; 926*b7579f77SDag-Erling Smørgrav uint8_t* wc; 927*b7579f77SDag-Erling Smørgrav struct query_info qinfo; 928*b7579f77SDag-Erling Smørgrav if(!neg) return 0; 929*b7579f77SDag-Erling Smørgrav 930*b7579f77SDag-Erling Smørgrav log_nametypeclass(VERB_ALGO, "negcache dlvlookup", qname, 931*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_DLV, qclass); 932*b7579f77SDag-Erling Smørgrav 933*b7579f77SDag-Erling Smørgrav labs = dname_count_labels(qname); 934*b7579f77SDag-Erling Smørgrav lock_basic_lock(&neg->lock); 935*b7579f77SDag-Erling Smørgrav zone = neg_closest_zone_parent(neg, qname, len, labs, qclass); 936*b7579f77SDag-Erling Smørgrav while(zone && !zone->in_use) 937*b7579f77SDag-Erling Smørgrav zone = zone->parent; 938*b7579f77SDag-Erling Smørgrav if(!zone) { 939*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 940*b7579f77SDag-Erling Smørgrav return 0; 941*b7579f77SDag-Erling Smørgrav } 942*b7579f77SDag-Erling Smørgrav log_nametypeclass(VERB_ALGO, "negcache zone", zone->name, 0, 943*b7579f77SDag-Erling Smørgrav zone->dclass); 944*b7579f77SDag-Erling Smørgrav 945*b7579f77SDag-Erling Smørgrav /* DLV is defined to use NSEC only */ 946*b7579f77SDag-Erling Smørgrav if(zone->nsec3_hash) { 947*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 948*b7579f77SDag-Erling Smørgrav return 0; 949*b7579f77SDag-Erling Smørgrav } 950*b7579f77SDag-Erling Smørgrav 951*b7579f77SDag-Erling Smørgrav /* lookup closest data record */ 952*b7579f77SDag-Erling Smørgrav (void)neg_closest_data(zone, qname, len, labs, &data); 953*b7579f77SDag-Erling Smørgrav while(data && !data->in_use) 954*b7579f77SDag-Erling Smørgrav data = data->parent; 955*b7579f77SDag-Erling Smørgrav if(!data) { 956*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 957*b7579f77SDag-Erling Smørgrav return 0; 958*b7579f77SDag-Erling Smørgrav } 959*b7579f77SDag-Erling Smørgrav log_nametypeclass(VERB_ALGO, "negcache rr", data->name, 960*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_NSEC, zone->dclass); 961*b7579f77SDag-Erling Smørgrav 962*b7579f77SDag-Erling Smørgrav /* lookup rrset in rrset cache */ 963*b7579f77SDag-Erling Smørgrav flags = 0; 964*b7579f77SDag-Erling Smørgrav if(query_dname_compare(data->name, zone->name) == 0) 965*b7579f77SDag-Erling Smørgrav flags = PACKED_RRSET_NSEC_AT_APEX; 966*b7579f77SDag-Erling Smørgrav nsec = rrset_cache_lookup(rrset_cache, data->name, data->len, 967*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_NSEC, zone->dclass, flags, now, 0); 968*b7579f77SDag-Erling Smørgrav 969*b7579f77SDag-Erling Smørgrav /* check if secure and TTL ok */ 970*b7579f77SDag-Erling Smørgrav if(!nsec) { 971*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 972*b7579f77SDag-Erling Smørgrav return 0; 973*b7579f77SDag-Erling Smørgrav } 974*b7579f77SDag-Erling Smørgrav d = (struct packed_rrset_data*)nsec->entry.data; 975*b7579f77SDag-Erling Smørgrav if(!d || now > d->ttl) { 976*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&nsec->entry.lock); 977*b7579f77SDag-Erling Smørgrav /* delete data record if expired */ 978*b7579f77SDag-Erling Smørgrav neg_delete_data(neg, data); 979*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 980*b7579f77SDag-Erling Smørgrav return 0; 981*b7579f77SDag-Erling Smørgrav } 982*b7579f77SDag-Erling Smørgrav if(d->security != sec_status_secure) { 983*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&nsec->entry.lock); 984*b7579f77SDag-Erling Smørgrav neg_delete_data(neg, data); 985*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 986*b7579f77SDag-Erling Smørgrav return 0; 987*b7579f77SDag-Erling Smørgrav } 988*b7579f77SDag-Erling Smørgrav verbose(VERB_ALGO, "negcache got secure rrset"); 989*b7579f77SDag-Erling Smørgrav 990*b7579f77SDag-Erling Smørgrav /* check NSEC security */ 991*b7579f77SDag-Erling Smørgrav /* check if NSEC proves no DLV type exists */ 992*b7579f77SDag-Erling Smørgrav /* check if NSEC proves NXDOMAIN for qname */ 993*b7579f77SDag-Erling Smørgrav qinfo.qname = qname; 994*b7579f77SDag-Erling Smørgrav qinfo.qtype = LDNS_RR_TYPE_DLV; 995*b7579f77SDag-Erling Smørgrav qinfo.qclass = qclass; 996*b7579f77SDag-Erling Smørgrav if(!nsec_proves_nodata(nsec, &qinfo, &wc) && 997*b7579f77SDag-Erling Smørgrav !val_nsec_proves_name_error(nsec, qname)) { 998*b7579f77SDag-Erling Smørgrav /* the NSEC is not a denial for the DLV */ 999*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&nsec->entry.lock); 1000*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 1001*b7579f77SDag-Erling Smørgrav verbose(VERB_ALGO, "negcache not proven"); 1002*b7579f77SDag-Erling Smørgrav return 0; 1003*b7579f77SDag-Erling Smørgrav } 1004*b7579f77SDag-Erling Smørgrav /* so the NSEC was a NODATA proof, or NXDOMAIN proof. */ 1005*b7579f77SDag-Erling Smørgrav 1006*b7579f77SDag-Erling Smørgrav /* no need to check for wildcard NSEC; no wildcards in DLV repos */ 1007*b7579f77SDag-Erling Smørgrav /* no need to lookup SOA record for client; no response message */ 1008*b7579f77SDag-Erling Smørgrav 1009*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&nsec->entry.lock); 1010*b7579f77SDag-Erling Smørgrav /* if OK touch the LRU for neg_data element */ 1011*b7579f77SDag-Erling Smørgrav neg_lru_touch(neg, data); 1012*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 1013*b7579f77SDag-Erling Smørgrav verbose(VERB_ALGO, "negcache DLV denial proven"); 1014*b7579f77SDag-Erling Smørgrav return 1; 1015*b7579f77SDag-Erling Smørgrav } 1016*b7579f77SDag-Erling Smørgrav 1017*b7579f77SDag-Erling Smørgrav /** see if the reply has signed NSEC records and return the signer */ 1018*b7579f77SDag-Erling Smørgrav static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len, 1019*b7579f77SDag-Erling Smørgrav uint16_t* dclass) 1020*b7579f77SDag-Erling Smørgrav { 1021*b7579f77SDag-Erling Smørgrav size_t i; 1022*b7579f77SDag-Erling Smørgrav struct packed_rrset_data* d; 1023*b7579f77SDag-Erling Smørgrav uint8_t* s; 1024*b7579f77SDag-Erling Smørgrav for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ 1025*b7579f77SDag-Erling Smørgrav if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC || 1026*b7579f77SDag-Erling Smørgrav ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC3) { 1027*b7579f77SDag-Erling Smørgrav d = (struct packed_rrset_data*)rep->rrsets[i]-> 1028*b7579f77SDag-Erling Smørgrav entry.data; 1029*b7579f77SDag-Erling Smørgrav /* return first signer name of first NSEC */ 1030*b7579f77SDag-Erling Smørgrav if(d->rrsig_count != 0) { 1031*b7579f77SDag-Erling Smørgrav val_find_rrset_signer(rep->rrsets[i], 1032*b7579f77SDag-Erling Smørgrav &s, signer_len); 1033*b7579f77SDag-Erling Smørgrav if(s && *signer_len) { 1034*b7579f77SDag-Erling Smørgrav *dclass = ntohs(rep->rrsets[i]-> 1035*b7579f77SDag-Erling Smørgrav rk.rrset_class); 1036*b7579f77SDag-Erling Smørgrav return s; 1037*b7579f77SDag-Erling Smørgrav } 1038*b7579f77SDag-Erling Smørgrav } 1039*b7579f77SDag-Erling Smørgrav } 1040*b7579f77SDag-Erling Smørgrav } 1041*b7579f77SDag-Erling Smørgrav return 0; 1042*b7579f77SDag-Erling Smørgrav } 1043*b7579f77SDag-Erling Smørgrav 1044*b7579f77SDag-Erling Smørgrav void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, 1045*b7579f77SDag-Erling Smørgrav uint8_t* zone_name) 1046*b7579f77SDag-Erling Smørgrav { 1047*b7579f77SDag-Erling Smørgrav size_t i, need; 1048*b7579f77SDag-Erling Smørgrav uint8_t* signer; 1049*b7579f77SDag-Erling Smørgrav size_t signer_len; 1050*b7579f77SDag-Erling Smørgrav uint16_t dclass; 1051*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone; 1052*b7579f77SDag-Erling Smørgrav /* no SOA in this message, find RRSIG over NSEC's signer name. 1053*b7579f77SDag-Erling Smørgrav * note the NSEC records are maybe not validated yet */ 1054*b7579f77SDag-Erling Smørgrav signer = reply_nsec_signer(rep, &signer_len, &dclass); 1055*b7579f77SDag-Erling Smørgrav if(!signer) 1056*b7579f77SDag-Erling Smørgrav return; 1057*b7579f77SDag-Erling Smørgrav if(!dname_subdomain_c(signer, zone_name)) { 1058*b7579f77SDag-Erling Smørgrav /* the signer is not in the bailiwick, throw it out */ 1059*b7579f77SDag-Erling Smørgrav return; 1060*b7579f77SDag-Erling Smørgrav } 1061*b7579f77SDag-Erling Smørgrav 1062*b7579f77SDag-Erling Smørgrav log_nametypeclass(VERB_ALGO, "negcache insert referral ", 1063*b7579f77SDag-Erling Smørgrav signer, LDNS_RR_TYPE_NS, dclass); 1064*b7579f77SDag-Erling Smørgrav 1065*b7579f77SDag-Erling Smørgrav /* ask for enough space to store all of it */ 1066*b7579f77SDag-Erling Smørgrav need = calc_data_need(rep) + calc_zone_need(signer, signer_len); 1067*b7579f77SDag-Erling Smørgrav lock_basic_lock(&neg->lock); 1068*b7579f77SDag-Erling Smørgrav neg_make_space(neg, need); 1069*b7579f77SDag-Erling Smørgrav 1070*b7579f77SDag-Erling Smørgrav /* find or create the zone entry */ 1071*b7579f77SDag-Erling Smørgrav zone = neg_find_zone(neg, signer, signer_len, dclass); 1072*b7579f77SDag-Erling Smørgrav if(!zone) { 1073*b7579f77SDag-Erling Smørgrav if(!(zone = neg_create_zone(neg, signer, signer_len, 1074*b7579f77SDag-Erling Smørgrav dclass))) { 1075*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 1076*b7579f77SDag-Erling Smørgrav log_err("out of memory adding negative zone"); 1077*b7579f77SDag-Erling Smørgrav return; 1078*b7579f77SDag-Erling Smørgrav } 1079*b7579f77SDag-Erling Smørgrav } 1080*b7579f77SDag-Erling Smørgrav val_neg_zone_take_inuse(zone); 1081*b7579f77SDag-Erling Smørgrav 1082*b7579f77SDag-Erling Smørgrav /* insert the NSECs */ 1083*b7579f77SDag-Erling Smørgrav for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ 1084*b7579f77SDag-Erling Smørgrav if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC && 1085*b7579f77SDag-Erling Smørgrav ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC3) 1086*b7579f77SDag-Erling Smørgrav continue; 1087*b7579f77SDag-Erling Smørgrav if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, 1088*b7579f77SDag-Erling Smørgrav zone->name)) continue; 1089*b7579f77SDag-Erling Smørgrav /* insert NSEC into this zone's tree */ 1090*b7579f77SDag-Erling Smørgrav neg_insert_data(neg, zone, rep->rrsets[i]); 1091*b7579f77SDag-Erling Smørgrav } 1092*b7579f77SDag-Erling Smørgrav if(zone->tree.count == 0) { 1093*b7579f77SDag-Erling Smørgrav /* remove empty zone if inserts failed */ 1094*b7579f77SDag-Erling Smørgrav neg_delete_zone(neg, zone); 1095*b7579f77SDag-Erling Smørgrav } 1096*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 1097*b7579f77SDag-Erling Smørgrav } 1098*b7579f77SDag-Erling Smørgrav 1099*b7579f77SDag-Erling Smørgrav /** 1100*b7579f77SDag-Erling Smørgrav * Check that an NSEC3 rrset does not have a type set. 1101*b7579f77SDag-Erling Smørgrav * None of the nsec3s in a hash-collision are allowed to have the type. 1102*b7579f77SDag-Erling Smørgrav * (since we do not know which one is the nsec3 looked at, flags, ..., we 1103*b7579f77SDag-Erling Smørgrav * ignore the cached item and let it bypass negative caching). 1104*b7579f77SDag-Erling Smørgrav * @param k: the nsec3 rrset to check. 1105*b7579f77SDag-Erling Smørgrav * @param t: type to check 1106*b7579f77SDag-Erling Smørgrav * @return true if no RRs have the type. 1107*b7579f77SDag-Erling Smørgrav */ 1108*b7579f77SDag-Erling Smørgrav static int nsec3_no_type(struct ub_packed_rrset_key* k, uint16_t t) 1109*b7579f77SDag-Erling Smørgrav { 1110*b7579f77SDag-Erling Smørgrav int count = (int)((struct packed_rrset_data*)k->entry.data)->count; 1111*b7579f77SDag-Erling Smørgrav int i; 1112*b7579f77SDag-Erling Smørgrav for(i=0; i<count; i++) 1113*b7579f77SDag-Erling Smørgrav if(nsec3_has_type(k, i, t)) 1114*b7579f77SDag-Erling Smørgrav return 0; 1115*b7579f77SDag-Erling Smørgrav return 1; 1116*b7579f77SDag-Erling Smørgrav } 1117*b7579f77SDag-Erling Smørgrav 1118*b7579f77SDag-Erling Smørgrav /** 1119*b7579f77SDag-Erling Smørgrav * See if rrset exists in rrset cache. 1120*b7579f77SDag-Erling Smørgrav * If it does, the bit is checked, and if not expired, it is returned 1121*b7579f77SDag-Erling Smørgrav * allocated in region. 1122*b7579f77SDag-Erling Smørgrav * @param rrset_cache: rrset cache 1123*b7579f77SDag-Erling Smørgrav * @param qname: to lookup rrset name 1124*b7579f77SDag-Erling Smørgrav * @param qname_len: length of qname. 1125*b7579f77SDag-Erling Smørgrav * @param qtype: type of rrset to lookup, host order 1126*b7579f77SDag-Erling Smørgrav * @param qclass: class of rrset to lookup, host order 1127*b7579f77SDag-Erling Smørgrav * @param flags: flags for rrset to lookup 1128*b7579f77SDag-Erling Smørgrav * @param region: where to alloc result 1129*b7579f77SDag-Erling Smørgrav * @param checkbit: if true, a bit in the nsec typemap is checked for absence. 1130*b7579f77SDag-Erling Smørgrav * @param checktype: which bit to check 1131*b7579f77SDag-Erling Smørgrav * @param now: to check ttl against 1132*b7579f77SDag-Erling Smørgrav * @return rrset or NULL 1133*b7579f77SDag-Erling Smørgrav */ 1134*b7579f77SDag-Erling Smørgrav static struct ub_packed_rrset_key* 1135*b7579f77SDag-Erling Smørgrav grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len, 1136*b7579f77SDag-Erling Smørgrav uint16_t qtype, uint16_t qclass, uint32_t flags, 1137*b7579f77SDag-Erling Smørgrav struct regional* region, int checkbit, uint16_t checktype, 1138*b7579f77SDag-Erling Smørgrav uint32_t now) 1139*b7579f77SDag-Erling Smørgrav { 1140*b7579f77SDag-Erling Smørgrav struct ub_packed_rrset_key* r, *k = rrset_cache_lookup(rrset_cache, 1141*b7579f77SDag-Erling Smørgrav qname, qname_len, qtype, qclass, flags, now, 0); 1142*b7579f77SDag-Erling Smørgrav struct packed_rrset_data* d; 1143*b7579f77SDag-Erling Smørgrav if(!k) return NULL; 1144*b7579f77SDag-Erling Smørgrav d = (struct packed_rrset_data*)k->entry.data; 1145*b7579f77SDag-Erling Smørgrav if(d->ttl < now) { 1146*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&k->entry.lock); 1147*b7579f77SDag-Erling Smørgrav return NULL; 1148*b7579f77SDag-Erling Smørgrav } 1149*b7579f77SDag-Erling Smørgrav /* only secure or unchecked records that have signatures. */ 1150*b7579f77SDag-Erling Smørgrav if( ! ( d->security == sec_status_secure || 1151*b7579f77SDag-Erling Smørgrav (d->security == sec_status_unchecked && 1152*b7579f77SDag-Erling Smørgrav d->rrsig_count > 0) ) ) { 1153*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&k->entry.lock); 1154*b7579f77SDag-Erling Smørgrav return NULL; 1155*b7579f77SDag-Erling Smørgrav } 1156*b7579f77SDag-Erling Smørgrav /* check if checktype is absent */ 1157*b7579f77SDag-Erling Smørgrav if(checkbit && ( 1158*b7579f77SDag-Erling Smørgrav (qtype == LDNS_RR_TYPE_NSEC && nsec_has_type(k, checktype)) || 1159*b7579f77SDag-Erling Smørgrav (qtype == LDNS_RR_TYPE_NSEC3 && !nsec3_no_type(k, checktype)) 1160*b7579f77SDag-Erling Smørgrav )) { 1161*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&k->entry.lock); 1162*b7579f77SDag-Erling Smørgrav return NULL; 1163*b7579f77SDag-Erling Smørgrav } 1164*b7579f77SDag-Erling Smørgrav /* looks OK! copy to region and return it */ 1165*b7579f77SDag-Erling Smørgrav r = packed_rrset_copy_region(k, region, now); 1166*b7579f77SDag-Erling Smørgrav /* if it failed, we return the NULL */ 1167*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&k->entry.lock); 1168*b7579f77SDag-Erling Smørgrav return r; 1169*b7579f77SDag-Erling Smørgrav } 1170*b7579f77SDag-Erling Smørgrav 1171*b7579f77SDag-Erling Smørgrav /** find nsec3 closest encloser in neg cache */ 1172*b7579f77SDag-Erling Smørgrav static struct val_neg_data* 1173*b7579f77SDag-Erling Smørgrav neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, 1174*b7579f77SDag-Erling Smørgrav int qlabs, ldns_buffer* buf, uint8_t* hashnc, size_t* nclen) 1175*b7579f77SDag-Erling Smørgrav { 1176*b7579f77SDag-Erling Smørgrav struct val_neg_data* data; 1177*b7579f77SDag-Erling Smørgrav uint8_t hashce[SHA_DIGEST_LENGTH]; 1178*b7579f77SDag-Erling Smørgrav uint8_t b32[257]; 1179*b7579f77SDag-Erling Smørgrav size_t celen, b32len; 1180*b7579f77SDag-Erling Smørgrav 1181*b7579f77SDag-Erling Smørgrav *nclen = 0; 1182*b7579f77SDag-Erling Smørgrav while(qlabs > 0) { 1183*b7579f77SDag-Erling Smørgrav /* hash */ 1184*b7579f77SDag-Erling Smørgrav if(!(celen=nsec3_get_hashed(buf, qname, qname_len, 1185*b7579f77SDag-Erling Smørgrav zone->nsec3_hash, zone->nsec3_iter, zone->nsec3_salt, 1186*b7579f77SDag-Erling Smørgrav zone->nsec3_saltlen, hashce, sizeof(hashce)))) 1187*b7579f77SDag-Erling Smørgrav return NULL; 1188*b7579f77SDag-Erling Smørgrav if(!(b32len=nsec3_hash_to_b32(hashce, celen, zone->name, 1189*b7579f77SDag-Erling Smørgrav zone->len, b32, sizeof(b32)))) 1190*b7579f77SDag-Erling Smørgrav return NULL; 1191*b7579f77SDag-Erling Smørgrav 1192*b7579f77SDag-Erling Smørgrav /* lookup (exact match only) */ 1193*b7579f77SDag-Erling Smørgrav data = neg_find_data(zone, b32, b32len, zone->labs+1); 1194*b7579f77SDag-Erling Smørgrav if(data && data->in_use) { 1195*b7579f77SDag-Erling Smørgrav /* found ce match! */ 1196*b7579f77SDag-Erling Smørgrav return data; 1197*b7579f77SDag-Erling Smørgrav } 1198*b7579f77SDag-Erling Smørgrav 1199*b7579f77SDag-Erling Smørgrav *nclen = celen; 1200*b7579f77SDag-Erling Smørgrav memmove(hashnc, hashce, celen); 1201*b7579f77SDag-Erling Smørgrav dname_remove_label(&qname, &qname_len); 1202*b7579f77SDag-Erling Smørgrav qlabs --; 1203*b7579f77SDag-Erling Smørgrav } 1204*b7579f77SDag-Erling Smørgrav return NULL; 1205*b7579f77SDag-Erling Smørgrav } 1206*b7579f77SDag-Erling Smørgrav 1207*b7579f77SDag-Erling Smørgrav /** check nsec3 parameters on nsec3 rrset with current zone values */ 1208*b7579f77SDag-Erling Smørgrav static int 1209*b7579f77SDag-Erling Smørgrav neg_params_ok(struct val_neg_zone* zone, struct ub_packed_rrset_key* rrset) 1210*b7579f77SDag-Erling Smørgrav { 1211*b7579f77SDag-Erling Smørgrav int h; 1212*b7579f77SDag-Erling Smørgrav uint8_t* s; 1213*b7579f77SDag-Erling Smørgrav size_t slen, it; 1214*b7579f77SDag-Erling Smørgrav if(!nsec3_get_params(rrset, 0, &h, &it, &s, &slen)) 1215*b7579f77SDag-Erling Smørgrav return 0; 1216*b7579f77SDag-Erling Smørgrav return (h == zone->nsec3_hash && it == zone->nsec3_iter && 1217*b7579f77SDag-Erling Smørgrav slen == zone->nsec3_saltlen && 1218*b7579f77SDag-Erling Smørgrav memcmp(zone->nsec3_salt, s, slen) == 0); 1219*b7579f77SDag-Erling Smørgrav } 1220*b7579f77SDag-Erling Smørgrav 1221*b7579f77SDag-Erling Smørgrav /** get next closer for nsec3 proof */ 1222*b7579f77SDag-Erling Smørgrav static struct ub_packed_rrset_key* 1223*b7579f77SDag-Erling Smørgrav neg_nsec3_getnc(struct val_neg_zone* zone, uint8_t* hashnc, size_t nclen, 1224*b7579f77SDag-Erling Smørgrav struct rrset_cache* rrset_cache, struct regional* region, 1225*b7579f77SDag-Erling Smørgrav uint32_t now, uint8_t* b32, size_t maxb32) 1226*b7579f77SDag-Erling Smørgrav { 1227*b7579f77SDag-Erling Smørgrav struct ub_packed_rrset_key* nc_rrset; 1228*b7579f77SDag-Erling Smørgrav struct val_neg_data* data; 1229*b7579f77SDag-Erling Smørgrav size_t b32len; 1230*b7579f77SDag-Erling Smørgrav 1231*b7579f77SDag-Erling Smørgrav if(!(b32len=nsec3_hash_to_b32(hashnc, nclen, zone->name, 1232*b7579f77SDag-Erling Smørgrav zone->len, b32, maxb32))) 1233*b7579f77SDag-Erling Smørgrav return NULL; 1234*b7579f77SDag-Erling Smørgrav (void)neg_closest_data(zone, b32, b32len, zone->labs+1, &data); 1235*b7579f77SDag-Erling Smørgrav if(!data && zone->tree.count != 0) { 1236*b7579f77SDag-Erling Smørgrav /* could be before the first entry ; return the last 1237*b7579f77SDag-Erling Smørgrav * entry (possibly the rollover nsec3 at end) */ 1238*b7579f77SDag-Erling Smørgrav data = (struct val_neg_data*)rbtree_last(&zone->tree); 1239*b7579f77SDag-Erling Smørgrav } 1240*b7579f77SDag-Erling Smørgrav while(data && !data->in_use) 1241*b7579f77SDag-Erling Smørgrav data = data->parent; 1242*b7579f77SDag-Erling Smørgrav if(!data) 1243*b7579f77SDag-Erling Smørgrav return NULL; 1244*b7579f77SDag-Erling Smørgrav /* got a data element in tree, grab it */ 1245*b7579f77SDag-Erling Smørgrav nc_rrset = grab_nsec(rrset_cache, data->name, data->len, 1246*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 0, 0, now); 1247*b7579f77SDag-Erling Smørgrav if(!nc_rrset) 1248*b7579f77SDag-Erling Smørgrav return NULL; 1249*b7579f77SDag-Erling Smørgrav if(!neg_params_ok(zone, nc_rrset)) 1250*b7579f77SDag-Erling Smørgrav return NULL; 1251*b7579f77SDag-Erling Smørgrav return nc_rrset; 1252*b7579f77SDag-Erling Smørgrav } 1253*b7579f77SDag-Erling Smørgrav 1254*b7579f77SDag-Erling Smørgrav /** neg cache nsec3 proof procedure*/ 1255*b7579f77SDag-Erling Smørgrav static struct dns_msg* 1256*b7579f77SDag-Erling Smørgrav neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, 1257*b7579f77SDag-Erling Smørgrav int qlabs, ldns_buffer* buf, struct rrset_cache* rrset_cache, 1258*b7579f77SDag-Erling Smørgrav struct regional* region, uint32_t now, uint8_t* topname) 1259*b7579f77SDag-Erling Smørgrav { 1260*b7579f77SDag-Erling Smørgrav struct dns_msg* msg; 1261*b7579f77SDag-Erling Smørgrav struct val_neg_data* data; 1262*b7579f77SDag-Erling Smørgrav uint8_t hashnc[SHA_DIGEST_LENGTH]; 1263*b7579f77SDag-Erling Smørgrav size_t nclen; 1264*b7579f77SDag-Erling Smørgrav struct ub_packed_rrset_key* ce_rrset, *nc_rrset; 1265*b7579f77SDag-Erling Smørgrav struct nsec3_cached_hash c; 1266*b7579f77SDag-Erling Smørgrav uint8_t nc_b32[257]; 1267*b7579f77SDag-Erling Smørgrav 1268*b7579f77SDag-Erling Smørgrav /* for NSEC3 ; determine the closest encloser for which we 1269*b7579f77SDag-Erling Smørgrav * can find an exact match. Remember the hashed lower name, 1270*b7579f77SDag-Erling Smørgrav * since that is the one we need a closest match for. 1271*b7579f77SDag-Erling Smørgrav * If we find a match straight away, then it becomes NODATA. 1272*b7579f77SDag-Erling Smørgrav * Otherwise, NXDOMAIN or if OPTOUT, an insecure delegation. 1273*b7579f77SDag-Erling Smørgrav * Also check that parameters are the same on closest encloser 1274*b7579f77SDag-Erling Smørgrav * and on closest match. 1275*b7579f77SDag-Erling Smørgrav */ 1276*b7579f77SDag-Erling Smørgrav if(!zone->nsec3_hash) 1277*b7579f77SDag-Erling Smørgrav return NULL; /* not nsec3 zone */ 1278*b7579f77SDag-Erling Smørgrav 1279*b7579f77SDag-Erling Smørgrav if(!(data=neg_find_nsec3_ce(zone, qname, qname_len, qlabs, buf, 1280*b7579f77SDag-Erling Smørgrav hashnc, &nclen))) { 1281*b7579f77SDag-Erling Smørgrav return NULL; 1282*b7579f77SDag-Erling Smørgrav } 1283*b7579f77SDag-Erling Smørgrav 1284*b7579f77SDag-Erling Smørgrav /* grab the ce rrset */ 1285*b7579f77SDag-Erling Smørgrav ce_rrset = grab_nsec(rrset_cache, data->name, data->len, 1286*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 1, 1287*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_DS, now); 1288*b7579f77SDag-Erling Smørgrav if(!ce_rrset) 1289*b7579f77SDag-Erling Smørgrav return NULL; 1290*b7579f77SDag-Erling Smørgrav if(!neg_params_ok(zone, ce_rrset)) 1291*b7579f77SDag-Erling Smørgrav return NULL; 1292*b7579f77SDag-Erling Smørgrav 1293*b7579f77SDag-Erling Smørgrav if(nclen == 0) { 1294*b7579f77SDag-Erling Smørgrav /* exact match, just check the type bits */ 1295*b7579f77SDag-Erling Smørgrav /* need: -SOA, -DS, +NS */ 1296*b7579f77SDag-Erling Smørgrav if(nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_SOA) || 1297*b7579f77SDag-Erling Smørgrav nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_DS) || 1298*b7579f77SDag-Erling Smørgrav !nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_NS)) 1299*b7579f77SDag-Erling Smørgrav return NULL; 1300*b7579f77SDag-Erling Smørgrav if(!(msg = dns_msg_create(qname, qname_len, 1301*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_DS, zone->dclass, region, 1))) 1302*b7579f77SDag-Erling Smørgrav return NULL; 1303*b7579f77SDag-Erling Smørgrav /* TTL reduced in grab_nsec */ 1304*b7579f77SDag-Erling Smørgrav if(!dns_msg_authadd(msg, region, ce_rrset, 0)) 1305*b7579f77SDag-Erling Smørgrav return NULL; 1306*b7579f77SDag-Erling Smørgrav return msg; 1307*b7579f77SDag-Erling Smørgrav } 1308*b7579f77SDag-Erling Smørgrav 1309*b7579f77SDag-Erling Smørgrav /* optout is not allowed without knowing the trust-anchor in use, 1310*b7579f77SDag-Erling Smørgrav * otherwise the optout could spoof away that anchor */ 1311*b7579f77SDag-Erling Smørgrav if(!topname) 1312*b7579f77SDag-Erling Smørgrav return NULL; 1313*b7579f77SDag-Erling Smørgrav 1314*b7579f77SDag-Erling Smørgrav /* if there is no exact match, it must be in an optout span 1315*b7579f77SDag-Erling Smørgrav * (an existing DS implies an NSEC3 must exist) */ 1316*b7579f77SDag-Erling Smørgrav nc_rrset = neg_nsec3_getnc(zone, hashnc, nclen, rrset_cache, 1317*b7579f77SDag-Erling Smørgrav region, now, nc_b32, sizeof(nc_b32)); 1318*b7579f77SDag-Erling Smørgrav if(!nc_rrset) 1319*b7579f77SDag-Erling Smørgrav return NULL; 1320*b7579f77SDag-Erling Smørgrav if(!neg_params_ok(zone, nc_rrset)) 1321*b7579f77SDag-Erling Smørgrav return NULL; 1322*b7579f77SDag-Erling Smørgrav if(!nsec3_has_optout(nc_rrset, 0)) 1323*b7579f77SDag-Erling Smørgrav return NULL; 1324*b7579f77SDag-Erling Smørgrav c.hash = hashnc; 1325*b7579f77SDag-Erling Smørgrav c.hash_len = nclen; 1326*b7579f77SDag-Erling Smørgrav c.b32 = nc_b32+1; 1327*b7579f77SDag-Erling Smørgrav c.b32_len = (size_t)nc_b32[0]; 1328*b7579f77SDag-Erling Smørgrav if(nsec3_covers(zone->name, &c, nc_rrset, 0, buf)) { 1329*b7579f77SDag-Erling Smørgrav /* nc_rrset covers the next closer name. 1330*b7579f77SDag-Erling Smørgrav * ce_rrset equals a closer encloser. 1331*b7579f77SDag-Erling Smørgrav * nc_rrset is optout. 1332*b7579f77SDag-Erling Smørgrav * No need to check wildcard for type DS */ 1333*b7579f77SDag-Erling Smørgrav /* capacity=3: ce + nc + soa(if needed) */ 1334*b7579f77SDag-Erling Smørgrav if(!(msg = dns_msg_create(qname, qname_len, 1335*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_DS, zone->dclass, region, 3))) 1336*b7579f77SDag-Erling Smørgrav return NULL; 1337*b7579f77SDag-Erling Smørgrav /* now=0 because TTL was reduced in grab_nsec */ 1338*b7579f77SDag-Erling Smørgrav if(!dns_msg_authadd(msg, region, ce_rrset, 0)) 1339*b7579f77SDag-Erling Smørgrav return NULL; 1340*b7579f77SDag-Erling Smørgrav if(!dns_msg_authadd(msg, region, nc_rrset, 0)) 1341*b7579f77SDag-Erling Smørgrav return NULL; 1342*b7579f77SDag-Erling Smørgrav return msg; 1343*b7579f77SDag-Erling Smørgrav } 1344*b7579f77SDag-Erling Smørgrav return NULL; 1345*b7579f77SDag-Erling Smørgrav } 1346*b7579f77SDag-Erling Smørgrav 1347*b7579f77SDag-Erling Smørgrav /** 1348*b7579f77SDag-Erling Smørgrav * Add SOA record for external responses. 1349*b7579f77SDag-Erling Smørgrav * @param rrset_cache: to look into. 1350*b7579f77SDag-Erling Smørgrav * @param now: current time. 1351*b7579f77SDag-Erling Smørgrav * @param region: where to perform the allocation 1352*b7579f77SDag-Erling Smørgrav * @param msg: current msg with NSEC. 1353*b7579f77SDag-Erling Smørgrav * @param zone: val_neg_zone if we have one. 1354*b7579f77SDag-Erling Smørgrav * @return false on lookup or alloc failure. 1355*b7579f77SDag-Erling Smørgrav */ 1356*b7579f77SDag-Erling Smørgrav static int add_soa(struct rrset_cache* rrset_cache, uint32_t now, 1357*b7579f77SDag-Erling Smørgrav struct regional* region, struct dns_msg* msg, struct val_neg_zone* zone) 1358*b7579f77SDag-Erling Smørgrav { 1359*b7579f77SDag-Erling Smørgrav struct ub_packed_rrset_key* soa; 1360*b7579f77SDag-Erling Smørgrav uint8_t* nm; 1361*b7579f77SDag-Erling Smørgrav size_t nmlen; 1362*b7579f77SDag-Erling Smørgrav uint16_t dclass; 1363*b7579f77SDag-Erling Smørgrav if(zone) { 1364*b7579f77SDag-Erling Smørgrav nm = zone->name; 1365*b7579f77SDag-Erling Smørgrav nmlen = zone->len; 1366*b7579f77SDag-Erling Smørgrav dclass = zone->dclass; 1367*b7579f77SDag-Erling Smørgrav } else { 1368*b7579f77SDag-Erling Smørgrav /* Assumes the signer is the zone SOA to add */ 1369*b7579f77SDag-Erling Smørgrav nm = reply_nsec_signer(msg->rep, &nmlen, &dclass); 1370*b7579f77SDag-Erling Smørgrav if(!nm) 1371*b7579f77SDag-Erling Smørgrav return 0; 1372*b7579f77SDag-Erling Smørgrav } 1373*b7579f77SDag-Erling Smørgrav soa = rrset_cache_lookup(rrset_cache, nm, nmlen, LDNS_RR_TYPE_SOA, 1374*b7579f77SDag-Erling Smørgrav dclass, PACKED_RRSET_SOA_NEG, now, 0); 1375*b7579f77SDag-Erling Smørgrav if(!soa) 1376*b7579f77SDag-Erling Smørgrav return 0; 1377*b7579f77SDag-Erling Smørgrav if(!dns_msg_authadd(msg, region, soa, now)) { 1378*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&soa->entry.lock); 1379*b7579f77SDag-Erling Smørgrav return 0; 1380*b7579f77SDag-Erling Smørgrav } 1381*b7579f77SDag-Erling Smørgrav lock_rw_unlock(&soa->entry.lock); 1382*b7579f77SDag-Erling Smørgrav return 1; 1383*b7579f77SDag-Erling Smørgrav } 1384*b7579f77SDag-Erling Smørgrav 1385*b7579f77SDag-Erling Smørgrav struct dns_msg* 1386*b7579f77SDag-Erling Smørgrav val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, 1387*b7579f77SDag-Erling Smørgrav struct regional* region, struct rrset_cache* rrset_cache, 1388*b7579f77SDag-Erling Smørgrav ldns_buffer* buf, uint32_t now, int addsoa, uint8_t* topname) 1389*b7579f77SDag-Erling Smørgrav { 1390*b7579f77SDag-Erling Smørgrav struct dns_msg* msg; 1391*b7579f77SDag-Erling Smørgrav struct ub_packed_rrset_key* rrset; 1392*b7579f77SDag-Erling Smørgrav uint8_t* zname; 1393*b7579f77SDag-Erling Smørgrav size_t zname_len; 1394*b7579f77SDag-Erling Smørgrav int zname_labs; 1395*b7579f77SDag-Erling Smørgrav struct val_neg_zone* zone; 1396*b7579f77SDag-Erling Smørgrav 1397*b7579f77SDag-Erling Smørgrav /* only for DS queries */ 1398*b7579f77SDag-Erling Smørgrav if(qinfo->qtype != LDNS_RR_TYPE_DS) 1399*b7579f77SDag-Erling Smørgrav return NULL; 1400*b7579f77SDag-Erling Smørgrav log_assert(!topname || dname_subdomain_c(qinfo->qname, topname)); 1401*b7579f77SDag-Erling Smørgrav 1402*b7579f77SDag-Erling Smørgrav /* see if info from neg cache is available 1403*b7579f77SDag-Erling Smørgrav * For NSECs, because there is no optout; a DS next to a delegation 1404*b7579f77SDag-Erling Smørgrav * always has exactly an NSEC for it itself; check its DS bit. 1405*b7579f77SDag-Erling Smørgrav * flags=0 (not the zone apex). 1406*b7579f77SDag-Erling Smørgrav */ 1407*b7579f77SDag-Erling Smørgrav rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len, 1408*b7579f77SDag-Erling Smørgrav LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1, 1409*b7579f77SDag-Erling Smørgrav qinfo->qtype, now); 1410*b7579f77SDag-Erling Smørgrav if(rrset) { 1411*b7579f77SDag-Erling Smørgrav /* return msg with that rrset */ 1412*b7579f77SDag-Erling Smørgrav if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, 1413*b7579f77SDag-Erling Smørgrav qinfo->qtype, qinfo->qclass, region, 2))) 1414*b7579f77SDag-Erling Smørgrav return NULL; 1415*b7579f77SDag-Erling Smørgrav /* TTL already subtracted in grab_nsec */ 1416*b7579f77SDag-Erling Smørgrav if(!dns_msg_authadd(msg, region, rrset, 0)) 1417*b7579f77SDag-Erling Smørgrav return NULL; 1418*b7579f77SDag-Erling Smørgrav if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) 1419*b7579f77SDag-Erling Smørgrav return NULL; 1420*b7579f77SDag-Erling Smørgrav return msg; 1421*b7579f77SDag-Erling Smørgrav } 1422*b7579f77SDag-Erling Smørgrav 1423*b7579f77SDag-Erling Smørgrav /* check NSEC3 neg cache for type DS */ 1424*b7579f77SDag-Erling Smørgrav /* need to look one zone higher for DS type */ 1425*b7579f77SDag-Erling Smørgrav zname = qinfo->qname; 1426*b7579f77SDag-Erling Smørgrav zname_len = qinfo->qname_len; 1427*b7579f77SDag-Erling Smørgrav dname_remove_label(&zname, &zname_len); 1428*b7579f77SDag-Erling Smørgrav zname_labs = dname_count_labels(zname); 1429*b7579f77SDag-Erling Smørgrav 1430*b7579f77SDag-Erling Smørgrav /* lookup closest zone */ 1431*b7579f77SDag-Erling Smørgrav lock_basic_lock(&neg->lock); 1432*b7579f77SDag-Erling Smørgrav zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs, 1433*b7579f77SDag-Erling Smørgrav qinfo->qclass); 1434*b7579f77SDag-Erling Smørgrav while(zone && !zone->in_use) 1435*b7579f77SDag-Erling Smørgrav zone = zone->parent; 1436*b7579f77SDag-Erling Smørgrav /* check that the zone is not too high up so that we do not pick data 1437*b7579f77SDag-Erling Smørgrav * out of a zone that is above the last-seen key (or trust-anchor). */ 1438*b7579f77SDag-Erling Smørgrav if(zone && topname) { 1439*b7579f77SDag-Erling Smørgrav if(!dname_subdomain_c(zone->name, topname)) 1440*b7579f77SDag-Erling Smørgrav zone = NULL; 1441*b7579f77SDag-Erling Smørgrav } 1442*b7579f77SDag-Erling Smørgrav if(!zone) { 1443*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 1444*b7579f77SDag-Erling Smørgrav return NULL; 1445*b7579f77SDag-Erling Smørgrav } 1446*b7579f77SDag-Erling Smørgrav 1447*b7579f77SDag-Erling Smørgrav msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len, 1448*b7579f77SDag-Erling Smørgrav zname_labs+1, buf, rrset_cache, region, now, topname); 1449*b7579f77SDag-Erling Smørgrav if(msg && addsoa && !add_soa(rrset_cache, now, region, msg, zone)) { 1450*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 1451*b7579f77SDag-Erling Smørgrav return NULL; 1452*b7579f77SDag-Erling Smørgrav } 1453*b7579f77SDag-Erling Smørgrav lock_basic_unlock(&neg->lock); 1454*b7579f77SDag-Erling Smørgrav return msg; 1455*b7579f77SDag-Erling Smørgrav } 1456