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