]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
localzone internal data structures.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 21 Nov 2007 16:19:31 +0000 (16:19 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 21 Nov 2007 16:19:31 +0000 (16:19 +0000)
git-svn-id: file:///svn/unbound/trunk@773 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
doc/unbound.conf.5
services/localzone.c
services/localzone.h
util/fptr_wlist.c

index acb3aa8af6d29009ec75cb81f55a54aea92a193a..1b081c9c4a6b36a68ba6b950c9e7e71d658a43fd 100644 (file)
@@ -1,3 +1,6 @@
+21 November 2007: Wouter
+       - local zone internal data setup.
+
 20 November 2007: Wouter
        - 0.8 - str2list config support for double string config options.
        - local-zone and local-data options, config storage and documentation.
index a904a797c4efd1609bb3203172cd1b2827cece77..97ccc627a4481e74bd9d126b321b8565d7c16d5f 100644 (file)
@@ -394,19 +394,19 @@ local-data: "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
 local-data: "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."
 .fi
 .It \fIreverse RFC1918 local use zones\fR 
-Reverse data for zones 10.IN-ADDR.ARPA, 16.172.IN-ADDR.ARPA to 
-31.172.IN-ADDR.ARPA, 168.192.IN-ADDR.ARPA.
+Reverse data for zones 10.in-addr.arpa, 16.172.in-addr.arpa to 
+31.172.in-addr.arpa, 168.192.in-addr.arpa.
 The \fBlocal-zone:\fR is set static and as \fBlocal-data:\fR SOA and NS 
 records are provided.
 .It \fIreverse RFC3330 IP4 this, link-local, testnet and broadcast\fR 
-Reverse data for zones 0.IN-ADDR.ARPA, 254.169.IN-ADDR.ARPA
-2.0.192.IN-ADDR.ARPA, 255.255.255.255.IN-ADDR.ARPA.
+Reverse data for zones 0.in-addr.arpa, 254.169.in-addr.arpa
+2.0.192.in-addr.arpa, 255.255.255.255.in-addr.arpa.
 .It \fIreverse RFC4291 IP6 unspecified\fR
-Reverse data for zone 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.
+Reverse data for zone 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.
 .It \fIreverse RFC4193 IPv6 Locally Assigned Local Addresses\fR
-Reverse data for zone D.F.IP6.ARPA.
+Reverse data for zone D.F.ip6.arpa.
 .It \fIreverse RFC4291 IPv6 Link Local Addresses\fR
-Reverse data for zones 8.E.F.IP6.ARPA to B.E.F.IP6.ARPA.
+Reverse data for zones 8.E.F.ip6.arpa to B.E.F.ip6.arpa.
 .El
 .\" End of local-zone listing.
 .It \fBlocal-data:\fR "<resource record string>"
index f27e226417d3c6d49d964c78e4a36a6e48455e2f..2051771178d67a7a50e178157b70a8b6d3dca306 100644 (file)
  */
 #include "config.h"
 #include "services/localzone.h"
+#include "util/regional.h"
+#include "util/config_file.h"
+#include "util/data/dname.h"
+#include "util/data/packed_rrset.h"
+#include "util/net_help.h"
 
+struct local_zones* 
+local_zones_create()
+{
+       struct local_zones* zones = (struct local_zones*)calloc(1, 
+               sizeof(*zones));
+       if(!zones)
+               return NULL;
+       rbtree_init(&zones->ztree, &local_zone_cmp);
+       return zones;
+}
+
+/** helper traverse to delete zones */
+static void 
+lzdel(rbnode_t* n, void* ATTR_UNUSED(arg))
+{
+       struct local_zone* z = (struct local_zone*)n->key;
+       local_zone_delete(z);
+}
+
+void 
+local_zones_delete(struct local_zones* zones)
+{
+       if(!zones)
+               return;
+       /* walk through zones and delete them all */
+       traverse_postorder(&zones->ztree, lzdel, NULL);
+       free(zones);
+}
+
+void 
+local_zone_delete(struct local_zone* z)
+{
+       if(!z)
+               return;
+       regional_destroy(z->region);
+       free(z->name);
+       free(z);
+}
+
+int 
+local_zone_cmp(const void* z1, const void* z2)
+{
+       /* first sort on class, so that hierarchy can be maintained within
+        * a class */
+       struct local_zone* a = (struct local_zone*)z1;
+       struct local_zone* b = (struct local_zone*)z2;
+       int m;
+       if(a->dclass != b->dclass) {
+               if(a->dclass < b->dclass)
+                       return -1;
+               return 1;
+       }
+       return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m);
+}
+
+int 
+local_data_cmp(const void* d1, const void* d2)
+{
+       struct local_data* a = (struct local_data*)d1;
+       struct local_data* b = (struct local_data*)d2;
+       int m;
+       return dname_canon_lab_cmp(a->name, a->namelabs, b->name, 
+               b->namelabs, &m);
+}
+
+/** form wireformat from text format domain name */
+static int
+parse_dname(const char* str, uint8_t** res, size_t* len, int* labs)
+{
+       ldns_rdf* rdf;
+       *res = NULL;
+       *len = 0;
+       *labs = 0;
+       rdf = ldns_dname_new_frm_str(str);
+       if(!rdf) {
+               log_err("cannot parse name %s", str);
+               return 0;
+       }
+       *res = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
+       ldns_rdf_deep_free(rdf);
+       if(!*res) {
+               log_err("out of memory");
+               return 0;
+       }
+       *labs = dname_count_size_labels(*res, len);
+       return 1;
+}
+
+/** enter a new zone with allocated dname */
+static struct local_zone*
+lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len, 
+       int labs, enum localzone_type t, uint16_t dclass)
+{
+       struct local_zone* z = (struct local_zone*)calloc(1, sizeof(*z));
+       if(!z) {
+               log_err("out of memory");
+               return NULL;
+       }
+       z->node.key = z;
+       z->dclass = dclass;
+       z->type = t;
+       z->name = nm;
+       z->namelen = len;
+       z->namelabs = labs;
+       z->region = regional_create();
+       if(!z->region) {
+               log_err("out of memory");
+               free(z);
+               return NULL;
+       }
+       rbtree_init(&z->data, &local_data_cmp);
+       /* add to rbtree */
+       if(!rbtree_insert(&zones->ztree, &z->node)) {
+               log_warn("duplicate local-zone");
+               local_zone_delete(z);
+               return NULL;
+       }
+       return z;
+}
+
+/** enter a new zone */
+static struct local_zone*
+lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
+       uint16_t dclass)
+{
+       struct local_zone* z;
+       enum localzone_type t;
+       uint8_t* nm;
+       size_t len;
+       int labs;
+       if(!parse_dname(name, &nm, &len, &labs)) {
+               log_err("bad zone name %s %s", name, type);
+               return NULL;
+       }
+       if(strcmp(type, "deny") == 0)
+               t = local_zone_deny;
+       else if(strcmp(type, "refuse") == 0)
+               t = local_zone_refuse;
+       else if(strcmp(type, "static") == 0)
+               t = local_zone_static;
+       else if(strcmp(type, "transparent") == 0)
+               t = local_zone_transparent;
+       else if(strcmp(type, "redirect") == 0)
+               t = local_zone_redirect;
+       else {
+               log_err("bad lz_enter_zone type %s %s", name, type);
+               return NULL;
+       }
+       if(!(z=lz_enter_zone_dname(zones, nm, len, labs, t, dclass))) {
+               log_err("could not enter zone %s %s", name, type);
+               return NULL;
+       }
+       return z;
+}
+
+/** return name and class and rdata of rr; parses string */
+static int
+get_rr_content(const char* str, uint8_t** nm, uint16_t* type,
+       uint16_t* dclass, uint32_t* ttl, ldns_buffer* rdata)
+{
+       ldns_rr* rr = NULL;
+       ldns_status status = ldns_rr_new_frm_str(&rr, str, 3600, NULL, NULL);
+       if(status != LDNS_STATUS_OK) {
+               log_err("error parsing local-data '%s': %s",
+                       str, ldns_get_errorstr_by_id(status));
+               ldns_rr_free(rr);
+               return 0;
+       }
+       *nm = memdup(ldns_rdf_data(ldns_rr_owner(rr)), 
+               ldns_rdf_size(ldns_rr_owner(rr)));
+       if(!*nm) {
+               log_err("out of memory");
+               ldns_rr_free(rr);
+               return 0;
+       }
+       *dclass = ldns_rr_get_class(rr);
+       *type = ldns_rr_get_type(rr);
+       *ttl = (uint32_t)ldns_rr_ttl(rr);
+       ldns_buffer_clear(rdata);
+       status = ldns_rr_rdata2buffer_wire(rdata, rr);
+       ldns_rr_free(rr);
+        if(status != LDNS_STATUS_OK) {
+                log_err("error converting RR '%s' to wireformat: %s",
+                        str, ldns_get_errorstr_by_id(status));
+                return 0;
+        }
+        ldns_buffer_flip(rdata);
+        ldns_buffer_write_u16_at(rdata, 0, ldns_buffer_limit(rdata) - 2);
+       return 1;
+}
+
+/** return name and class of rr; parses string */
+static int
+get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass)
+{
+       ldns_rr* rr = NULL;
+       ldns_status status = ldns_rr_new_frm_str(&rr, str, 3600, NULL, NULL);
+       if(status != LDNS_STATUS_OK) {
+               log_err("error parsing local-data '%s': %s",
+                       str, ldns_get_errorstr_by_id(status));
+               ldns_rr_free(rr);
+               return 0;
+       }
+       *nm = memdup(ldns_rdf_data(ldns_rr_owner(rr)), 
+               ldns_rdf_size(ldns_rr_owner(rr)));
+       *dclass = ldns_rr_get_class(rr);
+       ldns_rr_free(rr);
+       if(!*nm) {
+               log_err("out of memory");
+               return 0;
+       }
+       return 1;
+}
+
+/**
+ * Find an rrset in local data structure.
+ * @param data: local data domain name structure.
+ * @param type: type to look for (host order).
+ * @return rrset pointer or NULL if not found.
+ */
+struct local_rrset*
+local_data_find_type(struct local_data* data, uint16_t type)
+{
+       struct local_rrset* p;
+       for(p = data->rrsets; p; p = p->next) {
+               if(ntohs(p->rrset->rk.type) == type)
+                       return p;
+       }
+       return NULL;
+}
+
+/** check for RR duplicates */
+static int
+rr_is_duplicate(struct packed_rrset_data* pd, ldns_buffer* buf)
+{
+       size_t i;
+       for(i=0; i<pd->count; i++) {
+               if(ldns_buffer_limit(buf) == pd->rr_len[i] &&
+                       memcmp(ldns_buffer_begin(buf), pd->rr_data[i],
+                               ldns_buffer_limit(buf)) == 0) 
+                       return 1;
+       }
+       return 0;
+}
+
+/** new local_rrset */
+static struct local_rrset*
+new_local_rrset(struct regional* region, struct local_data* node,
+       uint16_t rrtype, uint16_t rrclass)
+{
+       struct packed_rrset_data* pd;
+       struct local_rrset* rrset = (struct local_rrset*)
+               regional_alloc_zero(region, sizeof(*rrset));
+       if(!rrset) {
+               log_err("out of memory");
+               return NULL;
+       }
+       rrset->next = node->rrsets;
+       node->rrsets = rrset;
+       rrset->rrset = (struct ub_packed_rrset_key*)
+               regional_alloc_zero(region, sizeof(*rrset->rrset));
+       if(!rrset->rrset) {
+               log_err("out of memory");
+               return NULL;
+       }
+       rrset->rrset->entry.key = rrset->rrset;
+       pd = (struct packed_rrset_data*)regional_alloc_zero(region,
+               sizeof(*pd));
+       if(!pd) {
+               log_err("out of memory");
+               return NULL;
+       }
+       pd->trust = rrset_trust_prim_noglue;
+       pd->security = sec_status_insecure;
+       rrset->rrset->entry.data = pd;
+       rrset->rrset->rk.dname = node->name;
+       rrset->rrset->rk.dname_len = node->namelen;
+       rrset->rrset->rk.type = htons(rrtype);
+       rrset->rrset->rk.rrset_class = htons(rrclass);
+       return rrset;
+}
+
+/** insert RR into RRset data structure; Wastes a couple of bytes */
+static int
+insert_rr(struct regional* region, struct packed_rrset_data* pd,
+       ldns_buffer* buf, uint32_t ttl)
+{
+       size_t* oldlen = pd->rr_len;
+       uint32_t* oldttl = pd->rr_ttl;
+       uint8_t** olddata = pd->rr_data;
+
+       /* add RR to rrset */
+       pd->count++;
+       pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count);
+       pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count);
+       pd->rr_data = regional_alloc(region, sizeof(*pd->rr_data)*pd->count);
+       if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
+               log_err("out of memory");
+               return 0;
+       }
+       if(pd->count > 1) {
+               memcpy(pd->rr_len+1, oldlen, 
+                       sizeof(*pd->rr_len)*(pd->count-1));
+               memcpy(pd->rr_ttl+1, oldttl, 
+                       sizeof(*pd->rr_ttl)*(pd->count-1));
+               memcpy(pd->rr_data+1, olddata, 
+                       sizeof(*pd->rr_data)*(pd->count-1));
+       }
+       pd->rr_len[0] = ldns_buffer_limit(buf);
+       pd->rr_ttl[0] = ttl;
+       pd->rr_data[0] = regional_alloc_init(region,
+               ldns_buffer_begin(buf), ldns_buffer_limit(buf));
+       if(!pd->rr_data[0]) {
+               log_err("out of memory");
+               return 0;
+       }
+       return 1;
+}
+
+/** enter data RR into auth zone */
+static int
+lz_enter_rr_into_zone(struct local_zone* z, ldns_buffer* buf,
+       const char* rrstr)
+{
+       struct local_data key;
+       struct local_data* node;
+       struct local_rrset* rrset;
+       struct packed_rrset_data* pd;
+       uint16_t rrtype, rrclass;
+       uint32_t ttl;
+       if(!get_rr_content(rrstr, &key.name, &rrtype, &rrclass, &ttl, buf)) {
+               log_err("bad local-data: %s", rrstr);
+               return 0;
+       }
+       log_assert(z->dclass == rrclass);
+       key.node.key = &key;
+       key.namelabs = dname_count_size_labels(key.name, &key.namelen);
+       node = (struct local_data*)rbtree_search(&z->data, &key.node);
+       if(!node) {
+               /* create a domain name to store rr. */
+               node = (struct local_data*)regional_alloc_zero(z->region,
+                       sizeof(*node));
+               if(!node) {
+                       log_err("out of memory adding local data");
+                       return 0;
+               }
+               node->node.key = &node;
+               node->name = regional_alloc_init(z->region, key.name, 
+                       key.namelen);
+               if(!node->name) {
+                       log_err("out of memory");
+                       return 0;
+               }
+               node->namelen = key.namelen;
+               node->namelabs = key.namelabs;
+               if(!rbtree_insert(&z->data, &node->node)) {
+                       log_assert(0); /* duplicate name */
+               }
+       }
+       free(key.name);
+       log_assert(node);
+
+       rrset = local_data_find_type(node, rrtype);
+       if(!rrset) {
+               rrset = new_local_rrset(z->region, node, rrtype, rrclass);
+               if(!rrset)
+                       return 0;
+               if(query_dname_compare(node->name, z->name) == 0) {
+                       if(rrtype == LDNS_RR_TYPE_NSEC)
+                         rrset->rrset->rk.flags = PACKED_RRSET_NSEC_AT_APEX;
+                       if(rrtype == LDNS_RR_TYPE_SOA)
+                               z->soa = rrset->rrset;
+               }
+       } 
+       pd = (struct packed_rrset_data*)rrset->rrset->entry.data;
+       log_assert(rrset && pd);
+
+       /* check for duplicate RR */
+       if(rr_is_duplicate(pd, buf)) {
+               verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr);
+               return 1;
+       } 
+       return insert_rr(z->region, pd, buf, ttl);
+}
+
+/** enter a data RR into auth data; a zone for it must exist */
+static int
+lz_enter_rr_str(struct local_zones* zones, const char* rr, ldns_buffer* buf)
+{
+       uint8_t* rr_name;
+       uint16_t rr_class;
+       size_t len;
+       int labs;
+       struct local_zone* z;
+       if(!get_rr_nameclass(rr, &rr_name, &rr_class)) {
+               log_err("bad rr %s", rr);
+               return 0;
+       }
+       labs = dname_count_size_labels(rr_name, &len);
+       z = local_zones_lookup(zones, rr_name, len, labs, rr_class);
+       if(!z)
+               fatal_exit("internal error: no zone for rr %s", rr);
+       free(rr_name);
+       return lz_enter_rr_into_zone(z, buf, rr);
+}
+
+/** parse local-zone: statements */
+static int
+lz_enter_zones(struct local_zones* zones, struct config_file* cfg)
+{
+       struct config_str2list* p;
+       for(p = cfg->local_zones; p; p = p->next) {
+               if(!lz_enter_zone(zones, p->str, p->str2, LDNS_RR_CLASS_IN))
+                       return 0;
+       }
+       return 1;
+}
+
+/** lookup a zone in rbtree; exact match only; SLOW due to parse */
+static int
+lz_exists(struct local_zones* zones, const char* name)
+{
+       struct local_zone z;
+       z.node.key = &z;
+       z.dclass = LDNS_RR_CLASS_IN;
+       if(!parse_dname(name, &z.name, &z.namelen, &z.namelabs)) {
+               log_err("bad name %s", name);
+               return 0;
+       }
+       if(rbtree_search(&zones->ztree, &z.node)) {
+               free(z.name);
+               return 1;
+       }
+       free(z.name);
+       return 0;
+}
+
+/** lookup a zone in cfg->nodefault list */
+static int
+lz_nodefault(struct config_file* cfg, const char* name)
+{
+       struct config_strlist* p;
+       for(p = cfg->local_zones_nodefault; p; p = p->next) {
+               /* compare zone name, lowercase */
+               if(strcasecmp(p->str, name) == 0)
+                       return 1;
+       }
+       return 0;
+}
+
+/** enter default zones */
+static int
+lz_enter_defaults(struct local_zones* zones, struct config_file* cfg,
+       ldns_buffer* buf)
+{
+       struct local_zone* z;
+
+       /* localhost. zone */
+       if(!lz_exists(zones, "localhost.") &&
+               !lz_nodefault(cfg, "localhost.")) {
+               if(!(z=lz_enter_zone(zones, "localhost.", "static", 
+                       LDNS_RR_CLASS_IN)) ||
+                  !lz_enter_rr_into_zone(z, buf,
+                       "localhost. 10800 IN NS localhost.") ||
+                  !lz_enter_rr_into_zone(z, buf,
+                       "localhost. 10800 IN SOA localhost. nobody.invalid. "
+                       "1 3600 1200 604800 10800") ||
+                  !lz_enter_rr_into_zone(z, buf,
+                       "localhost. 10800 IN A 127.0.0.1") ||
+                  !lz_enter_rr_into_zone(z, buf,
+                       "localhost. 10800 IN AAAA ::1")) {
+                       log_err("out of memory adding default zone");
+                       return 0;
+               }
+       }
+       /* @@@ TODO other zones */
+       return 0;
+}
+
+/** setup parent pointers, so that a lookup can be done for closest match */
+static void
+init_parents(struct local_zones* zones)
+{
+        struct local_zone* node, *prev = NULL, *p;
+        int m;
+        RBTREE_FOR(node, struct local_zone*, &zones->ztree) {
+                node->parent = NULL;
+                if(!prev || prev->dclass != node->dclass) {
+                        prev = node;
+                        continue;
+                }
+                (void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
+                        node->namelabs, &m); /* we know prev is smaller */
+                /* sort order like: . com. bla.com. zwb.com. net. */
+                /* find the previous, or parent-parent-parent */
+                for(p = prev; p; p = p->parent)
+                        /* looking for name with few labels, a parent */
+                        if(p->namelabs <= m) {
+                                /* ==: since prev matched m, this is closest*/
+                                /* <: prev matches more, but is not a parent,
+                                 * this one is a (grand)parent */
+                                node->parent = p;
+                                break;
+                        }
+                prev = node;
+        }
+}
+
+/** enter implicit transparent zone for local-data: without local-zone: */
+static int
+lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
+{
+       /* walk over all items that have no parent zone and find
+        * the name that covers them all (could be the root) and
+        * add that as a transparent zone */
+       struct config_strlist* p;
+       int have_name = 0;
+       int have_other_classes = 0;
+       uint16_t dclass = 0;
+       uint8_t* nm = 0;
+       size_t nmlen = 0;
+       int nmlabs = 0;
+       int match = 0; /* number of labels match count */
+
+       init_parents(zones); /* to enable local_zones_lookup() */
+       for(p = cfg->local_data; p; p = p->next) {
+               uint8_t* rr_name;
+               uint16_t rr_class;
+               size_t len;
+               int labs;
+               if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) {
+                       log_err("Bad local-data RR %s", p->str);
+                       return 0;
+               }
+               labs = dname_count_size_labels(rr_name, &len);
+               if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) {
+                       if(!have_name) {
+                               dclass = rr_class;
+                               nm = rr_name;
+                               nmlen = len;
+                               nmlabs = labs;
+                               match = labs;
+                               have_name = 1;
+                       } else {
+                               int m;
+                               if(rr_class != dclass) {
+                                       /* process other classes later */
+                                       have_other_classes = 1;
+                                       continue;
+                               }
+                               /* find smallest shared topdomain */
+                               (void)dname_lab_cmp(nm, nmlabs, 
+                                       rr_name, labs, &m);
+                               if(m < match)
+                                       match = m;
+                       }
+               }
+               free(rr_name);
+       }
+       if(have_name) {
+               uint8_t* n2;
+               /* allocate zone of smallest shared topdomain to contain em */
+               n2 = nm;
+               dname_remove_labels(&n2, &nmlen, nmlabs - match);
+               n2 = memdup(n2, nmlen);
+               free(nm);
+               if(!n2) {
+                       log_err("out of memory");
+                       return 0;
+               }
+               if(!lz_enter_zone_dname(zones, n2, nmlen, match, 
+                       local_zone_transparent, dclass)) {
+                       return 0;
+               }
+       }
+       if(have_other_classes) { 
+               /* restart to setup other class */
+               return lz_setup_implicit(zones, cfg);
+       }
+       return 0;
+}
+
+/** enter auth data */
+static int
+lz_enter_data(struct local_zones* zones, struct config_file* cfg,
+       ldns_buffer* buf)
+{
+       struct config_strlist* p;
+       for(p = cfg->local_data; p; p = p->next) {
+               if(!lz_enter_rr_str(zones, p->str, buf))
+                       return 0;
+       }
+       return 1;
+}
+
+/** free memory from config */
+static void
+lz_freeup_cfg(struct config_file* cfg)
+{
+       config_deldblstrlist(cfg->local_zones);
+       cfg->local_zones = NULL;
+       config_delstrlist(cfg->local_zones_nodefault);
+       cfg->local_zones_nodefault = NULL;
+       config_delstrlist(cfg->local_data);
+       cfg->local_data = NULL;
+}
+
+int 
+local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
+{
+       ldns_buffer* buf = ldns_buffer_new(65535);
+       if(!buf) fatal_exit("cannot create temporary buffer");
+
+       /* create zones from zone statements. */
+       if(!lz_enter_zones(zones, cfg)) {
+               ldns_buffer_free(buf);
+               return 0;
+       }
+       /* apply default zones+content (unless disabled, or overridden) */
+       if(!lz_enter_defaults(zones, cfg, buf)) {
+               ldns_buffer_free(buf);
+               return 0;
+       }
+       /* create implicit transparent zone from data. */
+       if(!lz_setup_implicit(zones, cfg)) {
+               ldns_buffer_free(buf);
+               return 0;
+       }
+
+       /* setup parent ptrs for lookup during data entry */
+       init_parents(zones);
+       /* insert local data */
+       if(!lz_enter_data(zones, cfg, buf)) {
+               ldns_buffer_free(buf);
+               return 0;
+       }
+       /* freeup memory from cfg struct. */
+       lz_freeup_cfg(cfg);
+       ldns_buffer_free(buf);
+       return 1;
+}
+
+struct local_zone* 
+local_zones_lookup(struct local_zones* zones,
+        uint8_t* name, size_t len, int labs, uint16_t dclass)
+{
+       rbnode_t* res = NULL;
+       struct local_zone *result;
+       struct local_zone key;
+       key.node.key = &key;
+       key.dclass = dclass;
+       key.name = name;
+       key.namelen = len;
+       key.namelabs = labs;
+       if(rbtree_find_less_equal(&zones->ztree, &key, &res)) {
+               /* exact */
+               return (struct local_zone*)res;
+       } else {
+               /* smaller element (or no element) */
+                int m;
+                result = (struct local_zone*)res;
+                if(!result || result->dclass != dclass)
+                        return NULL;
+                /* count number of labels matched */
+                (void)dname_lab_cmp(result->name, result->namelabs, key.name,
+                        key.namelabs, &m);
+                while(result) { /* go up until qname is subdomain of zone */
+                        if(result->namelabs <= m)
+                                break;
+                        result = result->parent;
+                }
+               return result;
+       }
+}
+
+/** print all RRsets in local zone */
+static void 
+local_zone_out(struct local_zone* z)
+{
+       struct local_data* d;
+       struct local_rrset* p;
+       RBTREE_FOR(d, struct local_data*, &z->data) {
+               for(p = d->rrsets; p; p = p->next) {
+                       log_nametypeclass(0, "rrset", d->name, 
+                               ntohs(p->rrset->rk.type),
+                               ntohs(p->rrset->rk.rrset_class));
+               }
+       }
+}
+
+void local_zones_print(struct local_zones* zones)
+{
+       struct local_zone* z;
+       log_info("number of auth zones %u", (unsigned)zones->ztree.count);
+       RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
+               switch(z->type) {
+               case local_zone_deny:
+                       log_nametypeclass(0, "deny zone", 
+                               z->name, 0, z->dclass);
+               case local_zone_refuse:
+                       log_nametypeclass(0, "refuse zone", 
+                               z->name, 0, z->dclass);
+               case local_zone_redirect:
+                       log_nametypeclass(0, "redirect zone", 
+                               z->name, 0, z->dclass);
+               case local_zone_transparent:
+                       log_nametypeclass(0, "transparent zone", 
+                               z->name, 0, z->dclass);
+               case local_zone_static:
+                       log_nametypeclass(0, "static zone", 
+                               z->name, 0, z->dclass);
+               default:
+                       log_nametypeclass(0, "badtyped zone", 
+                               z->name, 0, z->dclass);
+               }
+               local_zone_out(z);
+       }
+}
index 606b4ab8a0821495f63b48aaa4e88d125d53d61a..6339bf69bdee8099e34865de0034599f67b26eb2 100644 (file)
@@ -44,6 +44,7 @@
 #include "util/rbtree.h"
 struct ub_packed_rrset_key;
 struct regional;
+struct config_file;
 
 /**
  * Local zone type
@@ -67,12 +68,12 @@ enum localzone_type {
 };
 
 /**
- * Local zones storage, shared.
- * Fixed at startup, so, readonly, no locks or mutexes necessary.
+ * Authoritative local zones storage, shared.
+ * This tree is fixed at startup, so, readonly, no locks or mutexes necessary.
  */
 struct local_zones {
        /** rbtree of struct local_zone */
-       rbtree_t zones;
+       rbtree_t ztree;
 };
 
 /**
@@ -87,10 +88,11 @@ struct local_zone {
        /** zone name, in uncompressed wireformat */
        uint8_t* name;
        /** length of zone name */
-       size_t name_len;
+       size_t namelen;
        /** number of labels in zone name */
-       int name_labs;
-       /** the class of this zone */
+       int namelabs;
+       /** the class of this zone. 
+        * uses 'dclass' to not conflict with c++ keyword class. */
        uint16_t dclass;
 
        /** how to process zone */
@@ -103,7 +105,7 @@ struct local_zone {
         * rbtree of struct local_data */
        rbtree_t data;
        /** if data contains zone apex SOA data, this is a ptr to it. */
-       struct ub_packed_rrset_key* apex;
+       struct ub_packed_rrset_key* soa;
 };
 
 /**
@@ -118,7 +120,7 @@ struct local_data {
        size_t namelen;
        /** number of labels in name */
        int namelabs;
-       /** the data rrsets, match type and class, linked list */
+       /** the data rrsets, with different types, linked list */
        struct local_rrset* rrsets;
 };
 
@@ -132,4 +134,65 @@ struct local_rrset {
        struct ub_packed_rrset_key* rrset;
 };
 
+/**
+ * Create local zones storage
+ * @return new struct or NULL on error.
+ */
+struct local_zones* local_zones_create();
+
+/**
+ * Delete local zones storage
+ * @param zones: to delete.
+ */
+void local_zones_delete(struct local_zones* zones);
+
+/**
+ * Apply config settings; setup the local authoritative data. 
+ * @param zones: is set up.
+ * @param cfg: config data.
+ * @return false on error.
+ */
+int local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg);
+
+/**
+ * Compare two local_zone entries in rbtree. Sort hierarchical but not
+ * canonical
+ * @param z1: zone 1
+ * @param z2: zone 2
+ * @return: -1, 0, +1 comparison value.
+ */
+int local_zone_cmp(const void* z1, const void* z2);
+
+/**
+ * Compare two local_data entries in rbtree. Sort canonical.
+ * @param d1: data 1
+ * @param d2: data 2
+ * @return: -1, 0, +1 comparison value.
+ */
+int local_data_cmp(const void* d1, const void* d2);
+
+/**
+ * Delete one zone
+ * @param z: to delete.
+ */
+void local_zone_delete(struct local_zone* z);
+
+/**
+ * Lookup zone that contains the given name, class.
+ * @param zones: the zones tree
+ * @param name: dname to lookup
+ * @param len: length of name.
+ * @param labs: labelcount of name.
+ * @param dclass: class to lookup.
+ * @return closest local_zone or NULL if no covering zone is found.
+ */
+struct local_zone* local_zones_lookup(struct local_zones* zones, 
+       uint8_t* name, size_t len, int labs, uint16_t dclass);
+
+/**
+ * Debug helper. Print all zones 
+ * @param zones: the zones tree
+ */
+void local_zones_print(struct local_zones* zones);
+
 #endif /* SERVICES_LOCALZONE_H */
index 452d6c6aeec1b8aa695e465bee34a82c68dda1f2..16286859249de68d5b89bdb225fa05fcc42314ea 100644 (file)
@@ -49,6 +49,7 @@
 #include "daemon/worker.h"
 #include "services/outside_network.h"
 #include "services/mesh.h"
+#include "services/localzone.h"
 #include "services/cache/infra.h"
 #include "iterator/iterator.h"
 #include "iterator/iter_donotq.h"
@@ -132,6 +133,8 @@ fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *))
        if(fptr == &mesh_state_compare) return 1;
        else if(fptr == &mesh_state_ref_compare) return 1;
        else if(fptr == &acl_list_cmp) return 1;
+       else if(fptr == &local_zone_cmp) return 1;
+       else if(fptr == &local_data_cmp) return 1;
        else if(fptr == &donotq_cmp) return 1;
        else if(fptr == &fwd_cmp) return 1;
        else if(fptr == &stub_cmp) return 1;