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 24238106Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25238106Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26238106Sdes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 27238106Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28238106Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31238106Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33238106Sdes * 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 <ldns/dname.h> 43238106Sdes#include <ldns/host2wire.h> 44238106Sdes#include "services/localzone.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); 62238106Sdes lock_quick_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; 81238106Sdes lock_quick_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{ 128238106Sdes ldns_rdf* rdf; 129238106Sdes *res = NULL; 130238106Sdes *len = 0; 131238106Sdes *labs = 0; 132238106Sdes rdf = ldns_dname_new_frm_str(str); 133238106Sdes if(!rdf) { 134238106Sdes log_err("cannot parse name %s", str); 135238106Sdes return 0; 136238106Sdes } 137238106Sdes *res = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf)); 138238106Sdes ldns_rdf_deep_free(rdf); 139238106Sdes if(!*res) { 140238106Sdes log_err("out of memory"); 141238106Sdes return 0; 142238106Sdes } 143238106Sdes *labs = dname_count_size_labels(*res, len); 144238106Sdes return 1; 145238106Sdes} 146238106Sdes 147238106Sdes/** create a new localzone */ 148238106Sdesstatic struct local_zone* 149238106Sdeslocal_zone_create(uint8_t* nm, size_t len, int labs, 150238106Sdes enum localzone_type t, uint16_t dclass) 151238106Sdes{ 152238106Sdes struct local_zone* z = (struct local_zone*)calloc(1, sizeof(*z)); 153238106Sdes if(!z) { 154238106Sdes return NULL; 155238106Sdes } 156238106Sdes z->node.key = z; 157238106Sdes z->dclass = dclass; 158238106Sdes z->type = t; 159238106Sdes z->name = nm; 160238106Sdes z->namelen = len; 161238106Sdes z->namelabs = labs; 162238106Sdes lock_rw_init(&z->lock); 163238106Sdes z->region = regional_create(); 164238106Sdes if(!z->region) { 165238106Sdes free(z); 166238106Sdes return NULL; 167238106Sdes } 168238106Sdes rbtree_init(&z->data, &local_data_cmp); 169238106Sdes lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_t)); 170238106Sdes /* also the zones->lock protects node, parent, name*, class */ 171238106Sdes return z; 172238106Sdes} 173238106Sdes 174238106Sdes/** enter a new zone with allocated dname returns with WRlock */ 175238106Sdesstatic struct local_zone* 176238106Sdeslz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len, 177238106Sdes int labs, enum localzone_type t, uint16_t c) 178238106Sdes{ 179238106Sdes struct local_zone* z = local_zone_create(nm, len, labs, t, c); 180238106Sdes if(!z) { 181238106Sdes log_err("out of memory"); 182238106Sdes return NULL; 183238106Sdes } 184238106Sdes 185238106Sdes /* add to rbtree */ 186238106Sdes lock_quick_lock(&zones->lock); 187238106Sdes lock_rw_wrlock(&z->lock); 188238106Sdes if(!rbtree_insert(&zones->ztree, &z->node)) { 189238106Sdes log_warn("duplicate local-zone"); 190238106Sdes lock_rw_unlock(&z->lock); 191238106Sdes local_zone_delete(z); 192238106Sdes lock_quick_unlock(&zones->lock); 193238106Sdes return NULL; 194238106Sdes } 195238106Sdes lock_quick_unlock(&zones->lock); 196238106Sdes return z; 197238106Sdes} 198238106Sdes 199238106Sdes/** enter a new zone */ 200238106Sdesstatic struct local_zone* 201238106Sdeslz_enter_zone(struct local_zones* zones, const char* name, const char* type, 202238106Sdes uint16_t dclass) 203238106Sdes{ 204238106Sdes struct local_zone* z; 205238106Sdes enum localzone_type t; 206238106Sdes uint8_t* nm; 207238106Sdes size_t len; 208238106Sdes int labs; 209238106Sdes if(!parse_dname(name, &nm, &len, &labs)) { 210238106Sdes log_err("bad zone name %s %s", name, type); 211238106Sdes return NULL; 212238106Sdes } 213238106Sdes if(!local_zone_str2type(type, &t)) { 214238106Sdes log_err("bad lz_enter_zone type %s %s", name, type); 215238106Sdes free(nm); 216238106Sdes return NULL; 217238106Sdes } 218238106Sdes if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) { 219238106Sdes log_err("could not enter zone %s %s", name, type); 220238106Sdes return NULL; 221238106Sdes } 222238106Sdes return z; 223238106Sdes} 224238106Sdes 225238106Sdes/** return name and class and rdata of rr; parses string */ 226238106Sdesstatic int 227238106Sdesget_rr_content(const char* str, uint8_t** nm, uint16_t* type, 228238106Sdes uint16_t* dclass, uint32_t* ttl, ldns_buffer* rdata) 229238106Sdes{ 230238106Sdes ldns_rr* rr = NULL; 231238106Sdes ldns_status status = ldns_rr_new_frm_str(&rr, str, 3600, NULL, NULL); 232238106Sdes if(status != LDNS_STATUS_OK) { 233238106Sdes log_err("error parsing local-data '%s': %s", 234238106Sdes str, ldns_get_errorstr_by_id(status)); 235238106Sdes ldns_rr_free(rr); 236238106Sdes return 0; 237238106Sdes } 238238106Sdes *nm = memdup(ldns_rdf_data(ldns_rr_owner(rr)), 239238106Sdes ldns_rdf_size(ldns_rr_owner(rr))); 240238106Sdes if(!*nm) { 241238106Sdes log_err("out of memory"); 242238106Sdes ldns_rr_free(rr); 243238106Sdes return 0; 244238106Sdes } 245238106Sdes *dclass = ldns_rr_get_class(rr); 246238106Sdes *type = ldns_rr_get_type(rr); 247238106Sdes *ttl = (uint32_t)ldns_rr_ttl(rr); 248238106Sdes ldns_buffer_clear(rdata); 249238106Sdes ldns_buffer_skip(rdata, 2); 250238106Sdes status = ldns_rr_rdata2buffer_wire(rdata, rr); 251238106Sdes ldns_rr_free(rr); 252238106Sdes if(status != LDNS_STATUS_OK) { 253238106Sdes log_err("error converting RR '%s' to wireformat: %s", 254238106Sdes str, ldns_get_errorstr_by_id(status)); 255238106Sdes free(*nm); 256238106Sdes *nm = NULL; 257238106Sdes return 0; 258238106Sdes } 259238106Sdes ldns_buffer_flip(rdata); 260238106Sdes ldns_buffer_write_u16_at(rdata, 0, ldns_buffer_limit(rdata) - 2); 261238106Sdes return 1; 262238106Sdes} 263238106Sdes 264238106Sdes/** return name and class of rr; parses string */ 265238106Sdesstatic int 266238106Sdesget_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass) 267238106Sdes{ 268238106Sdes ldns_rr* rr = NULL; 269238106Sdes ldns_status status = ldns_rr_new_frm_str(&rr, str, 3600, NULL, NULL); 270238106Sdes if(status != LDNS_STATUS_OK) { 271238106Sdes log_err("error parsing local-data '%s': %s", 272238106Sdes str, ldns_get_errorstr_by_id(status)); 273238106Sdes ldns_rr_free(rr); 274238106Sdes return 0; 275238106Sdes } 276238106Sdes *nm = memdup(ldns_rdf_data(ldns_rr_owner(rr)), 277238106Sdes ldns_rdf_size(ldns_rr_owner(rr))); 278238106Sdes *dclass = ldns_rr_get_class(rr); 279238106Sdes ldns_rr_free(rr); 280238106Sdes if(!*nm) { 281238106Sdes log_err("out of memory"); 282238106Sdes return 0; 283238106Sdes } 284238106Sdes return 1; 285238106Sdes} 286238106Sdes 287238106Sdes/** 288238106Sdes * Find an rrset in local data structure. 289238106Sdes * @param data: local data domain name structure. 290238106Sdes * @param type: type to look for (host order). 291238106Sdes * @return rrset pointer or NULL if not found. 292238106Sdes */ 293238106Sdesstatic struct local_rrset* 294238106Sdeslocal_data_find_type(struct local_data* data, uint16_t type) 295238106Sdes{ 296238106Sdes struct local_rrset* p; 297238106Sdes type = htons(type); 298238106Sdes for(p = data->rrsets; p; p = p->next) { 299238106Sdes if(p->rrset->rk.type == type) 300238106Sdes return p; 301238106Sdes } 302238106Sdes return NULL; 303238106Sdes} 304238106Sdes 305238106Sdes/** check for RR duplicates */ 306238106Sdesstatic int 307238106Sdesrr_is_duplicate(struct packed_rrset_data* pd, ldns_buffer* buf) 308238106Sdes{ 309238106Sdes size_t i; 310238106Sdes for(i=0; i<pd->count; i++) { 311238106Sdes if(ldns_buffer_limit(buf) == pd->rr_len[i] && 312238106Sdes memcmp(ldns_buffer_begin(buf), pd->rr_data[i], 313238106Sdes ldns_buffer_limit(buf)) == 0) 314238106Sdes return 1; 315238106Sdes } 316238106Sdes return 0; 317238106Sdes} 318238106Sdes 319238106Sdes/** new local_rrset */ 320238106Sdesstatic struct local_rrset* 321238106Sdesnew_local_rrset(struct regional* region, struct local_data* node, 322238106Sdes uint16_t rrtype, uint16_t rrclass) 323238106Sdes{ 324238106Sdes struct packed_rrset_data* pd; 325238106Sdes struct local_rrset* rrset = (struct local_rrset*) 326238106Sdes regional_alloc_zero(region, sizeof(*rrset)); 327238106Sdes if(!rrset) { 328238106Sdes log_err("out of memory"); 329238106Sdes return NULL; 330238106Sdes } 331238106Sdes rrset->next = node->rrsets; 332238106Sdes node->rrsets = rrset; 333238106Sdes rrset->rrset = (struct ub_packed_rrset_key*) 334238106Sdes regional_alloc_zero(region, sizeof(*rrset->rrset)); 335238106Sdes if(!rrset->rrset) { 336238106Sdes log_err("out of memory"); 337238106Sdes return NULL; 338238106Sdes } 339238106Sdes rrset->rrset->entry.key = rrset->rrset; 340238106Sdes pd = (struct packed_rrset_data*)regional_alloc_zero(region, 341238106Sdes sizeof(*pd)); 342238106Sdes if(!pd) { 343238106Sdes log_err("out of memory"); 344238106Sdes return NULL; 345238106Sdes } 346238106Sdes pd->trust = rrset_trust_prim_noglue; 347238106Sdes pd->security = sec_status_insecure; 348238106Sdes rrset->rrset->entry.data = pd; 349238106Sdes rrset->rrset->rk.dname = node->name; 350238106Sdes rrset->rrset->rk.dname_len = node->namelen; 351238106Sdes rrset->rrset->rk.type = htons(rrtype); 352238106Sdes rrset->rrset->rk.rrset_class = htons(rrclass); 353238106Sdes return rrset; 354238106Sdes} 355238106Sdes 356238106Sdes/** insert RR into RRset data structure; Wastes a couple of bytes */ 357238106Sdesstatic int 358238106Sdesinsert_rr(struct regional* region, struct packed_rrset_data* pd, 359238106Sdes ldns_buffer* buf, uint32_t ttl) 360238106Sdes{ 361238106Sdes size_t* oldlen = pd->rr_len; 362238106Sdes uint32_t* oldttl = pd->rr_ttl; 363238106Sdes uint8_t** olddata = pd->rr_data; 364238106Sdes 365238106Sdes /* add RR to rrset */ 366238106Sdes pd->count++; 367238106Sdes pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count); 368238106Sdes pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count); 369238106Sdes pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count); 370238106Sdes if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) { 371238106Sdes log_err("out of memory"); 372238106Sdes return 0; 373238106Sdes } 374238106Sdes if(pd->count > 1) { 375238106Sdes memcpy(pd->rr_len+1, oldlen, 376238106Sdes sizeof(*pd->rr_len)*(pd->count-1)); 377238106Sdes memcpy(pd->rr_ttl+1, oldttl, 378238106Sdes sizeof(*pd->rr_ttl)*(pd->count-1)); 379238106Sdes memcpy(pd->rr_data+1, olddata, 380238106Sdes sizeof(*pd->rr_data)*(pd->count-1)); 381238106Sdes } 382238106Sdes pd->rr_len[0] = ldns_buffer_limit(buf); 383238106Sdes pd->rr_ttl[0] = ttl; 384238106Sdes pd->rr_data[0] = regional_alloc_init(region, 385238106Sdes ldns_buffer_begin(buf), ldns_buffer_limit(buf)); 386238106Sdes if(!pd->rr_data[0]) { 387238106Sdes log_err("out of memory"); 388238106Sdes return 0; 389238106Sdes } 390238106Sdes return 1; 391238106Sdes} 392238106Sdes 393238106Sdes/** find a data node by exact name */ 394238106Sdesstatic struct local_data* 395238106Sdeslz_find_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs) 396238106Sdes{ 397238106Sdes struct local_data key; 398238106Sdes key.node.key = &key; 399238106Sdes key.name = nm; 400238106Sdes key.namelen = nmlen; 401238106Sdes key.namelabs = nmlabs; 402238106Sdes return (struct local_data*)rbtree_search(&z->data, &key.node); 403238106Sdes} 404238106Sdes 405238106Sdes/** find a node, create it if not and all its empty nonterminal parents */ 406238106Sdesstatic int 407238106Sdeslz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, 408238106Sdes int nmlabs, struct local_data** res) 409238106Sdes{ 410238106Sdes struct local_data* ld = lz_find_node(z, nm, nmlen, nmlabs); 411238106Sdes if(!ld) { 412238106Sdes /* create a domain name to store rr. */ 413238106Sdes ld = (struct local_data*)regional_alloc_zero(z->region, 414238106Sdes sizeof(*ld)); 415238106Sdes if(!ld) { 416238106Sdes log_err("out of memory adding local data"); 417238106Sdes return 0; 418238106Sdes } 419238106Sdes ld->node.key = ld; 420238106Sdes ld->name = regional_alloc_init(z->region, nm, nmlen); 421238106Sdes if(!ld->name) { 422238106Sdes log_err("out of memory"); 423238106Sdes return 0; 424238106Sdes } 425238106Sdes ld->namelen = nmlen; 426238106Sdes ld->namelabs = nmlabs; 427238106Sdes if(!rbtree_insert(&z->data, &ld->node)) { 428238106Sdes log_assert(0); /* duplicate name */ 429238106Sdes } 430238106Sdes /* see if empty nonterminals need to be created */ 431238106Sdes if(nmlabs > z->namelabs) { 432238106Sdes dname_remove_label(&nm, &nmlen); 433238106Sdes if(!lz_find_create_node(z, nm, nmlen, nmlabs-1, res)) 434238106Sdes return 0; 435238106Sdes } 436238106Sdes } 437238106Sdes *res = ld; 438238106Sdes return 1; 439238106Sdes} 440238106Sdes 441238106Sdes/** enter data RR into auth zone */ 442238106Sdesstatic int 443238106Sdeslz_enter_rr_into_zone(struct local_zone* z, ldns_buffer* buf, 444238106Sdes const char* rrstr) 445238106Sdes{ 446238106Sdes uint8_t* nm; 447238106Sdes size_t nmlen; 448238106Sdes int nmlabs; 449238106Sdes struct local_data* node; 450238106Sdes struct local_rrset* rrset; 451238106Sdes struct packed_rrset_data* pd; 452249141Sdes uint16_t rrtype = 0, rrclass = 0; 453249141Sdes uint32_t ttl = 0; 454238106Sdes if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, buf)) { 455238106Sdes log_err("bad local-data: %s", rrstr); 456238106Sdes return 0; 457238106Sdes } 458238106Sdes log_assert(z->dclass == rrclass); 459238106Sdes if(z->type == local_zone_redirect && 460238106Sdes query_dname_compare(z->name, nm) != 0) { 461238106Sdes log_err("local-data in redirect zone must reside at top of zone" 462238106Sdes ", not at %s", rrstr); 463238106Sdes free(nm); 464238106Sdes return 0; 465238106Sdes } 466238106Sdes nmlabs = dname_count_size_labels(nm, &nmlen); 467238106Sdes if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) { 468238106Sdes free(nm); 469238106Sdes return 0; 470238106Sdes } 471238106Sdes log_assert(node); 472238106Sdes free(nm); 473238106Sdes 474238106Sdes rrset = local_data_find_type(node, rrtype); 475238106Sdes if(!rrset) { 476238106Sdes rrset = new_local_rrset(z->region, node, rrtype, rrclass); 477238106Sdes if(!rrset) 478238106Sdes return 0; 479238106Sdes if(query_dname_compare(node->name, z->name) == 0) { 480238106Sdes if(rrtype == LDNS_RR_TYPE_NSEC) 481238106Sdes rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX; 482238106Sdes if(rrtype == LDNS_RR_TYPE_SOA) 483238106Sdes z->soa = rrset->rrset; 484238106Sdes } 485238106Sdes } 486238106Sdes pd = (struct packed_rrset_data*)rrset->rrset->entry.data; 487238106Sdes log_assert(rrset && pd); 488238106Sdes 489238106Sdes /* check for duplicate RR */ 490238106Sdes if(rr_is_duplicate(pd, buf)) { 491238106Sdes verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr); 492238106Sdes return 1; 493238106Sdes } 494238106Sdes return insert_rr(z->region, pd, buf, ttl); 495238106Sdes} 496238106Sdes 497238106Sdes/** enter a data RR into auth data; a zone for it must exist */ 498238106Sdesstatic int 499238106Sdeslz_enter_rr_str(struct local_zones* zones, const char* rr, ldns_buffer* buf) 500238106Sdes{ 501238106Sdes uint8_t* rr_name; 502238106Sdes uint16_t rr_class; 503238106Sdes size_t len; 504238106Sdes int labs; 505238106Sdes struct local_zone* z; 506238106Sdes int r; 507238106Sdes if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { 508238106Sdes log_err("bad rr %s", rr); 509238106Sdes return 0; 510238106Sdes } 511238106Sdes labs = dname_count_size_labels(rr_name, &len); 512238106Sdes lock_quick_lock(&zones->lock); 513238106Sdes z = local_zones_lookup(zones, rr_name, len, labs, rr_class); 514238106Sdes if(!z) { 515238106Sdes lock_quick_unlock(&zones->lock); 516238106Sdes fatal_exit("internal error: no zone for rr %s", rr); 517238106Sdes } 518238106Sdes lock_rw_wrlock(&z->lock); 519238106Sdes lock_quick_unlock(&zones->lock); 520238106Sdes free(rr_name); 521238106Sdes r = lz_enter_rr_into_zone(z, buf, rr); 522238106Sdes lock_rw_unlock(&z->lock); 523238106Sdes return r; 524238106Sdes} 525238106Sdes 526238106Sdes/** parse local-zone: statements */ 527238106Sdesstatic int 528238106Sdeslz_enter_zones(struct local_zones* zones, struct config_file* cfg) 529238106Sdes{ 530238106Sdes struct config_str2list* p; 531238106Sdes struct local_zone* z; 532238106Sdes for(p = cfg->local_zones; p; p = p->next) { 533238106Sdes if(!(z=lz_enter_zone(zones, p->str, p->str2, 534238106Sdes LDNS_RR_CLASS_IN))) 535238106Sdes return 0; 536238106Sdes lock_rw_unlock(&z->lock); 537238106Sdes } 538238106Sdes return 1; 539238106Sdes} 540238106Sdes 541238106Sdes/** lookup a zone in rbtree; exact match only; SLOW due to parse */ 542238106Sdesstatic int 543238106Sdeslz_exists(struct local_zones* zones, const char* name) 544238106Sdes{ 545238106Sdes struct local_zone z; 546238106Sdes z.node.key = &z; 547238106Sdes z.dclass = LDNS_RR_CLASS_IN; 548238106Sdes if(!parse_dname(name, &z.name, &z.namelen, &z.namelabs)) { 549238106Sdes log_err("bad name %s", name); 550238106Sdes return 0; 551238106Sdes } 552238106Sdes lock_quick_lock(&zones->lock); 553238106Sdes if(rbtree_search(&zones->ztree, &z.node)) { 554238106Sdes lock_quick_unlock(&zones->lock); 555238106Sdes free(z.name); 556238106Sdes return 1; 557238106Sdes } 558238106Sdes lock_quick_unlock(&zones->lock); 559238106Sdes free(z.name); 560238106Sdes return 0; 561238106Sdes} 562238106Sdes 563238106Sdes/** lookup a zone in cfg->nodefault list */ 564238106Sdesstatic int 565238106Sdeslz_nodefault(struct config_file* cfg, const char* name) 566238106Sdes{ 567238106Sdes struct config_strlist* p; 568238106Sdes size_t len = strlen(name); 569238106Sdes if(len == 0) return 0; 570238106Sdes if(name[len-1] == '.') len--; 571238106Sdes 572238106Sdes for(p = cfg->local_zones_nodefault; p; p = p->next) { 573238106Sdes /* compare zone name, lowercase, compare without ending . */ 574238106Sdes if(strncasecmp(p->str, name, len) == 0 && 575238106Sdes (strlen(p->str) == len || (strlen(p->str)==len+1 && 576238106Sdes p->str[len] == '.'))) 577238106Sdes return 1; 578238106Sdes } 579238106Sdes return 0; 580238106Sdes} 581238106Sdes 582238106Sdes/** enter AS112 default zone */ 583238106Sdesstatic int 584238106Sdesadd_as112_default(struct local_zones* zones, struct config_file* cfg, 585238106Sdes ldns_buffer* buf, const char* name) 586238106Sdes{ 587238106Sdes struct local_zone* z; 588238106Sdes char str[1024]; /* known long enough */ 589238106Sdes if(lz_exists(zones, name) || lz_nodefault(cfg, name)) 590238106Sdes return 1; /* do not enter default content */ 591238106Sdes if(!(z=lz_enter_zone(zones, name, "static", LDNS_RR_CLASS_IN))) 592238106Sdes return 0; 593238106Sdes snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. " 594238106Sdes "nobody.invalid. 1 3600 1200 604800 10800", name); 595238106Sdes if(!lz_enter_rr_into_zone(z, buf, str)) { 596238106Sdes lock_rw_unlock(&z->lock); 597238106Sdes return 0; 598238106Sdes } 599238106Sdes snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name); 600238106Sdes if(!lz_enter_rr_into_zone(z, buf, str)) { 601238106Sdes lock_rw_unlock(&z->lock); 602238106Sdes return 0; 603238106Sdes } 604238106Sdes lock_rw_unlock(&z->lock); 605238106Sdes return 1; 606238106Sdes} 607238106Sdes 608238106Sdes/** enter default zones */ 609238106Sdesstatic int 610238106Sdeslz_enter_defaults(struct local_zones* zones, struct config_file* cfg, 611238106Sdes ldns_buffer* buf) 612238106Sdes{ 613238106Sdes struct local_zone* z; 614238106Sdes 615238106Sdes /* this list of zones is from RFC 6303 */ 616238106Sdes 617238106Sdes /* localhost. zone */ 618238106Sdes if(!lz_exists(zones, "localhost.") && 619238106Sdes !lz_nodefault(cfg, "localhost.")) { 620238106Sdes if(!(z=lz_enter_zone(zones, "localhost.", "static", 621238106Sdes LDNS_RR_CLASS_IN)) || 622238106Sdes !lz_enter_rr_into_zone(z, buf, 623238106Sdes "localhost. 10800 IN NS localhost.") || 624238106Sdes !lz_enter_rr_into_zone(z, buf, 625238106Sdes "localhost. 10800 IN SOA localhost. nobody.invalid. " 626238106Sdes "1 3600 1200 604800 10800") || 627238106Sdes !lz_enter_rr_into_zone(z, buf, 628238106Sdes "localhost. 10800 IN A 127.0.0.1") || 629238106Sdes !lz_enter_rr_into_zone(z, buf, 630238106Sdes "localhost. 10800 IN AAAA ::1")) { 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 ip4 zone */ 638238106Sdes if(!lz_exists(zones, "127.in-addr.arpa.") && 639238106Sdes !lz_nodefault(cfg, "127.in-addr.arpa.")) { 640238106Sdes if(!(z=lz_enter_zone(zones, "127.in-addr.arpa.", "static", 641238106Sdes LDNS_RR_CLASS_IN)) || 642238106Sdes !lz_enter_rr_into_zone(z, buf, 643238106Sdes "127.in-addr.arpa. 10800 IN NS localhost.") || 644238106Sdes !lz_enter_rr_into_zone(z, buf, 645238106Sdes "127.in-addr.arpa. 10800 IN SOA localhost. " 646238106Sdes "nobody.invalid. 1 3600 1200 604800 10800") || 647238106Sdes !lz_enter_rr_into_zone(z, buf, 648238106Sdes "1.0.0.127.in-addr.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 } 655238106Sdes /* reverse ip6 zone */ 656238106Sdes 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.") && 657238106Sdes !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.")) { 658238106Sdes 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", 659238106Sdes LDNS_RR_CLASS_IN)) || 660238106Sdes !lz_enter_rr_into_zone(z, buf, 661238106Sdes "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.") || 662238106Sdes !lz_enter_rr_into_zone(z, buf, 663238106Sdes "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. " 664238106Sdes "nobody.invalid. 1 3600 1200 604800 10800") || 665238106Sdes !lz_enter_rr_into_zone(z, buf, 666238106Sdes "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.")) { 667238106Sdes log_err("out of memory adding default zone"); 668238106Sdes if(z) { lock_rw_unlock(&z->lock); } 669238106Sdes return 0; 670238106Sdes } 671238106Sdes lock_rw_unlock(&z->lock); 672238106Sdes } 673238106Sdes if ( !add_as112_default(zones, cfg, buf, "10.in-addr.arpa.") || 674238106Sdes !add_as112_default(zones, cfg, buf, "16.172.in-addr.arpa.") || 675238106Sdes !add_as112_default(zones, cfg, buf, "17.172.in-addr.arpa.") || 676238106Sdes !add_as112_default(zones, cfg, buf, "18.172.in-addr.arpa.") || 677238106Sdes !add_as112_default(zones, cfg, buf, "19.172.in-addr.arpa.") || 678238106Sdes !add_as112_default(zones, cfg, buf, "20.172.in-addr.arpa.") || 679238106Sdes !add_as112_default(zones, cfg, buf, "21.172.in-addr.arpa.") || 680238106Sdes !add_as112_default(zones, cfg, buf, "22.172.in-addr.arpa.") || 681238106Sdes !add_as112_default(zones, cfg, buf, "23.172.in-addr.arpa.") || 682238106Sdes !add_as112_default(zones, cfg, buf, "24.172.in-addr.arpa.") || 683238106Sdes !add_as112_default(zones, cfg, buf, "25.172.in-addr.arpa.") || 684238106Sdes !add_as112_default(zones, cfg, buf, "26.172.in-addr.arpa.") || 685238106Sdes !add_as112_default(zones, cfg, buf, "27.172.in-addr.arpa.") || 686238106Sdes !add_as112_default(zones, cfg, buf, "28.172.in-addr.arpa.") || 687238106Sdes !add_as112_default(zones, cfg, buf, "29.172.in-addr.arpa.") || 688238106Sdes !add_as112_default(zones, cfg, buf, "30.172.in-addr.arpa.") || 689238106Sdes !add_as112_default(zones, cfg, buf, "31.172.in-addr.arpa.") || 690238106Sdes !add_as112_default(zones, cfg, buf, "168.192.in-addr.arpa.") || 691238106Sdes !add_as112_default(zones, cfg, buf, "0.in-addr.arpa.") || 692238106Sdes !add_as112_default(zones, cfg, buf, "254.169.in-addr.arpa.") || 693238106Sdes !add_as112_default(zones, cfg, buf, "2.0.192.in-addr.arpa.") || 694238106Sdes !add_as112_default(zones, cfg, buf, "100.51.198.in-addr.arpa.") || 695238106Sdes !add_as112_default(zones, cfg, buf, "113.0.203.in-addr.arpa.") || 696238106Sdes !add_as112_default(zones, cfg, buf, "255.255.255.255.in-addr.arpa.") || 697238106Sdes !add_as112_default(zones, cfg, buf, "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.") || 698238106Sdes !add_as112_default(zones, cfg, buf, "d.f.ip6.arpa.") || 699238106Sdes !add_as112_default(zones, cfg, buf, "8.e.f.ip6.arpa.") || 700238106Sdes !add_as112_default(zones, cfg, buf, "9.e.f.ip6.arpa.") || 701238106Sdes !add_as112_default(zones, cfg, buf, "a.e.f.ip6.arpa.") || 702238106Sdes !add_as112_default(zones, cfg, buf, "b.e.f.ip6.arpa.") || 703238106Sdes !add_as112_default(zones, cfg, buf, "8.b.d.0.1.0.0.2.ip6.arpa.")) { 704238106Sdes log_err("out of memory adding default zone"); 705238106Sdes return 0; 706238106Sdes } 707238106Sdes return 1; 708238106Sdes} 709238106Sdes 710238106Sdes/** setup parent pointers, so that a lookup can be done for closest match */ 711238106Sdesstatic void 712238106Sdesinit_parents(struct local_zones* zones) 713238106Sdes{ 714238106Sdes struct local_zone* node, *prev = NULL, *p; 715238106Sdes int m; 716238106Sdes lock_quick_lock(&zones->lock); 717238106Sdes RBTREE_FOR(node, struct local_zone*, &zones->ztree) { 718238106Sdes lock_rw_wrlock(&node->lock); 719238106Sdes node->parent = NULL; 720238106Sdes if(!prev || prev->dclass != node->dclass) { 721238106Sdes prev = node; 722238106Sdes lock_rw_unlock(&node->lock); 723238106Sdes continue; 724238106Sdes } 725238106Sdes (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, 726238106Sdes node->namelabs, &m); /* we know prev is smaller */ 727238106Sdes /* sort order like: . com. bla.com. zwb.com. net. */ 728238106Sdes /* find the previous, or parent-parent-parent */ 729238106Sdes for(p = prev; p; p = p->parent) 730238106Sdes /* looking for name with few labels, a parent */ 731238106Sdes if(p->namelabs <= m) { 732238106Sdes /* ==: since prev matched m, this is closest*/ 733238106Sdes /* <: prev matches more, but is not a parent, 734238106Sdes * this one is a (grand)parent */ 735238106Sdes node->parent = p; 736238106Sdes break; 737238106Sdes } 738238106Sdes prev = node; 739238106Sdes lock_rw_unlock(&node->lock); 740238106Sdes } 741238106Sdes lock_quick_unlock(&zones->lock); 742238106Sdes} 743238106Sdes 744238106Sdes/** enter implicit transparent zone for local-data: without local-zone: */ 745238106Sdesstatic int 746238106Sdeslz_setup_implicit(struct local_zones* zones, struct config_file* cfg) 747238106Sdes{ 748238106Sdes /* walk over all items that have no parent zone and find 749238106Sdes * the name that covers them all (could be the root) and 750238106Sdes * add that as a transparent zone */ 751238106Sdes struct config_strlist* p; 752238106Sdes int have_name = 0; 753238106Sdes int have_other_classes = 0; 754238106Sdes uint16_t dclass = 0; 755238106Sdes uint8_t* nm = 0; 756238106Sdes size_t nmlen = 0; 757238106Sdes int nmlabs = 0; 758238106Sdes int match = 0; /* number of labels match count */ 759238106Sdes 760238106Sdes init_parents(zones); /* to enable local_zones_lookup() */ 761238106Sdes for(p = cfg->local_data; p; p = p->next) { 762238106Sdes uint8_t* rr_name; 763238106Sdes uint16_t rr_class; 764238106Sdes size_t len; 765238106Sdes int labs; 766238106Sdes if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) { 767238106Sdes log_err("Bad local-data RR %s", p->str); 768238106Sdes return 0; 769238106Sdes } 770238106Sdes labs = dname_count_size_labels(rr_name, &len); 771238106Sdes lock_quick_lock(&zones->lock); 772238106Sdes if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) { 773238106Sdes if(!have_name) { 774238106Sdes dclass = rr_class; 775238106Sdes nm = rr_name; 776238106Sdes nmlen = len; 777238106Sdes nmlabs = labs; 778238106Sdes match = labs; 779238106Sdes have_name = 1; 780238106Sdes } else { 781238106Sdes int m; 782238106Sdes if(rr_class != dclass) { 783238106Sdes /* process other classes later */ 784238106Sdes free(rr_name); 785238106Sdes have_other_classes = 1; 786238106Sdes lock_quick_unlock(&zones->lock); 787238106Sdes continue; 788238106Sdes } 789238106Sdes /* find smallest shared topdomain */ 790238106Sdes (void)dname_lab_cmp(nm, nmlabs, 791238106Sdes rr_name, labs, &m); 792238106Sdes free(rr_name); 793238106Sdes if(m < match) 794238106Sdes match = m; 795238106Sdes } 796238106Sdes } else free(rr_name); 797238106Sdes lock_quick_unlock(&zones->lock); 798238106Sdes } 799238106Sdes if(have_name) { 800238106Sdes uint8_t* n2; 801238106Sdes struct local_zone* z; 802238106Sdes /* allocate zone of smallest shared topdomain to contain em */ 803238106Sdes n2 = nm; 804238106Sdes dname_remove_labels(&n2, &nmlen, nmlabs - match); 805238106Sdes n2 = memdup(n2, nmlen); 806238106Sdes free(nm); 807238106Sdes if(!n2) { 808238106Sdes log_err("out of memory"); 809238106Sdes return 0; 810238106Sdes } 811238106Sdes log_nametypeclass(VERB_ALGO, "implicit transparent local-zone", 812238106Sdes n2, 0, dclass); 813238106Sdes if(!(z=lz_enter_zone_dname(zones, n2, nmlen, match, 814238106Sdes local_zone_transparent, dclass))) { 815238106Sdes return 0; 816238106Sdes } 817238106Sdes lock_rw_unlock(&z->lock); 818238106Sdes } 819238106Sdes if(have_other_classes) { 820238106Sdes /* restart to setup other class */ 821238106Sdes return lz_setup_implicit(zones, cfg); 822238106Sdes } 823238106Sdes return 1; 824238106Sdes} 825238106Sdes 826238106Sdes/** enter auth data */ 827238106Sdesstatic int 828238106Sdeslz_enter_data(struct local_zones* zones, struct config_file* cfg, 829238106Sdes ldns_buffer* buf) 830238106Sdes{ 831238106Sdes struct config_strlist* p; 832238106Sdes for(p = cfg->local_data; p; p = p->next) { 833238106Sdes if(!lz_enter_rr_str(zones, p->str, buf)) 834238106Sdes return 0; 835238106Sdes } 836238106Sdes return 1; 837238106Sdes} 838238106Sdes 839238106Sdes/** free memory from config */ 840238106Sdesstatic void 841238106Sdeslz_freeup_cfg(struct config_file* cfg) 842238106Sdes{ 843238106Sdes config_deldblstrlist(cfg->local_zones); 844238106Sdes cfg->local_zones = NULL; 845238106Sdes config_delstrlist(cfg->local_zones_nodefault); 846238106Sdes cfg->local_zones_nodefault = NULL; 847238106Sdes config_delstrlist(cfg->local_data); 848238106Sdes cfg->local_data = NULL; 849238106Sdes} 850238106Sdes 851238106Sdesint 852238106Sdeslocal_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg) 853238106Sdes{ 854238106Sdes ldns_buffer* buf = ldns_buffer_new(65535); 855238106Sdes if(!buf) fatal_exit("cannot create temporary buffer"); 856238106Sdes 857238106Sdes /* create zones from zone statements. */ 858238106Sdes if(!lz_enter_zones(zones, cfg)) { 859238106Sdes ldns_buffer_free(buf); 860238106Sdes return 0; 861238106Sdes } 862238106Sdes /* apply default zones+content (unless disabled, or overridden) */ 863238106Sdes if(!lz_enter_defaults(zones, cfg, buf)) { 864238106Sdes ldns_buffer_free(buf); 865238106Sdes return 0; 866238106Sdes } 867238106Sdes /* create implicit transparent zone from data. */ 868238106Sdes if(!lz_setup_implicit(zones, cfg)) { 869238106Sdes ldns_buffer_free(buf); 870238106Sdes return 0; 871238106Sdes } 872238106Sdes 873238106Sdes /* setup parent ptrs for lookup during data entry */ 874238106Sdes init_parents(zones); 875238106Sdes /* insert local data */ 876238106Sdes if(!lz_enter_data(zones, cfg, buf)) { 877238106Sdes ldns_buffer_free(buf); 878238106Sdes return 0; 879238106Sdes } 880238106Sdes /* freeup memory from cfg struct. */ 881238106Sdes lz_freeup_cfg(cfg); 882238106Sdes ldns_buffer_free(buf); 883238106Sdes return 1; 884238106Sdes} 885238106Sdes 886238106Sdesstruct local_zone* 887238106Sdeslocal_zones_lookup(struct local_zones* zones, 888238106Sdes uint8_t* name, size_t len, int labs, uint16_t dclass) 889238106Sdes{ 890238106Sdes rbnode_t* res = NULL; 891238106Sdes struct local_zone *result; 892238106Sdes struct local_zone key; 893238106Sdes key.node.key = &key; 894238106Sdes key.dclass = dclass; 895238106Sdes key.name = name; 896238106Sdes key.namelen = len; 897238106Sdes key.namelabs = labs; 898238106Sdes if(rbtree_find_less_equal(&zones->ztree, &key, &res)) { 899238106Sdes /* exact */ 900238106Sdes return (struct local_zone*)res; 901238106Sdes } else { 902238106Sdes /* smaller element (or no element) */ 903238106Sdes int m; 904238106Sdes result = (struct local_zone*)res; 905238106Sdes if(!result || result->dclass != dclass) 906238106Sdes return NULL; 907238106Sdes /* count number of labels matched */ 908238106Sdes (void)dname_lab_cmp(result->name, result->namelabs, key.name, 909238106Sdes key.namelabs, &m); 910238106Sdes while(result) { /* go up until qname is subdomain of zone */ 911238106Sdes if(result->namelabs <= m) 912238106Sdes break; 913238106Sdes result = result->parent; 914238106Sdes } 915238106Sdes return result; 916238106Sdes } 917238106Sdes} 918238106Sdes 919238106Sdesstruct local_zone* 920238106Sdeslocal_zones_find(struct local_zones* zones, 921238106Sdes uint8_t* name, size_t len, int labs, uint16_t dclass) 922238106Sdes{ 923238106Sdes struct local_zone key; 924238106Sdes key.node.key = &key; 925238106Sdes key.dclass = dclass; 926238106Sdes key.name = name; 927238106Sdes key.namelen = len; 928238106Sdes key.namelabs = labs; 929238106Sdes /* exact */ 930238106Sdes return (struct local_zone*)rbtree_search(&zones->ztree, &key); 931238106Sdes} 932238106Sdes 933238106Sdes/** print all RRsets in local zone */ 934238106Sdesstatic void 935238106Sdeslocal_zone_out(struct local_zone* z) 936238106Sdes{ 937238106Sdes struct local_data* d; 938238106Sdes struct local_rrset* p; 939238106Sdes RBTREE_FOR(d, struct local_data*, &z->data) { 940238106Sdes for(p = d->rrsets; p; p = p->next) { 941238106Sdes log_nametypeclass(0, "rrset", d->name, 942238106Sdes ntohs(p->rrset->rk.type), 943238106Sdes ntohs(p->rrset->rk.rrset_class)); 944238106Sdes } 945238106Sdes } 946238106Sdes} 947238106Sdes 948238106Sdesvoid local_zones_print(struct local_zones* zones) 949238106Sdes{ 950238106Sdes struct local_zone* z; 951238106Sdes lock_quick_lock(&zones->lock); 952238106Sdes log_info("number of auth zones %u", (unsigned)zones->ztree.count); 953238106Sdes RBTREE_FOR(z, struct local_zone*, &zones->ztree) { 954238106Sdes lock_rw_rdlock(&z->lock); 955238106Sdes switch(z->type) { 956238106Sdes case local_zone_deny: 957238106Sdes log_nametypeclass(0, "deny zone", 958238106Sdes z->name, 0, z->dclass); 959238106Sdes break; 960238106Sdes case local_zone_refuse: 961238106Sdes log_nametypeclass(0, "refuse zone", 962238106Sdes z->name, 0, z->dclass); 963238106Sdes break; 964238106Sdes case local_zone_redirect: 965238106Sdes log_nametypeclass(0, "redirect zone", 966238106Sdes z->name, 0, z->dclass); 967238106Sdes break; 968238106Sdes case local_zone_transparent: 969238106Sdes log_nametypeclass(0, "transparent zone", 970238106Sdes z->name, 0, z->dclass); 971238106Sdes break; 972238106Sdes case local_zone_typetransparent: 973238106Sdes log_nametypeclass(0, "typetransparent zone", 974238106Sdes z->name, 0, z->dclass); 975238106Sdes break; 976238106Sdes case local_zone_static: 977238106Sdes log_nametypeclass(0, "static zone", 978238106Sdes z->name, 0, z->dclass); 979238106Sdes break; 980238106Sdes default: 981238106Sdes log_nametypeclass(0, "badtyped zone", 982238106Sdes z->name, 0, z->dclass); 983238106Sdes break; 984238106Sdes } 985238106Sdes local_zone_out(z); 986238106Sdes lock_rw_unlock(&z->lock); 987238106Sdes } 988238106Sdes lock_quick_unlock(&zones->lock); 989238106Sdes} 990238106Sdes 991238106Sdes/** encode answer consisting of 1 rrset */ 992238106Sdesstatic int 993238106Sdeslocal_encode(struct query_info* qinfo, struct edns_data* edns, 994238106Sdes ldns_buffer* buf, struct regional* temp, 995238106Sdes struct ub_packed_rrset_key* rrset, int ansec, int rcode) 996238106Sdes{ 997238106Sdes struct reply_info rep; 998238106Sdes uint16_t udpsize; 999238106Sdes /* make answer with time=0 for fixed TTL values */ 1000238106Sdes memset(&rep, 0, sizeof(rep)); 1001238106Sdes rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); 1002238106Sdes rep.qdcount = 1; 1003238106Sdes if(ansec) 1004238106Sdes rep.an_numrrsets = 1; 1005238106Sdes else rep.ns_numrrsets = 1; 1006238106Sdes rep.rrset_count = 1; 1007238106Sdes rep.rrsets = &rrset; 1008238106Sdes udpsize = edns->udp_size; 1009238106Sdes edns->edns_version = EDNS_ADVERTISED_VERSION; 1010238106Sdes edns->udp_size = EDNS_ADVERTISED_SIZE; 1011238106Sdes edns->ext_rcode = 0; 1012238106Sdes edns->bits &= EDNS_DO; 1013238106Sdes if(!reply_info_answer_encode(qinfo, &rep, 1014238106Sdes *(uint16_t*)ldns_buffer_begin(buf), 1015238106Sdes ldns_buffer_read_u16_at(buf, 2), 1016238106Sdes buf, 0, 0, temp, udpsize, edns, 1017238106Sdes (int)(edns->bits&EDNS_DO), 0)) 1018238106Sdes error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, 1019238106Sdes *(uint16_t*)ldns_buffer_begin(buf), 1020238106Sdes ldns_buffer_read_u16_at(buf, 2), edns); 1021238106Sdes return 1; 1022238106Sdes} 1023238106Sdes 1024238106Sdes/** answer local data match */ 1025238106Sdesstatic int 1026238106Sdeslocal_data_answer(struct local_zone* z, struct query_info* qinfo, 1027238106Sdes struct edns_data* edns, ldns_buffer* buf, struct regional* temp, 1028238106Sdes int labs, struct local_data** ldp) 1029238106Sdes{ 1030238106Sdes struct local_data key; 1031238106Sdes struct local_data* ld; 1032238106Sdes struct local_rrset* lr; 1033238106Sdes key.node.key = &key; 1034238106Sdes key.name = qinfo->qname; 1035238106Sdes key.namelen = qinfo->qname_len; 1036238106Sdes key.namelabs = labs; 1037238106Sdes if(z->type == local_zone_redirect) { 1038238106Sdes key.name = z->name; 1039238106Sdes key.namelen = z->namelen; 1040238106Sdes key.namelabs = z->namelabs; 1041238106Sdes } 1042238106Sdes ld = (struct local_data*)rbtree_search(&z->data, &key.node); 1043238106Sdes *ldp = ld; 1044238106Sdes if(!ld) { 1045238106Sdes return 0; 1046238106Sdes } 1047238106Sdes lr = local_data_find_type(ld, qinfo->qtype); 1048238106Sdes if(!lr) 1049238106Sdes return 0; 1050238106Sdes if(z->type == local_zone_redirect) { 1051238106Sdes /* convert rrset name to query name; like a wildcard */ 1052238106Sdes struct ub_packed_rrset_key r = *lr->rrset; 1053238106Sdes r.rk.dname = qinfo->qname; 1054238106Sdes r.rk.dname_len = qinfo->qname_len; 1055238106Sdes return local_encode(qinfo, edns, buf, temp, &r, 1, 1056238106Sdes LDNS_RCODE_NOERROR); 1057238106Sdes } 1058238106Sdes return local_encode(qinfo, edns, buf, temp, lr->rrset, 1, 1059238106Sdes LDNS_RCODE_NOERROR); 1060238106Sdes} 1061238106Sdes 1062238106Sdes/** 1063238106Sdes * answer in case where no exact match is found 1064238106Sdes * @param z: zone for query 1065238106Sdes * @param qinfo: query 1066238106Sdes * @param edns: edns from query 1067238106Sdes * @param buf: buffer for answer. 1068238106Sdes * @param temp: temp region for encoding 1069238106Sdes * @param ld: local data, if NULL, no such name exists in localdata. 1070238106Sdes * @return 1 if a reply is to be sent, 0 if not. 1071238106Sdes */ 1072238106Sdesstatic int 1073238106Sdeslz_zone_answer(struct local_zone* z, struct query_info* qinfo, 1074238106Sdes struct edns_data* edns, ldns_buffer* buf, struct regional* temp, 1075238106Sdes struct local_data* ld) 1076238106Sdes{ 1077238106Sdes if(z->type == local_zone_deny) { 1078238106Sdes /** no reply at all, signal caller by clearing buffer. */ 1079238106Sdes ldns_buffer_clear(buf); 1080238106Sdes ldns_buffer_flip(buf); 1081238106Sdes return 1; 1082238106Sdes } else if(z->type == local_zone_refuse) { 1083238106Sdes error_encode(buf, (LDNS_RCODE_REFUSED|BIT_AA), qinfo, 1084238106Sdes *(uint16_t*)ldns_buffer_begin(buf), 1085238106Sdes ldns_buffer_read_u16_at(buf, 2), edns); 1086238106Sdes return 1; 1087238106Sdes } else if(z->type == local_zone_static || 1088238106Sdes z->type == local_zone_redirect) { 1089238106Sdes /* for static, reply nodata or nxdomain 1090238106Sdes * for redirect, reply nodata */ 1091238106Sdes /* no additional section processing, 1092238106Sdes * cname, dname or wildcard processing, 1093238106Sdes * or using closest match for NSEC. 1094238106Sdes * or using closest match for returning delegation downwards 1095238106Sdes */ 1096238106Sdes int rcode = ld?LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN; 1097238106Sdes if(z->soa) 1098238106Sdes return local_encode(qinfo, edns, buf, temp, 1099238106Sdes z->soa, 0, rcode); 1100238106Sdes error_encode(buf, (rcode|BIT_AA), qinfo, 1101238106Sdes *(uint16_t*)ldns_buffer_begin(buf), 1102238106Sdes ldns_buffer_read_u16_at(buf, 2), edns); 1103238106Sdes return 1; 1104238106Sdes } else if(z->type == local_zone_typetransparent) { 1105238106Sdes /* no NODATA or NXDOMAINS for this zone type */ 1106238106Sdes return 0; 1107238106Sdes } 1108238106Sdes /* else z->type == local_zone_transparent */ 1109238106Sdes 1110238106Sdes /* if the zone is transparent and the name exists, but the type 1111238106Sdes * does not, then we should make this noerror/nodata */ 1112238106Sdes if(ld && ld->rrsets) { 1113238106Sdes int rcode = LDNS_RCODE_NOERROR; 1114238106Sdes if(z->soa) 1115238106Sdes return local_encode(qinfo, edns, buf, temp, 1116238106Sdes z->soa, 0, rcode); 1117238106Sdes error_encode(buf, (rcode|BIT_AA), qinfo, 1118238106Sdes *(uint16_t*)ldns_buffer_begin(buf), 1119238106Sdes ldns_buffer_read_u16_at(buf, 2), edns); 1120238106Sdes return 1; 1121238106Sdes } 1122238106Sdes 1123238106Sdes /* stop here, and resolve further on */ 1124238106Sdes return 0; 1125238106Sdes} 1126238106Sdes 1127238106Sdesint 1128238106Sdeslocal_zones_answer(struct local_zones* zones, struct query_info* qinfo, 1129238106Sdes struct edns_data* edns, ldns_buffer* buf, struct regional* temp) 1130238106Sdes{ 1131238106Sdes /* see if query is covered by a zone, 1132238106Sdes * if so: - try to match (exact) local data 1133238106Sdes * - look at zone type for negative response. */ 1134238106Sdes int labs = dname_count_labels(qinfo->qname); 1135238106Sdes struct local_data* ld; 1136238106Sdes struct local_zone* z; 1137238106Sdes int r; 1138238106Sdes lock_quick_lock(&zones->lock); 1139238106Sdes z = local_zones_lookup(zones, qinfo->qname, 1140238106Sdes qinfo->qname_len, labs, qinfo->qclass); 1141238106Sdes if(!z) { 1142238106Sdes lock_quick_unlock(&zones->lock); 1143238106Sdes return 0; 1144238106Sdes } 1145238106Sdes lock_rw_rdlock(&z->lock); 1146238106Sdes lock_quick_unlock(&zones->lock); 1147238106Sdes 1148238106Sdes if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld)) { 1149238106Sdes lock_rw_unlock(&z->lock); 1150238106Sdes return 1; 1151238106Sdes } 1152238106Sdes r = lz_zone_answer(z, qinfo, edns, buf, temp, ld); 1153238106Sdes lock_rw_unlock(&z->lock); 1154238106Sdes return r; 1155238106Sdes} 1156238106Sdes 1157238106Sdesconst char* local_zone_type2str(enum localzone_type t) 1158238106Sdes{ 1159238106Sdes switch(t) { 1160238106Sdes case local_zone_deny: return "deny"; 1161238106Sdes case local_zone_refuse: return "refuse"; 1162238106Sdes case local_zone_redirect: return "redirect"; 1163238106Sdes case local_zone_transparent: return "transparent"; 1164238106Sdes case local_zone_typetransparent: return "typetransparent"; 1165238106Sdes case local_zone_static: return "static"; 1166238106Sdes case local_zone_nodefault: return "nodefault"; 1167238106Sdes } 1168238106Sdes return "badtyped"; 1169238106Sdes} 1170238106Sdes 1171238106Sdesint local_zone_str2type(const char* type, enum localzone_type* t) 1172238106Sdes{ 1173238106Sdes if(strcmp(type, "deny") == 0) 1174238106Sdes *t = local_zone_deny; 1175238106Sdes else if(strcmp(type, "refuse") == 0) 1176238106Sdes *t = local_zone_refuse; 1177238106Sdes else if(strcmp(type, "static") == 0) 1178238106Sdes *t = local_zone_static; 1179238106Sdes else if(strcmp(type, "transparent") == 0) 1180238106Sdes *t = local_zone_transparent; 1181238106Sdes else if(strcmp(type, "typetransparent") == 0) 1182238106Sdes *t = local_zone_typetransparent; 1183238106Sdes else if(strcmp(type, "redirect") == 0) 1184238106Sdes *t = local_zone_redirect; 1185238106Sdes else return 0; 1186238106Sdes return 1; 1187238106Sdes} 1188238106Sdes 1189238106Sdes/** iterate over the kiddies of the given name and set their parent ptr */ 1190238106Sdesstatic void 1191238106Sdesset_kiddo_parents(struct local_zone* z, struct local_zone* match, 1192238106Sdes struct local_zone* newp) 1193238106Sdes{ 1194238106Sdes /* both zones and z are locked already */ 1195238106Sdes /* in the sorted rbtree, the kiddies of z are located after z */ 1196238106Sdes /* z must be present in the tree */ 1197238106Sdes struct local_zone* p = z; 1198238106Sdes p = (struct local_zone*)rbtree_next(&p->node); 1199238106Sdes while(p!=(struct local_zone*)RBTREE_NULL && 1200238106Sdes p->dclass == z->dclass && dname_strict_subdomain(p->name, 1201238106Sdes p->namelabs, z->name, z->namelabs)) { 1202238106Sdes /* update parent ptr */ 1203238106Sdes /* only when matches with existing parent pointer, so that 1204238106Sdes * deeper child structures are not touched, i.e. 1205238106Sdes * update of x, and a.x, b.x, f.b.x, g.b.x, c.x, y 1206238106Sdes * gets to update a.x, b.x and c.x */ 1207238106Sdes lock_rw_wrlock(&p->lock); 1208238106Sdes if(p->parent == match) 1209238106Sdes p->parent = newp; 1210238106Sdes lock_rw_unlock(&p->lock); 1211238106Sdes p = (struct local_zone*)rbtree_next(&p->node); 1212238106Sdes } 1213238106Sdes} 1214238106Sdes 1215238106Sdesstruct local_zone* local_zones_add_zone(struct local_zones* zones, 1216238106Sdes uint8_t* name, size_t len, int labs, uint16_t dclass, 1217238106Sdes enum localzone_type tp) 1218238106Sdes{ 1219238106Sdes /* create */ 1220238106Sdes struct local_zone* z = local_zone_create(name, len, labs, tp, dclass); 1221238106Sdes if(!z) return NULL; 1222238106Sdes lock_rw_wrlock(&z->lock); 1223238106Sdes 1224238106Sdes /* find the closest parent */ 1225238106Sdes z->parent = local_zones_find(zones, name, len, labs, dclass); 1226238106Sdes 1227238106Sdes /* insert into the tree */ 1228238106Sdes if(!rbtree_insert(&zones->ztree, &z->node)) { 1229238106Sdes /* duplicate entry! */ 1230238106Sdes lock_rw_unlock(&z->lock); 1231238106Sdes local_zone_delete(z); 1232238106Sdes log_err("internal: duplicate entry in local_zones_add_zone"); 1233238106Sdes return NULL; 1234238106Sdes } 1235238106Sdes 1236238106Sdes /* set parent pointers right */ 1237238106Sdes set_kiddo_parents(z, z->parent, z); 1238238106Sdes 1239238106Sdes lock_rw_unlock(&z->lock); 1240238106Sdes return z; 1241238106Sdes} 1242238106Sdes 1243238106Sdesvoid local_zones_del_zone(struct local_zones* zones, struct local_zone* z) 1244238106Sdes{ 1245238106Sdes /* fix up parents in tree */ 1246238106Sdes lock_rw_wrlock(&z->lock); 1247238106Sdes set_kiddo_parents(z, z, z->parent); 1248238106Sdes 1249238106Sdes /* remove from tree */ 1250238106Sdes (void)rbtree_delete(&zones->ztree, z); 1251238106Sdes 1252238106Sdes /* delete the zone */ 1253238106Sdes lock_rw_unlock(&z->lock); 1254238106Sdes local_zone_delete(z); 1255238106Sdes} 1256238106Sdes 1257238106Sdesint 1258238106Sdeslocal_zones_add_RR(struct local_zones* zones, const char* rr, ldns_buffer* buf) 1259238106Sdes{ 1260238106Sdes uint8_t* rr_name; 1261238106Sdes uint16_t rr_class; 1262238106Sdes size_t len; 1263238106Sdes int labs; 1264238106Sdes struct local_zone* z; 1265238106Sdes int r; 1266238106Sdes if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { 1267238106Sdes return 0; 1268238106Sdes } 1269238106Sdes labs = dname_count_size_labels(rr_name, &len); 1270238106Sdes lock_quick_lock(&zones->lock); 1271238106Sdes z = local_zones_lookup(zones, rr_name, len, labs, rr_class); 1272238106Sdes if(!z) { 1273238106Sdes z = local_zones_add_zone(zones, rr_name, len, labs, rr_class, 1274238106Sdes local_zone_transparent); 1275238106Sdes if(!z) { 1276238106Sdes lock_quick_unlock(&zones->lock); 1277238106Sdes return 0; 1278238106Sdes } 1279238106Sdes } else { 1280238106Sdes free(rr_name); 1281238106Sdes } 1282238106Sdes lock_rw_wrlock(&z->lock); 1283238106Sdes lock_quick_unlock(&zones->lock); 1284238106Sdes r = lz_enter_rr_into_zone(z, buf, rr); 1285238106Sdes lock_rw_unlock(&z->lock); 1286238106Sdes return r; 1287238106Sdes} 1288238106Sdes 1289238106Sdes/** returns true if the node is terminal so no deeper domain names exist */ 1290238106Sdesstatic int 1291238106Sdesis_terminal(struct local_data* d) 1292238106Sdes{ 1293238106Sdes /* for empty nonterminals, the deeper domain names are sorted 1294238106Sdes * right after them, so simply check the next name in the tree 1295238106Sdes */ 1296238106Sdes struct local_data* n = (struct local_data*)rbtree_next(&d->node); 1297238106Sdes if(n == (struct local_data*)RBTREE_NULL) 1298238106Sdes return 1; /* last in tree, no deeper node */ 1299238106Sdes if(dname_strict_subdomain(n->name, n->namelabs, d->name, d->namelabs)) 1300238106Sdes return 0; /* there is a deeper node */ 1301238106Sdes return 1; 1302238106Sdes} 1303238106Sdes 1304238106Sdes/** delete empty terminals from tree when final data is deleted */ 1305238106Sdesstatic void 1306238106Sdesdel_empty_term(struct local_zone* z, struct local_data* d, 1307238106Sdes uint8_t* name, size_t len, int labs) 1308238106Sdes{ 1309238106Sdes while(d && d->rrsets == NULL && is_terminal(d)) { 1310238106Sdes /* is this empty nonterminal? delete */ 1311238106Sdes /* note, no memory recycling in zone region */ 1312238106Sdes (void)rbtree_delete(&z->data, d); 1313238106Sdes 1314238106Sdes /* go up and to the next label */ 1315238106Sdes if(dname_is_root(name)) 1316238106Sdes return; 1317238106Sdes dname_remove_label(&name, &len); 1318238106Sdes labs--; 1319238106Sdes d = lz_find_node(z, name, len, labs); 1320238106Sdes } 1321238106Sdes} 1322238106Sdes 1323238106Sdesvoid local_zones_del_data(struct local_zones* zones, 1324238106Sdes uint8_t* name, size_t len, int labs, uint16_t dclass) 1325238106Sdes{ 1326238106Sdes /* find zone */ 1327238106Sdes struct local_zone* z; 1328238106Sdes struct local_data* d; 1329238106Sdes lock_quick_lock(&zones->lock); 1330238106Sdes z = local_zones_lookup(zones, name, len, labs, dclass); 1331238106Sdes if(!z) { 1332238106Sdes /* no such zone, we're done */ 1333238106Sdes lock_quick_unlock(&zones->lock); 1334238106Sdes return; 1335238106Sdes } 1336238106Sdes lock_rw_wrlock(&z->lock); 1337238106Sdes lock_quick_unlock(&zones->lock); 1338238106Sdes 1339238106Sdes /* find the domain */ 1340238106Sdes d = lz_find_node(z, name, len, labs); 1341238106Sdes if(d) { 1342238106Sdes /* no memory recycling for zone deletions ... */ 1343238106Sdes d->rrsets = NULL; 1344238106Sdes /* did we delete the soa record ? */ 1345238106Sdes if(query_dname_compare(d->name, z->name) == 0) 1346238106Sdes z->soa = NULL; 1347238106Sdes 1348238106Sdes /* cleanup the empty nonterminals for this name */ 1349238106Sdes del_empty_term(z, d, name, len, labs); 1350238106Sdes } 1351238106Sdes 1352238106Sdes lock_rw_unlock(&z->lock); 1353238106Sdes} 1354