1238106Sdes/* 2238106Sdes * services/localzone.c - local zones authority service. 3238106Sdes * 4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved. 5238106Sdes * 6238106Sdes * This software is open source. 7238106Sdes * 8238106Sdes * Redistribution and use in source and binary forms, with or without 9238106Sdes * modification, are permitted provided that the following conditions 10238106Sdes * are met: 11238106Sdes * 12238106Sdes * Redistributions of source code must retain the above copyright notice, 13238106Sdes * this list of conditions and the following disclaimer. 14238106Sdes * 15238106Sdes * Redistributions in binary form must reproduce the above copyright notice, 16238106Sdes * this list of conditions and the following disclaimer in the documentation 17238106Sdes * and/or other materials provided with the distribution. 18238106Sdes * 19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may 20238106Sdes * be used to endorse or promote products derived from this software without 21238106Sdes * specific prior written permission. 22238106Sdes * 23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34238106Sdes */ 35238106Sdes 36238106Sdes/** 37238106Sdes * \file 38238106Sdes * 39238106Sdes * This file contains functions to enable local zone authority service. 40238106Sdes */ 41238106Sdes#include "config.h" 42238106Sdes#include "services/localzone.h" 43269257Sdes#include "ldns/str2wire.h" 44269257Sdes#include "ldns/sbuffer.h" 45238106Sdes#include "util/regional.h" 46238106Sdes#include "util/config_file.h" 47238106Sdes#include "util/data/dname.h" 48238106Sdes#include "util/data/packed_rrset.h" 49238106Sdes#include "util/data/msgencode.h" 50238106Sdes#include "util/net_help.h" 51238106Sdes#include "util/data/msgreply.h" 52238106Sdes#include "util/data/msgparse.h" 53238106Sdes 54238106Sdesstruct local_zones* 55238106Sdeslocal_zones_create(void) 56238106Sdes{ 57238106Sdes struct local_zones* zones = (struct local_zones*)calloc(1, 58238106Sdes sizeof(*zones)); 59238106Sdes if(!zones) 60238106Sdes return NULL; 61238106Sdes rbtree_init(&zones->ztree, &local_zone_cmp); 62269257Sdes lock_rw_init(&zones->lock); 63238106Sdes lock_protect(&zones->lock, &zones->ztree, sizeof(zones->ztree)); 64238106Sdes /* also lock protects the rbnode's in struct local_zone */ 65238106Sdes return zones; 66238106Sdes} 67238106Sdes 68238106Sdes/** helper traverse to delete zones */ 69238106Sdesstatic void 70238106Sdeslzdel(rbnode_t* n, void* ATTR_UNUSED(arg)) 71238106Sdes{ 72238106Sdes struct local_zone* z = (struct local_zone*)n->key; 73238106Sdes local_zone_delete(z); 74238106Sdes} 75238106Sdes 76238106Sdesvoid 77238106Sdeslocal_zones_delete(struct local_zones* zones) 78238106Sdes{ 79238106Sdes if(!zones) 80238106Sdes return; 81269257Sdes lock_rw_destroy(&zones->lock); 82238106Sdes /* walk through zones and delete them all */ 83238106Sdes traverse_postorder(&zones->ztree, lzdel, NULL); 84238106Sdes free(zones); 85238106Sdes} 86238106Sdes 87238106Sdesvoid 88238106Sdeslocal_zone_delete(struct local_zone* z) 89238106Sdes{ 90238106Sdes if(!z) 91238106Sdes return; 92238106Sdes lock_rw_destroy(&z->lock); 93238106Sdes regional_destroy(z->region); 94238106Sdes free(z->name); 95238106Sdes free(z); 96238106Sdes} 97238106Sdes 98238106Sdesint 99238106Sdeslocal_zone_cmp(const void* z1, const void* z2) 100238106Sdes{ 101238106Sdes /* first sort on class, so that hierarchy can be maintained within 102238106Sdes * a class */ 103238106Sdes struct local_zone* a = (struct local_zone*)z1; 104238106Sdes struct local_zone* b = (struct local_zone*)z2; 105238106Sdes int m; 106238106Sdes if(a->dclass != b->dclass) { 107238106Sdes if(a->dclass < b->dclass) 108238106Sdes return -1; 109238106Sdes return 1; 110238106Sdes } 111238106Sdes return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m); 112238106Sdes} 113238106Sdes 114238106Sdesint 115238106Sdeslocal_data_cmp(const void* d1, const void* d2) 116238106Sdes{ 117238106Sdes struct local_data* a = (struct local_data*)d1; 118238106Sdes struct local_data* b = (struct local_data*)d2; 119238106Sdes int m; 120238106Sdes return dname_canon_lab_cmp(a->name, a->namelabs, b->name, 121238106Sdes b->namelabs, &m); 122238106Sdes} 123238106Sdes 124238106Sdes/* form wireformat from text format domain name */ 125238106Sdesint 126238106Sdesparse_dname(const char* str, uint8_t** res, size_t* len, int* labs) 127238106Sdes{ 128269257Sdes *res = sldns_str2wire_dname(str, len); 129238106Sdes *labs = 0; 130269257Sdes if(!*res) { 131238106Sdes log_err("cannot parse name %s", str); 132238106Sdes return 0; 133238106Sdes } 134238106Sdes *labs = dname_count_size_labels(*res, len); 135238106Sdes return 1; 136238106Sdes} 137238106Sdes 138238106Sdes/** create a new localzone */ 139238106Sdesstatic struct local_zone* 140238106Sdeslocal_zone_create(uint8_t* nm, size_t len, int labs, 141238106Sdes enum localzone_type t, uint16_t dclass) 142238106Sdes{ 143238106Sdes struct local_zone* z = (struct local_zone*)calloc(1, sizeof(*z)); 144238106Sdes if(!z) { 145238106Sdes return NULL; 146238106Sdes } 147238106Sdes z->node.key = z; 148238106Sdes z->dclass = dclass; 149238106Sdes z->type = t; 150238106Sdes z->name = nm; 151238106Sdes z->namelen = len; 152238106Sdes z->namelabs = labs; 153238106Sdes lock_rw_init(&z->lock); 154238106Sdes z->region = regional_create(); 155238106Sdes if(!z->region) { 156238106Sdes free(z); 157238106Sdes return NULL; 158238106Sdes } 159238106Sdes rbtree_init(&z->data, &local_data_cmp); 160238106Sdes lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_t)); 161238106Sdes /* also the zones->lock protects node, parent, name*, class */ 162238106Sdes return z; 163238106Sdes} 164238106Sdes 165238106Sdes/** enter a new zone with allocated dname returns with WRlock */ 166238106Sdesstatic struct local_zone* 167238106Sdeslz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len, 168238106Sdes int labs, enum localzone_type t, uint16_t c) 169238106Sdes{ 170238106Sdes struct local_zone* z = local_zone_create(nm, len, labs, t, c); 171238106Sdes if(!z) { 172238106Sdes log_err("out of memory"); 173238106Sdes return NULL; 174238106Sdes } 175238106Sdes 176238106Sdes /* add to rbtree */ 177269257Sdes lock_rw_wrlock(&zones->lock); 178238106Sdes lock_rw_wrlock(&z->lock); 179238106Sdes if(!rbtree_insert(&zones->ztree, &z->node)) { 180238106Sdes log_warn("duplicate local-zone"); 181238106Sdes lock_rw_unlock(&z->lock); 182238106Sdes local_zone_delete(z); 183269257Sdes lock_rw_unlock(&zones->lock); 184238106Sdes return NULL; 185238106Sdes } 186269257Sdes lock_rw_unlock(&zones->lock); 187238106Sdes return z; 188238106Sdes} 189238106Sdes 190238106Sdes/** enter a new zone */ 191238106Sdesstatic struct local_zone* 192238106Sdeslz_enter_zone(struct local_zones* zones, const char* name, const char* type, 193238106Sdes uint16_t dclass) 194238106Sdes{ 195238106Sdes struct local_zone* z; 196238106Sdes enum localzone_type t; 197238106Sdes uint8_t* nm; 198238106Sdes size_t len; 199238106Sdes int labs; 200238106Sdes if(!parse_dname(name, &nm, &len, &labs)) { 201238106Sdes log_err("bad zone name %s %s", name, type); 202238106Sdes return NULL; 203238106Sdes } 204238106Sdes if(!local_zone_str2type(type, &t)) { 205238106Sdes log_err("bad lz_enter_zone type %s %s", name, type); 206238106Sdes free(nm); 207238106Sdes return NULL; 208238106Sdes } 209238106Sdes if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) { 210238106Sdes log_err("could not enter zone %s %s", name, type); 211238106Sdes return NULL; 212238106Sdes } 213238106Sdes return z; 214238106Sdes} 215238106Sdes 216238106Sdes/** return name and class and rdata of rr; parses string */ 217238106Sdesstatic int 218238106Sdesget_rr_content(const char* str, uint8_t** nm, uint16_t* type, 219269257Sdes uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len, 220269257Sdes uint8_t** rdata, size_t* rdata_len) 221238106Sdes{ 222269257Sdes size_t dname_len = 0; 223269257Sdes int e = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600, 224269257Sdes NULL, 0, NULL, 0); 225269257Sdes if(e) { 226269257Sdes log_err("error parsing local-data at %d: '%s': %s", 227269257Sdes LDNS_WIREPARSE_OFFSET(e), str, 228269257Sdes sldns_get_errorstr_parse(e)); 229238106Sdes return 0; 230238106Sdes } 231269257Sdes *nm = memdup(rr, dname_len); 232238106Sdes if(!*nm) { 233238106Sdes log_err("out of memory"); 234238106Sdes return 0; 235238106Sdes } 236269257Sdes *dclass = sldns_wirerr_get_class(rr, len, dname_len); 237269257Sdes *type = sldns_wirerr_get_type(rr, len, dname_len); 238269257Sdes *ttl = (time_t)sldns_wirerr_get_ttl(rr, len, dname_len); 239269257Sdes *rdata = sldns_wirerr_get_rdatawl(rr, len, dname_len); 240269257Sdes *rdata_len = sldns_wirerr_get_rdatalen(rr, len, dname_len)+2; 241238106Sdes return 1; 242238106Sdes} 243238106Sdes 244238106Sdes/** return name and class of rr; parses string */ 245238106Sdesstatic int 246238106Sdesget_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass) 247238106Sdes{ 248269257Sdes uint8_t rr[LDNS_RR_BUF_SIZE]; 249269257Sdes size_t len = sizeof(rr), dname_len = 0; 250269257Sdes int s = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600, 251269257Sdes NULL, 0, NULL, 0); 252269257Sdes if(s != 0) { 253269257Sdes log_err("error parsing local-data at %d '%s': %s", 254269257Sdes LDNS_WIREPARSE_OFFSET(s), str, 255269257Sdes sldns_get_errorstr_parse(s)); 256238106Sdes return 0; 257238106Sdes } 258269257Sdes *nm = memdup(rr, dname_len); 259269257Sdes *dclass = sldns_wirerr_get_class(rr, len, dname_len); 260238106Sdes if(!*nm) { 261238106Sdes log_err("out of memory"); 262238106Sdes return 0; 263238106Sdes } 264238106Sdes return 1; 265238106Sdes} 266238106Sdes 267238106Sdes/** 268238106Sdes * Find an rrset in local data structure. 269238106Sdes * @param data: local data domain name structure. 270238106Sdes * @param type: type to look for (host order). 271238106Sdes * @return rrset pointer or NULL if not found. 272238106Sdes */ 273238106Sdesstatic struct local_rrset* 274238106Sdeslocal_data_find_type(struct local_data* data, uint16_t type) 275238106Sdes{ 276238106Sdes struct local_rrset* p; 277238106Sdes type = htons(type); 278238106Sdes for(p = data->rrsets; p; p = p->next) { 279238106Sdes if(p->rrset->rk.type == type) 280238106Sdes return p; 281238106Sdes } 282238106Sdes return NULL; 283238106Sdes} 284238106Sdes 285238106Sdes/** check for RR duplicates */ 286238106Sdesstatic int 287269257Sdesrr_is_duplicate(struct packed_rrset_data* pd, uint8_t* rdata, size_t rdata_len) 288238106Sdes{ 289238106Sdes size_t i; 290238106Sdes for(i=0; i<pd->count; i++) { 291269257Sdes if(pd->rr_len[i] == rdata_len && 292269257Sdes memcmp(pd->rr_data[i], rdata, rdata_len) == 0) 293238106Sdes return 1; 294238106Sdes } 295238106Sdes return 0; 296238106Sdes} 297238106Sdes 298238106Sdes/** new local_rrset */ 299238106Sdesstatic struct local_rrset* 300238106Sdesnew_local_rrset(struct regional* region, struct local_data* node, 301238106Sdes uint16_t rrtype, uint16_t rrclass) 302238106Sdes{ 303238106Sdes struct packed_rrset_data* pd; 304238106Sdes struct local_rrset* rrset = (struct local_rrset*) 305238106Sdes regional_alloc_zero(region, sizeof(*rrset)); 306238106Sdes if(!rrset) { 307238106Sdes log_err("out of memory"); 308238106Sdes return NULL; 309238106Sdes } 310238106Sdes rrset->next = node->rrsets; 311238106Sdes node->rrsets = rrset; 312238106Sdes rrset->rrset = (struct ub_packed_rrset_key*) 313238106Sdes regional_alloc_zero(region, sizeof(*rrset->rrset)); 314238106Sdes if(!rrset->rrset) { 315238106Sdes log_err("out of memory"); 316238106Sdes return NULL; 317238106Sdes } 318238106Sdes rrset->rrset->entry.key = rrset->rrset; 319238106Sdes pd = (struct packed_rrset_data*)regional_alloc_zero(region, 320238106Sdes sizeof(*pd)); 321238106Sdes if(!pd) { 322238106Sdes log_err("out of memory"); 323238106Sdes return NULL; 324238106Sdes } 325238106Sdes pd->trust = rrset_trust_prim_noglue; 326238106Sdes pd->security = sec_status_insecure; 327238106Sdes rrset->rrset->entry.data = pd; 328238106Sdes rrset->rrset->rk.dname = node->name; 329238106Sdes rrset->rrset->rk.dname_len = node->namelen; 330238106Sdes rrset->rrset->rk.type = htons(rrtype); 331238106Sdes rrset->rrset->rk.rrset_class = htons(rrclass); 332238106Sdes return rrset; 333238106Sdes} 334238106Sdes 335238106Sdes/** insert RR into RRset data structure; Wastes a couple of bytes */ 336238106Sdesstatic int 337238106Sdesinsert_rr(struct regional* region, struct packed_rrset_data* pd, 338269257Sdes uint8_t* rdata, size_t rdata_len, time_t ttl) 339238106Sdes{ 340238106Sdes size_t* oldlen = pd->rr_len; 341269257Sdes time_t* oldttl = pd->rr_ttl; 342238106Sdes uint8_t** olddata = pd->rr_data; 343238106Sdes 344238106Sdes /* add RR to rrset */ 345238106Sdes pd->count++; 346238106Sdes pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count); 347238106Sdes pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count); 348238106Sdes pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count); 349238106Sdes if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) { 350238106Sdes log_err("out of memory"); 351238106Sdes return 0; 352238106Sdes } 353238106Sdes if(pd->count > 1) { 354238106Sdes memcpy(pd->rr_len+1, oldlen, 355238106Sdes sizeof(*pd->rr_len)*(pd->count-1)); 356238106Sdes memcpy(pd->rr_ttl+1, oldttl, 357238106Sdes sizeof(*pd->rr_ttl)*(pd->count-1)); 358238106Sdes memcpy(pd->rr_data+1, olddata, 359238106Sdes sizeof(*pd->rr_data)*(pd->count-1)); 360238106Sdes } 361269257Sdes pd->rr_len[0] = rdata_len; 362238106Sdes pd->rr_ttl[0] = ttl; 363269257Sdes pd->rr_data[0] = regional_alloc_init(region, rdata, rdata_len); 364238106Sdes if(!pd->rr_data[0]) { 365238106Sdes log_err("out of memory"); 366238106Sdes return 0; 367238106Sdes } 368238106Sdes return 1; 369238106Sdes} 370238106Sdes 371238106Sdes/** find a data node by exact name */ 372238106Sdesstatic struct local_data* 373238106Sdeslz_find_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs) 374238106Sdes{ 375238106Sdes struct local_data key; 376238106Sdes key.node.key = &key; 377238106Sdes key.name = nm; 378238106Sdes key.namelen = nmlen; 379238106Sdes key.namelabs = nmlabs; 380238106Sdes return (struct local_data*)rbtree_search(&z->data, &key.node); 381238106Sdes} 382238106Sdes 383238106Sdes/** find a node, create it if not and all its empty nonterminal parents */ 384238106Sdesstatic int 385238106Sdeslz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, 386238106Sdes int nmlabs, struct local_data** res) 387238106Sdes{ 388238106Sdes struct local_data* ld = lz_find_node(z, nm, nmlen, nmlabs); 389238106Sdes if(!ld) { 390238106Sdes /* create a domain name to store rr. */ 391238106Sdes ld = (struct local_data*)regional_alloc_zero(z->region, 392238106Sdes sizeof(*ld)); 393238106Sdes if(!ld) { 394238106Sdes log_err("out of memory adding local data"); 395238106Sdes return 0; 396238106Sdes } 397238106Sdes ld->node.key = ld; 398238106Sdes ld->name = regional_alloc_init(z->region, nm, nmlen); 399238106Sdes if(!ld->name) { 400238106Sdes log_err("out of memory"); 401238106Sdes return 0; 402238106Sdes } 403238106Sdes ld->namelen = nmlen; 404238106Sdes ld->namelabs = nmlabs; 405238106Sdes if(!rbtree_insert(&z->data, &ld->node)) { 406238106Sdes log_assert(0); /* duplicate name */ 407238106Sdes } 408238106Sdes /* see if empty nonterminals need to be created */ 409238106Sdes if(nmlabs > z->namelabs) { 410238106Sdes dname_remove_label(&nm, &nmlen); 411238106Sdes if(!lz_find_create_node(z, nm, nmlen, nmlabs-1, res)) 412238106Sdes return 0; 413238106Sdes } 414238106Sdes } 415238106Sdes *res = ld; 416238106Sdes return 1; 417238106Sdes} 418238106Sdes 419238106Sdes/** enter data RR into auth zone */ 420238106Sdesstatic int 421269257Sdeslz_enter_rr_into_zone(struct local_zone* z, const char* rrstr) 422238106Sdes{ 423238106Sdes uint8_t* nm; 424238106Sdes size_t nmlen; 425238106Sdes int nmlabs; 426238106Sdes struct local_data* node; 427238106Sdes struct local_rrset* rrset; 428238106Sdes struct packed_rrset_data* pd; 429249141Sdes uint16_t rrtype = 0, rrclass = 0; 430269257Sdes time_t ttl = 0; 431269257Sdes uint8_t rr[LDNS_RR_BUF_SIZE]; 432269257Sdes uint8_t* rdata; 433269257Sdes size_t rdata_len; 434269257Sdes if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr), 435269257Sdes &rdata, &rdata_len)) { 436238106Sdes log_err("bad local-data: %s", rrstr); 437238106Sdes return 0; 438238106Sdes } 439238106Sdes log_assert(z->dclass == rrclass); 440238106Sdes if(z->type == local_zone_redirect && 441238106Sdes query_dname_compare(z->name, nm) != 0) { 442238106Sdes log_err("local-data in redirect zone must reside at top of zone" 443238106Sdes ", not at %s", rrstr); 444238106Sdes free(nm); 445238106Sdes return 0; 446238106Sdes } 447238106Sdes nmlabs = dname_count_size_labels(nm, &nmlen); 448238106Sdes if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) { 449238106Sdes free(nm); 450238106Sdes return 0; 451238106Sdes } 452238106Sdes log_assert(node); 453238106Sdes free(nm); 454238106Sdes 455238106Sdes rrset = local_data_find_type(node, rrtype); 456238106Sdes if(!rrset) { 457238106Sdes rrset = new_local_rrset(z->region, node, rrtype, rrclass); 458238106Sdes if(!rrset) 459238106Sdes return 0; 460238106Sdes if(query_dname_compare(node->name, z->name) == 0) { 461238106Sdes if(rrtype == LDNS_RR_TYPE_NSEC) 462238106Sdes rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX; 463238106Sdes if(rrtype == LDNS_RR_TYPE_SOA) 464238106Sdes z->soa = rrset->rrset; 465238106Sdes } 466238106Sdes } 467238106Sdes pd = (struct packed_rrset_data*)rrset->rrset->entry.data; 468238106Sdes log_assert(rrset && pd); 469238106Sdes 470238106Sdes /* check for duplicate RR */ 471269257Sdes if(rr_is_duplicate(pd, rdata, rdata_len)) { 472238106Sdes verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr); 473238106Sdes return 1; 474238106Sdes } 475269257Sdes return insert_rr(z->region, pd, rdata, rdata_len, ttl); 476238106Sdes} 477238106Sdes 478238106Sdes/** enter a data RR into auth data; a zone for it must exist */ 479238106Sdesstatic int 480269257Sdeslz_enter_rr_str(struct local_zones* zones, const char* rr) 481238106Sdes{ 482238106Sdes uint8_t* rr_name; 483238106Sdes uint16_t rr_class; 484238106Sdes size_t len; 485238106Sdes int labs; 486238106Sdes struct local_zone* z; 487238106Sdes int r; 488238106Sdes if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { 489238106Sdes log_err("bad rr %s", rr); 490238106Sdes return 0; 491238106Sdes } 492238106Sdes labs = dname_count_size_labels(rr_name, &len); 493269257Sdes lock_rw_rdlock(&zones->lock); 494238106Sdes z = local_zones_lookup(zones, rr_name, len, labs, rr_class); 495238106Sdes if(!z) { 496269257Sdes lock_rw_unlock(&zones->lock); 497238106Sdes fatal_exit("internal error: no zone for rr %s", rr); 498238106Sdes } 499238106Sdes lock_rw_wrlock(&z->lock); 500269257Sdes lock_rw_unlock(&zones->lock); 501238106Sdes free(rr_name); 502269257Sdes r = lz_enter_rr_into_zone(z, rr); 503238106Sdes lock_rw_unlock(&z->lock); 504238106Sdes return r; 505238106Sdes} 506238106Sdes 507238106Sdes/** parse local-zone: statements */ 508238106Sdesstatic int 509238106Sdeslz_enter_zones(struct local_zones* zones, struct config_file* cfg) 510238106Sdes{ 511238106Sdes struct config_str2list* p; 512238106Sdes struct local_zone* z; 513238106Sdes for(p = cfg->local_zones; p; p = p->next) { 514238106Sdes if(!(z=lz_enter_zone(zones, p->str, p->str2, 515238106Sdes LDNS_RR_CLASS_IN))) 516238106Sdes return 0; 517238106Sdes lock_rw_unlock(&z->lock); 518238106Sdes } 519238106Sdes return 1; 520238106Sdes} 521238106Sdes 522238106Sdes/** lookup a zone in rbtree; exact match only; SLOW due to parse */ 523238106Sdesstatic int 524238106Sdeslz_exists(struct local_zones* zones, const char* name) 525238106Sdes{ 526238106Sdes struct local_zone z; 527238106Sdes z.node.key = &z; 528238106Sdes z.dclass = LDNS_RR_CLASS_IN; 529238106Sdes if(!parse_dname(name, &z.name, &z.namelen, &z.namelabs)) { 530238106Sdes log_err("bad name %s", name); 531238106Sdes return 0; 532238106Sdes } 533269257Sdes lock_rw_rdlock(&zones->lock); 534238106Sdes if(rbtree_search(&zones->ztree, &z.node)) { 535269257Sdes lock_rw_unlock(&zones->lock); 536238106Sdes free(z.name); 537238106Sdes return 1; 538238106Sdes } 539269257Sdes lock_rw_unlock(&zones->lock); 540238106Sdes free(z.name); 541238106Sdes return 0; 542238106Sdes} 543238106Sdes 544238106Sdes/** lookup a zone in cfg->nodefault list */ 545238106Sdesstatic int 546238106Sdeslz_nodefault(struct config_file* cfg, const char* name) 547238106Sdes{ 548238106Sdes struct config_strlist* p; 549238106Sdes size_t len = strlen(name); 550238106Sdes if(len == 0) return 0; 551238106Sdes if(name[len-1] == '.') len--; 552238106Sdes 553238106Sdes for(p = cfg->local_zones_nodefault; p; p = p->next) { 554238106Sdes /* compare zone name, lowercase, compare without ending . */ 555238106Sdes if(strncasecmp(p->str, name, len) == 0 && 556238106Sdes (strlen(p->str) == len || (strlen(p->str)==len+1 && 557238106Sdes p->str[len] == '.'))) 558238106Sdes return 1; 559238106Sdes } 560238106Sdes return 0; 561238106Sdes} 562238106Sdes 563238106Sdes/** enter AS112 default zone */ 564238106Sdesstatic int 565238106Sdesadd_as112_default(struct local_zones* zones, struct config_file* cfg, 566269257Sdes const char* name) 567238106Sdes{ 568238106Sdes struct local_zone* z; 569238106Sdes char str[1024]; /* known long enough */ 570238106Sdes if(lz_exists(zones, name) || lz_nodefault(cfg, name)) 571238106Sdes return 1; /* do not enter default content */ 572238106Sdes if(!(z=lz_enter_zone(zones, name, "static", LDNS_RR_CLASS_IN))) 573238106Sdes return 0; 574238106Sdes snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. " 575238106Sdes "nobody.invalid. 1 3600 1200 604800 10800", name); 576269257Sdes if(!lz_enter_rr_into_zone(z, str)) { 577238106Sdes lock_rw_unlock(&z->lock); 578238106Sdes return 0; 579238106Sdes } 580238106Sdes snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name); 581269257Sdes if(!lz_enter_rr_into_zone(z, str)) { 582238106Sdes lock_rw_unlock(&z->lock); 583238106Sdes return 0; 584238106Sdes } 585238106Sdes lock_rw_unlock(&z->lock); 586238106Sdes return 1; 587238106Sdes} 588238106Sdes 589238106Sdes/** enter default zones */ 590238106Sdesstatic int 591269257Sdeslz_enter_defaults(struct local_zones* zones, struct config_file* cfg) 592238106Sdes{ 593238106Sdes struct local_zone* z; 594238106Sdes 595238106Sdes /* this list of zones is from RFC 6303 */ 596238106Sdes 597269257Sdes /* block localhost level zones, first, later the LAN zones */ 598269257Sdes 599238106Sdes /* localhost. zone */ 600238106Sdes if(!lz_exists(zones, "localhost.") && 601238106Sdes !lz_nodefault(cfg, "localhost.")) { 602238106Sdes if(!(z=lz_enter_zone(zones, "localhost.", "static", 603238106Sdes LDNS_RR_CLASS_IN)) || 604269257Sdes !lz_enter_rr_into_zone(z, 605238106Sdes "localhost. 10800 IN NS localhost.") || 606269257Sdes !lz_enter_rr_into_zone(z, 607238106Sdes "localhost. 10800 IN SOA localhost. nobody.invalid. " 608238106Sdes "1 3600 1200 604800 10800") || 609269257Sdes !lz_enter_rr_into_zone(z, 610238106Sdes "localhost. 10800 IN A 127.0.0.1") || 611269257Sdes !lz_enter_rr_into_zone(z, 612238106Sdes "localhost. 10800 IN AAAA ::1")) { 613238106Sdes log_err("out of memory adding default zone"); 614238106Sdes if(z) { lock_rw_unlock(&z->lock); } 615238106Sdes return 0; 616238106Sdes } 617238106Sdes lock_rw_unlock(&z->lock); 618238106Sdes } 619238106Sdes /* reverse ip4 zone */ 620238106Sdes if(!lz_exists(zones, "127.in-addr.arpa.") && 621238106Sdes !lz_nodefault(cfg, "127.in-addr.arpa.")) { 622238106Sdes if(!(z=lz_enter_zone(zones, "127.in-addr.arpa.", "static", 623238106Sdes LDNS_RR_CLASS_IN)) || 624269257Sdes !lz_enter_rr_into_zone(z, 625238106Sdes "127.in-addr.arpa. 10800 IN NS localhost.") || 626269257Sdes !lz_enter_rr_into_zone(z, 627238106Sdes "127.in-addr.arpa. 10800 IN SOA localhost. " 628238106Sdes "nobody.invalid. 1 3600 1200 604800 10800") || 629269257Sdes !lz_enter_rr_into_zone(z, 630238106Sdes "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) { 631238106Sdes log_err("out of memory adding default zone"); 632238106Sdes if(z) { lock_rw_unlock(&z->lock); } 633238106Sdes return 0; 634238106Sdes } 635238106Sdes lock_rw_unlock(&z->lock); 636238106Sdes } 637238106Sdes /* reverse ip6 zone */ 638238106Sdes 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.") && 639238106Sdes !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.")) { 640238106Sdes 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", 641238106Sdes LDNS_RR_CLASS_IN)) || 642269257Sdes !lz_enter_rr_into_zone(z, 643238106Sdes "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.") || 644269257Sdes !lz_enter_rr_into_zone(z, 645238106Sdes "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. " 646238106Sdes "nobody.invalid. 1 3600 1200 604800 10800") || 647269257Sdes !lz_enter_rr_into_zone(z, 648238106Sdes "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.")) { 649238106Sdes log_err("out of memory adding default zone"); 650238106Sdes if(z) { lock_rw_unlock(&z->lock); } 651238106Sdes return 0; 652238106Sdes } 653238106Sdes lock_rw_unlock(&z->lock); 654238106Sdes } 655269257Sdes 656269257Sdes /* if unblock lan-zones, then do not add the zones below. 657269257Sdes * we do add the zones above, about 127.0.0.1, because localhost is 658269257Sdes * not on the lan. */ 659269257Sdes if(cfg->unblock_lan_zones) 660269257Sdes return 1; 661269257Sdes 662269257Sdes /* block LAN level zones */ 663269257Sdes if ( !add_as112_default(zones, cfg, "10.in-addr.arpa.") || 664269257Sdes !add_as112_default(zones, cfg, "16.172.in-addr.arpa.") || 665269257Sdes !add_as112_default(zones, cfg, "17.172.in-addr.arpa.") || 666269257Sdes !add_as112_default(zones, cfg, "18.172.in-addr.arpa.") || 667269257Sdes !add_as112_default(zones, cfg, "19.172.in-addr.arpa.") || 668269257Sdes !add_as112_default(zones, cfg, "20.172.in-addr.arpa.") || 669269257Sdes !add_as112_default(zones, cfg, "21.172.in-addr.arpa.") || 670269257Sdes !add_as112_default(zones, cfg, "22.172.in-addr.arpa.") || 671269257Sdes !add_as112_default(zones, cfg, "23.172.in-addr.arpa.") || 672269257Sdes !add_as112_default(zones, cfg, "24.172.in-addr.arpa.") || 673269257Sdes !add_as112_default(zones, cfg, "25.172.in-addr.arpa.") || 674269257Sdes !add_as112_default(zones, cfg, "26.172.in-addr.arpa.") || 675269257Sdes !add_as112_default(zones, cfg, "27.172.in-addr.arpa.") || 676269257Sdes !add_as112_default(zones, cfg, "28.172.in-addr.arpa.") || 677269257Sdes !add_as112_default(zones, cfg, "29.172.in-addr.arpa.") || 678269257Sdes !add_as112_default(zones, cfg, "30.172.in-addr.arpa.") || 679269257Sdes !add_as112_default(zones, cfg, "31.172.in-addr.arpa.") || 680269257Sdes !add_as112_default(zones, cfg, "168.192.in-addr.arpa.") || 681269257Sdes !add_as112_default(zones, cfg, "0.in-addr.arpa.") || 682269257Sdes !add_as112_default(zones, cfg, "254.169.in-addr.arpa.") || 683269257Sdes !add_as112_default(zones, cfg, "2.0.192.in-addr.arpa.") || 684269257Sdes !add_as112_default(zones, cfg, "100.51.198.in-addr.arpa.") || 685269257Sdes !add_as112_default(zones, cfg, "113.0.203.in-addr.arpa.") || 686269257Sdes !add_as112_default(zones, cfg, "255.255.255.255.in-addr.arpa.") || 687269257Sdes !add_as112_default(zones, cfg, "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.0.ip6.arpa.") || 688269257Sdes !add_as112_default(zones, cfg, "d.f.ip6.arpa.") || 689269257Sdes !add_as112_default(zones, cfg, "8.e.f.ip6.arpa.") || 690269257Sdes !add_as112_default(zones, cfg, "9.e.f.ip6.arpa.") || 691269257Sdes !add_as112_default(zones, cfg, "a.e.f.ip6.arpa.") || 692269257Sdes !add_as112_default(zones, cfg, "b.e.f.ip6.arpa.") || 693269257Sdes !add_as112_default(zones, cfg, "8.b.d.0.1.0.0.2.ip6.arpa.")) { 694238106Sdes log_err("out of memory adding default zone"); 695238106Sdes return 0; 696238106Sdes } 697238106Sdes return 1; 698238106Sdes} 699238106Sdes 700238106Sdes/** setup parent pointers, so that a lookup can be done for closest match */ 701238106Sdesstatic void 702238106Sdesinit_parents(struct local_zones* zones) 703238106Sdes{ 704238106Sdes struct local_zone* node, *prev = NULL, *p; 705238106Sdes int m; 706269257Sdes lock_rw_wrlock(&zones->lock); 707238106Sdes RBTREE_FOR(node, struct local_zone*, &zones->ztree) { 708238106Sdes lock_rw_wrlock(&node->lock); 709238106Sdes node->parent = NULL; 710238106Sdes if(!prev || prev->dclass != node->dclass) { 711238106Sdes prev = node; 712238106Sdes lock_rw_unlock(&node->lock); 713238106Sdes continue; 714238106Sdes } 715238106Sdes (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, 716238106Sdes node->namelabs, &m); /* we know prev is smaller */ 717238106Sdes /* sort order like: . com. bla.com. zwb.com. net. */ 718238106Sdes /* find the previous, or parent-parent-parent */ 719238106Sdes for(p = prev; p; p = p->parent) 720238106Sdes /* looking for name with few labels, a parent */ 721238106Sdes if(p->namelabs <= m) { 722238106Sdes /* ==: since prev matched m, this is closest*/ 723238106Sdes /* <: prev matches more, but is not a parent, 724238106Sdes * this one is a (grand)parent */ 725238106Sdes node->parent = p; 726238106Sdes break; 727238106Sdes } 728238106Sdes prev = node; 729238106Sdes lock_rw_unlock(&node->lock); 730238106Sdes } 731269257Sdes lock_rw_unlock(&zones->lock); 732238106Sdes} 733238106Sdes 734238106Sdes/** enter implicit transparent zone for local-data: without local-zone: */ 735238106Sdesstatic int 736238106Sdeslz_setup_implicit(struct local_zones* zones, struct config_file* cfg) 737238106Sdes{ 738238106Sdes /* walk over all items that have no parent zone and find 739238106Sdes * the name that covers them all (could be the root) and 740238106Sdes * add that as a transparent zone */ 741238106Sdes struct config_strlist* p; 742238106Sdes int have_name = 0; 743238106Sdes int have_other_classes = 0; 744238106Sdes uint16_t dclass = 0; 745238106Sdes uint8_t* nm = 0; 746238106Sdes size_t nmlen = 0; 747238106Sdes int nmlabs = 0; 748238106Sdes int match = 0; /* number of labels match count */ 749238106Sdes 750238106Sdes init_parents(zones); /* to enable local_zones_lookup() */ 751238106Sdes for(p = cfg->local_data; p; p = p->next) { 752238106Sdes uint8_t* rr_name; 753238106Sdes uint16_t rr_class; 754238106Sdes size_t len; 755238106Sdes int labs; 756238106Sdes if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) { 757238106Sdes log_err("Bad local-data RR %s", p->str); 758238106Sdes return 0; 759238106Sdes } 760238106Sdes labs = dname_count_size_labels(rr_name, &len); 761269257Sdes lock_rw_rdlock(&zones->lock); 762238106Sdes if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) { 763238106Sdes if(!have_name) { 764238106Sdes dclass = rr_class; 765238106Sdes nm = rr_name; 766238106Sdes nmlen = len; 767238106Sdes nmlabs = labs; 768238106Sdes match = labs; 769238106Sdes have_name = 1; 770238106Sdes } else { 771238106Sdes int m; 772238106Sdes if(rr_class != dclass) { 773238106Sdes /* process other classes later */ 774238106Sdes free(rr_name); 775238106Sdes have_other_classes = 1; 776269257Sdes lock_rw_unlock(&zones->lock); 777238106Sdes continue; 778238106Sdes } 779238106Sdes /* find smallest shared topdomain */ 780238106Sdes (void)dname_lab_cmp(nm, nmlabs, 781238106Sdes rr_name, labs, &m); 782238106Sdes free(rr_name); 783238106Sdes if(m < match) 784238106Sdes match = m; 785238106Sdes } 786238106Sdes } else free(rr_name); 787269257Sdes lock_rw_unlock(&zones->lock); 788238106Sdes } 789238106Sdes if(have_name) { 790238106Sdes uint8_t* n2; 791238106Sdes struct local_zone* z; 792238106Sdes /* allocate zone of smallest shared topdomain to contain em */ 793238106Sdes n2 = nm; 794238106Sdes dname_remove_labels(&n2, &nmlen, nmlabs - match); 795238106Sdes n2 = memdup(n2, nmlen); 796238106Sdes free(nm); 797238106Sdes if(!n2) { 798238106Sdes log_err("out of memory"); 799238106Sdes return 0; 800238106Sdes } 801238106Sdes log_nametypeclass(VERB_ALGO, "implicit transparent local-zone", 802238106Sdes n2, 0, dclass); 803238106Sdes if(!(z=lz_enter_zone_dname(zones, n2, nmlen, match, 804238106Sdes local_zone_transparent, dclass))) { 805238106Sdes return 0; 806238106Sdes } 807238106Sdes lock_rw_unlock(&z->lock); 808238106Sdes } 809238106Sdes if(have_other_classes) { 810238106Sdes /* restart to setup other class */ 811238106Sdes return lz_setup_implicit(zones, cfg); 812238106Sdes } 813238106Sdes return 1; 814238106Sdes} 815238106Sdes 816238106Sdes/** enter auth data */ 817238106Sdesstatic int 818269257Sdeslz_enter_data(struct local_zones* zones, struct config_file* cfg) 819238106Sdes{ 820238106Sdes struct config_strlist* p; 821238106Sdes for(p = cfg->local_data; p; p = p->next) { 822269257Sdes if(!lz_enter_rr_str(zones, p->str)) 823238106Sdes return 0; 824238106Sdes } 825238106Sdes return 1; 826238106Sdes} 827238106Sdes 828238106Sdes/** free memory from config */ 829238106Sdesstatic void 830238106Sdeslz_freeup_cfg(struct config_file* cfg) 831238106Sdes{ 832238106Sdes config_deldblstrlist(cfg->local_zones); 833238106Sdes cfg->local_zones = NULL; 834238106Sdes config_delstrlist(cfg->local_zones_nodefault); 835238106Sdes cfg->local_zones_nodefault = NULL; 836238106Sdes config_delstrlist(cfg->local_data); 837238106Sdes cfg->local_data = NULL; 838238106Sdes} 839238106Sdes 840238106Sdesint 841238106Sdeslocal_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg) 842238106Sdes{ 843238106Sdes /* create zones from zone statements. */ 844238106Sdes if(!lz_enter_zones(zones, cfg)) { 845238106Sdes return 0; 846238106Sdes } 847238106Sdes /* apply default zones+content (unless disabled, or overridden) */ 848269257Sdes if(!lz_enter_defaults(zones, cfg)) { 849238106Sdes return 0; 850238106Sdes } 851238106Sdes /* create implicit transparent zone from data. */ 852238106Sdes if(!lz_setup_implicit(zones, cfg)) { 853238106Sdes return 0; 854238106Sdes } 855238106Sdes 856238106Sdes /* setup parent ptrs for lookup during data entry */ 857238106Sdes init_parents(zones); 858238106Sdes /* insert local data */ 859269257Sdes if(!lz_enter_data(zones, cfg)) { 860238106Sdes return 0; 861238106Sdes } 862238106Sdes /* freeup memory from cfg struct. */ 863238106Sdes lz_freeup_cfg(cfg); 864238106Sdes return 1; 865238106Sdes} 866238106Sdes 867238106Sdesstruct local_zone* 868238106Sdeslocal_zones_lookup(struct local_zones* zones, 869238106Sdes uint8_t* name, size_t len, int labs, uint16_t dclass) 870238106Sdes{ 871238106Sdes rbnode_t* res = NULL; 872238106Sdes struct local_zone *result; 873238106Sdes struct local_zone key; 874238106Sdes key.node.key = &key; 875238106Sdes key.dclass = dclass; 876238106Sdes key.name = name; 877238106Sdes key.namelen = len; 878238106Sdes key.namelabs = labs; 879238106Sdes if(rbtree_find_less_equal(&zones->ztree, &key, &res)) { 880238106Sdes /* exact */ 881238106Sdes return (struct local_zone*)res; 882238106Sdes } else { 883238106Sdes /* smaller element (or no element) */ 884238106Sdes int m; 885238106Sdes result = (struct local_zone*)res; 886238106Sdes if(!result || result->dclass != dclass) 887238106Sdes return NULL; 888238106Sdes /* count number of labels matched */ 889238106Sdes (void)dname_lab_cmp(result->name, result->namelabs, key.name, 890238106Sdes key.namelabs, &m); 891238106Sdes while(result) { /* go up until qname is subdomain of zone */ 892238106Sdes if(result->namelabs <= m) 893238106Sdes break; 894238106Sdes result = result->parent; 895238106Sdes } 896238106Sdes return result; 897238106Sdes } 898238106Sdes} 899238106Sdes 900238106Sdesstruct local_zone* 901238106Sdeslocal_zones_find(struct local_zones* zones, 902238106Sdes uint8_t* name, size_t len, int labs, uint16_t dclass) 903238106Sdes{ 904238106Sdes struct local_zone key; 905238106Sdes key.node.key = &key; 906238106Sdes key.dclass = dclass; 907238106Sdes key.name = name; 908238106Sdes key.namelen = len; 909238106Sdes key.namelabs = labs; 910238106Sdes /* exact */ 911238106Sdes return (struct local_zone*)rbtree_search(&zones->ztree, &key); 912238106Sdes} 913238106Sdes 914238106Sdes/** print all RRsets in local zone */ 915238106Sdesstatic void 916238106Sdeslocal_zone_out(struct local_zone* z) 917238106Sdes{ 918238106Sdes struct local_data* d; 919238106Sdes struct local_rrset* p; 920238106Sdes RBTREE_FOR(d, struct local_data*, &z->data) { 921238106Sdes for(p = d->rrsets; p; p = p->next) { 922238106Sdes log_nametypeclass(0, "rrset", d->name, 923238106Sdes ntohs(p->rrset->rk.type), 924238106Sdes ntohs(p->rrset->rk.rrset_class)); 925238106Sdes } 926238106Sdes } 927238106Sdes} 928238106Sdes 929238106Sdesvoid local_zones_print(struct local_zones* zones) 930238106Sdes{ 931238106Sdes struct local_zone* z; 932269257Sdes lock_rw_rdlock(&zones->lock); 933238106Sdes log_info("number of auth zones %u", (unsigned)zones->ztree.count); 934238106Sdes RBTREE_FOR(z, struct local_zone*, &zones->ztree) { 935238106Sdes lock_rw_rdlock(&z->lock); 936238106Sdes switch(z->type) { 937238106Sdes case local_zone_deny: 938238106Sdes log_nametypeclass(0, "deny zone", 939238106Sdes z->name, 0, z->dclass); 940238106Sdes break; 941238106Sdes case local_zone_refuse: 942238106Sdes log_nametypeclass(0, "refuse zone", 943238106Sdes z->name, 0, z->dclass); 944238106Sdes break; 945238106Sdes case local_zone_redirect: 946238106Sdes log_nametypeclass(0, "redirect zone", 947238106Sdes z->name, 0, z->dclass); 948238106Sdes break; 949238106Sdes case local_zone_transparent: 950238106Sdes log_nametypeclass(0, "transparent zone", 951238106Sdes z->name, 0, z->dclass); 952238106Sdes break; 953238106Sdes case local_zone_typetransparent: 954238106Sdes log_nametypeclass(0, "typetransparent zone", 955238106Sdes z->name, 0, z->dclass); 956238106Sdes break; 957238106Sdes case local_zone_static: 958238106Sdes log_nametypeclass(0, "static zone", 959238106Sdes z->name, 0, z->dclass); 960238106Sdes break; 961238106Sdes default: 962238106Sdes log_nametypeclass(0, "badtyped zone", 963238106Sdes z->name, 0, z->dclass); 964238106Sdes break; 965238106Sdes } 966238106Sdes local_zone_out(z); 967238106Sdes lock_rw_unlock(&z->lock); 968238106Sdes } 969269257Sdes lock_rw_unlock(&zones->lock); 970238106Sdes} 971238106Sdes 972238106Sdes/** encode answer consisting of 1 rrset */ 973238106Sdesstatic int 974238106Sdeslocal_encode(struct query_info* qinfo, struct edns_data* edns, 975269257Sdes sldns_buffer* buf, struct regional* temp, 976238106Sdes struct ub_packed_rrset_key* rrset, int ansec, int rcode) 977238106Sdes{ 978238106Sdes struct reply_info rep; 979238106Sdes uint16_t udpsize; 980238106Sdes /* make answer with time=0 for fixed TTL values */ 981238106Sdes memset(&rep, 0, sizeof(rep)); 982238106Sdes rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); 983238106Sdes rep.qdcount = 1; 984238106Sdes if(ansec) 985238106Sdes rep.an_numrrsets = 1; 986238106Sdes else rep.ns_numrrsets = 1; 987238106Sdes rep.rrset_count = 1; 988238106Sdes rep.rrsets = &rrset; 989238106Sdes udpsize = edns->udp_size; 990238106Sdes edns->edns_version = EDNS_ADVERTISED_VERSION; 991238106Sdes edns->udp_size = EDNS_ADVERTISED_SIZE; 992238106Sdes edns->ext_rcode = 0; 993238106Sdes edns->bits &= EDNS_DO; 994238106Sdes if(!reply_info_answer_encode(qinfo, &rep, 995269257Sdes *(uint16_t*)sldns_buffer_begin(buf), 996269257Sdes sldns_buffer_read_u16_at(buf, 2), 997238106Sdes buf, 0, 0, temp, udpsize, edns, 998238106Sdes (int)(edns->bits&EDNS_DO), 0)) 999238106Sdes error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, 1000269257Sdes *(uint16_t*)sldns_buffer_begin(buf), 1001269257Sdes sldns_buffer_read_u16_at(buf, 2), edns); 1002238106Sdes return 1; 1003238106Sdes} 1004238106Sdes 1005238106Sdes/** answer local data match */ 1006238106Sdesstatic int 1007238106Sdeslocal_data_answer(struct local_zone* z, struct query_info* qinfo, 1008269257Sdes struct edns_data* edns, sldns_buffer* buf, struct regional* temp, 1009238106Sdes int labs, struct local_data** ldp) 1010238106Sdes{ 1011238106Sdes struct local_data key; 1012238106Sdes struct local_data* ld; 1013238106Sdes struct local_rrset* lr; 1014238106Sdes key.node.key = &key; 1015238106Sdes key.name = qinfo->qname; 1016238106Sdes key.namelen = qinfo->qname_len; 1017238106Sdes key.namelabs = labs; 1018238106Sdes if(z->type == local_zone_redirect) { 1019238106Sdes key.name = z->name; 1020238106Sdes key.namelen = z->namelen; 1021238106Sdes key.namelabs = z->namelabs; 1022238106Sdes } 1023238106Sdes ld = (struct local_data*)rbtree_search(&z->data, &key.node); 1024238106Sdes *ldp = ld; 1025238106Sdes if(!ld) { 1026238106Sdes return 0; 1027238106Sdes } 1028238106Sdes lr = local_data_find_type(ld, qinfo->qtype); 1029238106Sdes if(!lr) 1030238106Sdes return 0; 1031238106Sdes if(z->type == local_zone_redirect) { 1032238106Sdes /* convert rrset name to query name; like a wildcard */ 1033238106Sdes struct ub_packed_rrset_key r = *lr->rrset; 1034238106Sdes r.rk.dname = qinfo->qname; 1035238106Sdes r.rk.dname_len = qinfo->qname_len; 1036238106Sdes return local_encode(qinfo, edns, buf, temp, &r, 1, 1037238106Sdes LDNS_RCODE_NOERROR); 1038238106Sdes } 1039238106Sdes return local_encode(qinfo, edns, buf, temp, lr->rrset, 1, 1040238106Sdes LDNS_RCODE_NOERROR); 1041238106Sdes} 1042238106Sdes 1043238106Sdes/** 1044238106Sdes * answer in case where no exact match is found 1045238106Sdes * @param z: zone for query 1046238106Sdes * @param qinfo: query 1047238106Sdes * @param edns: edns from query 1048238106Sdes * @param buf: buffer for answer. 1049238106Sdes * @param temp: temp region for encoding 1050238106Sdes * @param ld: local data, if NULL, no such name exists in localdata. 1051238106Sdes * @return 1 if a reply is to be sent, 0 if not. 1052238106Sdes */ 1053238106Sdesstatic int 1054238106Sdeslz_zone_answer(struct local_zone* z, struct query_info* qinfo, 1055269257Sdes struct edns_data* edns, sldns_buffer* buf, struct regional* temp, 1056238106Sdes struct local_data* ld) 1057238106Sdes{ 1058238106Sdes if(z->type == local_zone_deny) { 1059238106Sdes /** no reply at all, signal caller by clearing buffer. */ 1060269257Sdes sldns_buffer_clear(buf); 1061269257Sdes sldns_buffer_flip(buf); 1062238106Sdes return 1; 1063238106Sdes } else if(z->type == local_zone_refuse) { 1064238106Sdes error_encode(buf, (LDNS_RCODE_REFUSED|BIT_AA), qinfo, 1065269257Sdes *(uint16_t*)sldns_buffer_begin(buf), 1066269257Sdes sldns_buffer_read_u16_at(buf, 2), edns); 1067238106Sdes return 1; 1068238106Sdes } else if(z->type == local_zone_static || 1069238106Sdes z->type == local_zone_redirect) { 1070238106Sdes /* for static, reply nodata or nxdomain 1071238106Sdes * for redirect, reply nodata */ 1072238106Sdes /* no additional section processing, 1073238106Sdes * cname, dname or wildcard processing, 1074238106Sdes * or using closest match for NSEC. 1075238106Sdes * or using closest match for returning delegation downwards 1076238106Sdes */ 1077238106Sdes int rcode = ld?LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN; 1078238106Sdes if(z->soa) 1079238106Sdes return local_encode(qinfo, edns, buf, temp, 1080238106Sdes z->soa, 0, rcode); 1081238106Sdes error_encode(buf, (rcode|BIT_AA), qinfo, 1082269257Sdes *(uint16_t*)sldns_buffer_begin(buf), 1083269257Sdes sldns_buffer_read_u16_at(buf, 2), edns); 1084238106Sdes return 1; 1085238106Sdes } else if(z->type == local_zone_typetransparent) { 1086238106Sdes /* no NODATA or NXDOMAINS for this zone type */ 1087238106Sdes return 0; 1088238106Sdes } 1089238106Sdes /* else z->type == local_zone_transparent */ 1090238106Sdes 1091238106Sdes /* if the zone is transparent and the name exists, but the type 1092238106Sdes * does not, then we should make this noerror/nodata */ 1093238106Sdes if(ld && ld->rrsets) { 1094238106Sdes int rcode = LDNS_RCODE_NOERROR; 1095238106Sdes if(z->soa) 1096238106Sdes return local_encode(qinfo, edns, buf, temp, 1097238106Sdes z->soa, 0, rcode); 1098238106Sdes error_encode(buf, (rcode|BIT_AA), qinfo, 1099269257Sdes *(uint16_t*)sldns_buffer_begin(buf), 1100269257Sdes sldns_buffer_read_u16_at(buf, 2), edns); 1101238106Sdes return 1; 1102238106Sdes } 1103238106Sdes 1104238106Sdes /* stop here, and resolve further on */ 1105238106Sdes return 0; 1106238106Sdes} 1107238106Sdes 1108238106Sdesint 1109238106Sdeslocal_zones_answer(struct local_zones* zones, struct query_info* qinfo, 1110269257Sdes struct edns_data* edns, sldns_buffer* buf, struct regional* temp) 1111238106Sdes{ 1112238106Sdes /* see if query is covered by a zone, 1113238106Sdes * if so: - try to match (exact) local data 1114238106Sdes * - look at zone type for negative response. */ 1115238106Sdes int labs = dname_count_labels(qinfo->qname); 1116238106Sdes struct local_data* ld; 1117238106Sdes struct local_zone* z; 1118238106Sdes int r; 1119269257Sdes lock_rw_rdlock(&zones->lock); 1120238106Sdes z = local_zones_lookup(zones, qinfo->qname, 1121238106Sdes qinfo->qname_len, labs, qinfo->qclass); 1122238106Sdes if(!z) { 1123269257Sdes lock_rw_unlock(&zones->lock); 1124238106Sdes return 0; 1125238106Sdes } 1126238106Sdes lock_rw_rdlock(&z->lock); 1127269257Sdes lock_rw_unlock(&zones->lock); 1128238106Sdes 1129238106Sdes if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld)) { 1130238106Sdes lock_rw_unlock(&z->lock); 1131238106Sdes return 1; 1132238106Sdes } 1133238106Sdes r = lz_zone_answer(z, qinfo, edns, buf, temp, ld); 1134238106Sdes lock_rw_unlock(&z->lock); 1135238106Sdes return r; 1136238106Sdes} 1137238106Sdes 1138238106Sdesconst char* local_zone_type2str(enum localzone_type t) 1139238106Sdes{ 1140238106Sdes switch(t) { 1141238106Sdes case local_zone_deny: return "deny"; 1142238106Sdes case local_zone_refuse: return "refuse"; 1143238106Sdes case local_zone_redirect: return "redirect"; 1144238106Sdes case local_zone_transparent: return "transparent"; 1145238106Sdes case local_zone_typetransparent: return "typetransparent"; 1146238106Sdes case local_zone_static: return "static"; 1147238106Sdes case local_zone_nodefault: return "nodefault"; 1148238106Sdes } 1149238106Sdes return "badtyped"; 1150238106Sdes} 1151238106Sdes 1152238106Sdesint local_zone_str2type(const char* type, enum localzone_type* t) 1153238106Sdes{ 1154238106Sdes if(strcmp(type, "deny") == 0) 1155238106Sdes *t = local_zone_deny; 1156238106Sdes else if(strcmp(type, "refuse") == 0) 1157238106Sdes *t = local_zone_refuse; 1158238106Sdes else if(strcmp(type, "static") == 0) 1159238106Sdes *t = local_zone_static; 1160238106Sdes else if(strcmp(type, "transparent") == 0) 1161238106Sdes *t = local_zone_transparent; 1162238106Sdes else if(strcmp(type, "typetransparent") == 0) 1163238106Sdes *t = local_zone_typetransparent; 1164238106Sdes else if(strcmp(type, "redirect") == 0) 1165238106Sdes *t = local_zone_redirect; 1166238106Sdes else return 0; 1167238106Sdes return 1; 1168238106Sdes} 1169238106Sdes 1170238106Sdes/** iterate over the kiddies of the given name and set their parent ptr */ 1171238106Sdesstatic void 1172238106Sdesset_kiddo_parents(struct local_zone* z, struct local_zone* match, 1173238106Sdes struct local_zone* newp) 1174238106Sdes{ 1175238106Sdes /* both zones and z are locked already */ 1176238106Sdes /* in the sorted rbtree, the kiddies of z are located after z */ 1177238106Sdes /* z must be present in the tree */ 1178238106Sdes struct local_zone* p = z; 1179238106Sdes p = (struct local_zone*)rbtree_next(&p->node); 1180238106Sdes while(p!=(struct local_zone*)RBTREE_NULL && 1181238106Sdes p->dclass == z->dclass && dname_strict_subdomain(p->name, 1182238106Sdes p->namelabs, z->name, z->namelabs)) { 1183238106Sdes /* update parent ptr */ 1184238106Sdes /* only when matches with existing parent pointer, so that 1185238106Sdes * deeper child structures are not touched, i.e. 1186238106Sdes * update of x, and a.x, b.x, f.b.x, g.b.x, c.x, y 1187238106Sdes * gets to update a.x, b.x and c.x */ 1188238106Sdes lock_rw_wrlock(&p->lock); 1189238106Sdes if(p->parent == match) 1190238106Sdes p->parent = newp; 1191238106Sdes lock_rw_unlock(&p->lock); 1192238106Sdes p = (struct local_zone*)rbtree_next(&p->node); 1193238106Sdes } 1194238106Sdes} 1195238106Sdes 1196238106Sdesstruct local_zone* local_zones_add_zone(struct local_zones* zones, 1197238106Sdes uint8_t* name, size_t len, int labs, uint16_t dclass, 1198238106Sdes enum localzone_type tp) 1199238106Sdes{ 1200238106Sdes /* create */ 1201238106Sdes struct local_zone* z = local_zone_create(name, len, labs, tp, dclass); 1202238106Sdes if(!z) return NULL; 1203238106Sdes lock_rw_wrlock(&z->lock); 1204238106Sdes 1205238106Sdes /* find the closest parent */ 1206238106Sdes z->parent = local_zones_find(zones, name, len, labs, dclass); 1207238106Sdes 1208238106Sdes /* insert into the tree */ 1209238106Sdes if(!rbtree_insert(&zones->ztree, &z->node)) { 1210238106Sdes /* duplicate entry! */ 1211238106Sdes lock_rw_unlock(&z->lock); 1212238106Sdes local_zone_delete(z); 1213238106Sdes log_err("internal: duplicate entry in local_zones_add_zone"); 1214238106Sdes return NULL; 1215238106Sdes } 1216238106Sdes 1217238106Sdes /* set parent pointers right */ 1218238106Sdes set_kiddo_parents(z, z->parent, z); 1219238106Sdes 1220238106Sdes lock_rw_unlock(&z->lock); 1221238106Sdes return z; 1222238106Sdes} 1223238106Sdes 1224238106Sdesvoid local_zones_del_zone(struct local_zones* zones, struct local_zone* z) 1225238106Sdes{ 1226238106Sdes /* fix up parents in tree */ 1227238106Sdes lock_rw_wrlock(&z->lock); 1228238106Sdes set_kiddo_parents(z, z, z->parent); 1229238106Sdes 1230238106Sdes /* remove from tree */ 1231238106Sdes (void)rbtree_delete(&zones->ztree, z); 1232238106Sdes 1233238106Sdes /* delete the zone */ 1234238106Sdes lock_rw_unlock(&z->lock); 1235238106Sdes local_zone_delete(z); 1236238106Sdes} 1237238106Sdes 1238238106Sdesint 1239269257Sdeslocal_zones_add_RR(struct local_zones* zones, const char* rr) 1240238106Sdes{ 1241238106Sdes uint8_t* rr_name; 1242238106Sdes uint16_t rr_class; 1243238106Sdes size_t len; 1244238106Sdes int labs; 1245238106Sdes struct local_zone* z; 1246238106Sdes int r; 1247238106Sdes if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { 1248238106Sdes return 0; 1249238106Sdes } 1250238106Sdes labs = dname_count_size_labels(rr_name, &len); 1251269257Sdes /* could first try readlock then get writelock if zone does not exist, 1252269257Sdes * but we do not add enough RRs (from multiple threads) to optimize */ 1253269257Sdes lock_rw_wrlock(&zones->lock); 1254238106Sdes z = local_zones_lookup(zones, rr_name, len, labs, rr_class); 1255238106Sdes if(!z) { 1256238106Sdes z = local_zones_add_zone(zones, rr_name, len, labs, rr_class, 1257238106Sdes local_zone_transparent); 1258238106Sdes if(!z) { 1259269257Sdes lock_rw_unlock(&zones->lock); 1260238106Sdes return 0; 1261238106Sdes } 1262238106Sdes } else { 1263238106Sdes free(rr_name); 1264238106Sdes } 1265238106Sdes lock_rw_wrlock(&z->lock); 1266269257Sdes lock_rw_unlock(&zones->lock); 1267269257Sdes r = lz_enter_rr_into_zone(z, rr); 1268238106Sdes lock_rw_unlock(&z->lock); 1269238106Sdes return r; 1270238106Sdes} 1271238106Sdes 1272238106Sdes/** returns true if the node is terminal so no deeper domain names exist */ 1273238106Sdesstatic int 1274238106Sdesis_terminal(struct local_data* d) 1275238106Sdes{ 1276238106Sdes /* for empty nonterminals, the deeper domain names are sorted 1277238106Sdes * right after them, so simply check the next name in the tree 1278238106Sdes */ 1279238106Sdes struct local_data* n = (struct local_data*)rbtree_next(&d->node); 1280238106Sdes if(n == (struct local_data*)RBTREE_NULL) 1281238106Sdes return 1; /* last in tree, no deeper node */ 1282238106Sdes if(dname_strict_subdomain(n->name, n->namelabs, d->name, d->namelabs)) 1283238106Sdes return 0; /* there is a deeper node */ 1284238106Sdes return 1; 1285238106Sdes} 1286238106Sdes 1287238106Sdes/** delete empty terminals from tree when final data is deleted */ 1288238106Sdesstatic void 1289238106Sdesdel_empty_term(struct local_zone* z, struct local_data* d, 1290238106Sdes uint8_t* name, size_t len, int labs) 1291238106Sdes{ 1292238106Sdes while(d && d->rrsets == NULL && is_terminal(d)) { 1293238106Sdes /* is this empty nonterminal? delete */ 1294238106Sdes /* note, no memory recycling in zone region */ 1295238106Sdes (void)rbtree_delete(&z->data, d); 1296238106Sdes 1297238106Sdes /* go up and to the next label */ 1298238106Sdes if(dname_is_root(name)) 1299238106Sdes return; 1300238106Sdes dname_remove_label(&name, &len); 1301238106Sdes labs--; 1302238106Sdes d = lz_find_node(z, name, len, labs); 1303238106Sdes } 1304238106Sdes} 1305238106Sdes 1306238106Sdesvoid local_zones_del_data(struct local_zones* zones, 1307238106Sdes uint8_t* name, size_t len, int labs, uint16_t dclass) 1308238106Sdes{ 1309238106Sdes /* find zone */ 1310238106Sdes struct local_zone* z; 1311238106Sdes struct local_data* d; 1312269257Sdes lock_rw_rdlock(&zones->lock); 1313238106Sdes z = local_zones_lookup(zones, name, len, labs, dclass); 1314238106Sdes if(!z) { 1315238106Sdes /* no such zone, we're done */ 1316269257Sdes lock_rw_unlock(&zones->lock); 1317238106Sdes return; 1318238106Sdes } 1319238106Sdes lock_rw_wrlock(&z->lock); 1320269257Sdes lock_rw_unlock(&zones->lock); 1321238106Sdes 1322238106Sdes /* find the domain */ 1323238106Sdes d = lz_find_node(z, name, len, labs); 1324238106Sdes if(d) { 1325238106Sdes /* no memory recycling for zone deletions ... */ 1326238106Sdes d->rrsets = NULL; 1327238106Sdes /* did we delete the soa record ? */ 1328238106Sdes if(query_dname_compare(d->name, z->name) == 0) 1329238106Sdes z->soa = NULL; 1330238106Sdes 1331238106Sdes /* cleanup the empty nonterminals for this name */ 1332238106Sdes del_empty_term(z, d, name, len, labs); 1333238106Sdes } 1334238106Sdes 1335238106Sdes lock_rw_unlock(&z->lock); 1336238106Sdes} 1337