]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
add remove local data and local zone with remote control
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 19 Sep 2008 14:49:29 +0000 (14:49 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 19 Sep 2008 14:49:29 +0000 (14:49 +0000)
(ldns-testpkts sync with ldns trunk).

git-svn-id: file:///svn/unbound/trunk@1261 be551aaa-1e26-0410-a405-d3ace91eadb9

contrib/unbound_munin_
daemon/remote.c
doc/Changelog
doc/plan
doc/unbound-control.8.in
services/localzone.c
services/localzone.h
smallapp/unbound-control.c
testcode/ldns-testpkts.c
validator/validator.c

index df63ead4b04737e6eaf6ce9dc80615704ef4ef9a..341f5071bec8b2762b34ee707882c3f1300561a7 100755 (executable)
@@ -9,8 +9,9 @@
 #                      statistics-cumulative: no
 #                      statistics-interval: 0
 #      remote-control: control-enable: yes
+# Run the command unbound-control-setup to generate the key files.
 #
-# Environment variables
+# Environment variables for this script
 #      statefile       - where to put temporary statefile.
 #      unbound_conf    - where the unbound.conf file is located.
 #      unbound_control - where to find unbound-control executable.
index d6e4f0ef64071b48fa73d9027db45486499bcd12..870fec0a0d592ed202857f770e5df65c618aebcc 100644 (file)
 #include "services/listen_dnsport.h"
 #include "services/cache/rrset.h"
 #include "services/mesh.h"
+#include "services/localzone.h"
 #include "util/storage/slabhash.h"
 #include "util/fptr_wlist.h"
+#include "util/data/dname.h"
 
 #ifdef HAVE_SYS_TYPES_H
 #  include <sys/types.h>
@@ -839,6 +841,139 @@ do_stats(SSL* ssl, struct daemon_remote* rc)
        }
 }
 
+/** parse commandline argument domain name */
+static int
+parse_arg_name(SSL* ssl, 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) {
+               ssl_printf(ssl, "error cannot parse name %s\n", str);
+               return 0;
+       }
+       *res = memdup(ldns_rdf_data(rdf), ldns_rdf_size(rdf));
+       ldns_rdf_deep_free(rdf);
+       if(!*res) {
+               ssl_printf(ssl, "error out of memory\n");
+               return 0;
+       }
+       *labs = dname_count_size_labels(*res, len);
+       return 1;
+}
+
+/** find second argument, modifies string */
+static int
+find_arg2(SSL* ssl, char* arg, char** arg2)
+{
+       char* as = strchr(arg, ' ');
+       char* at = strchr(arg, '\t');
+       if(as && at) {
+               if(at < as)
+                       as = at;
+               as[0]=0;
+               *arg2 = skipwhite(as+1);
+       } else if(as) {
+               as[0]=0;
+               *arg2 = skipwhite(as+1);
+       } else if(at) {
+               at[0]=0;
+               *arg2 = skipwhite(at+1);
+       } else {
+               ssl_printf(ssl, "error could not find next argument "
+                       "after %s\n", arg);
+               return 0;
+       }
+       return 1;
+}
+
+/** Add a new zone */
+static void
+do_zone_add(SSL* ssl, struct worker* worker, char* arg)
+{
+       uint8_t* nm;
+       int nmlabs;
+       size_t nmlen;
+       char* arg2;
+       enum localzone_type t;
+       struct local_zone* z;
+       if(!find_arg2(ssl, arg, &arg2))
+               return;
+       if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+               return;
+       if(!local_zone_str2type(arg2, &t)) {
+               ssl_printf(ssl, "error not a zone type. %s\n", arg2);
+               return;
+       }
+       lock_quick_lock(&worker->daemon->local_zones->lock);
+       if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen, 
+               nmlabs, LDNS_RR_CLASS_IN))) {
+               /* already present in tree */
+               lock_rw_wrlock(&z->lock);
+               z->type = t; /* update type anyway */
+               lock_rw_unlock(&z->lock);
+               lock_quick_unlock(&worker->daemon->local_zones->lock);
+               send_ok(ssl);
+               return;
+       }
+       if(!local_zones_add_zone(worker->daemon->local_zones, nm, nmlen, 
+               nmlabs, LDNS_RR_CLASS_IN, t)) {
+               lock_quick_unlock(&worker->daemon->local_zones->lock);
+               ssl_printf(ssl, "error out of memory\n");
+               return;
+       }
+       lock_quick_unlock(&worker->daemon->local_zones->lock);
+       send_ok(ssl);
+}
+
+/** Remove a zone */
+static void
+do_zone_remove(SSL* ssl, struct worker* worker, char* arg)
+{
+       uint8_t* nm;
+       int nmlabs;
+       size_t nmlen;
+       struct local_zone* z;
+       if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+               return;
+       lock_quick_lock(&worker->daemon->local_zones->lock);
+       if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen, 
+               nmlabs, LDNS_RR_CLASS_IN))) {
+               /* present in tree */
+               local_zones_del_zone(worker->daemon->local_zones, z);
+       }
+       lock_quick_unlock(&worker->daemon->local_zones->lock);
+       send_ok(ssl);
+}
+
+/** Add new RR data */
+static void
+do_data_add(SSL* ssl, struct worker* worker, char* arg)
+{
+       if(!local_zones_add_RR(worker->daemon->local_zones, arg,
+               worker->env.scratch_buffer)) {
+               ssl_printf(ssl,"error in syntax or out of memory, %s\n", arg);
+               return;
+       }
+       send_ok(ssl);
+}
+
+/** Remove RR data */
+static void
+do_data_remove(SSL* ssl, struct worker* worker, char* arg)
+{
+       uint8_t* nm;
+       int nmlabs;
+       size_t nmlen;
+       if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
+               return;
+       local_zones_del_data(worker->daemon->local_zones, nm,
+               nmlen, nmlabs, LDNS_RR_CLASS_IN);
+       send_ok(ssl);
+}
+
 /** execute a remote control command */
 static void
 execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd)
@@ -853,6 +988,14 @@ execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd)
                do_verbosity(ssl, skipwhite(p+9));
        } else if(strncmp(p, "stats", 5) == 0) {
                do_stats(ssl, rc);
+       } else if(strncmp(p, "local_zone_remove", 17) == 0) {
+               do_zone_remove(ssl, rc->worker, skipwhite(p+17));
+       } else if(strncmp(p, "local_zone", 10) == 0) {
+               do_zone_add(ssl, rc->worker, skipwhite(p+10));
+       } else if(strncmp(p, "local_data_remove", 17) == 0) {
+               do_data_remove(ssl, rc->worker, skipwhite(p+17));
+       } else if(strncmp(p, "local_data", 10) == 0) {
+               do_data_add(ssl, rc->worker, skipwhite(p+10));
        } else {
                (void)ssl_printf(ssl, "error unknown command '%s'\n", p);
        }
index 8f64b7dc734672c72fa15f7064ae18192b27f7ca..768a8d9ecf1b0c53bd355c73b04ef0f95ca3aaa4 100644 (file)
@@ -1,3 +1,7 @@
+19 September 2008: Wouter
+       - locking on the localdata structure.
+       - add and remove local zone and data with unbound-control.
+
 18 September 2008: Wouter
        - fixup error in time calculation.
        - munin plugin improvements.
index bf9e34ded355077427997a11323d603665bf839f..305a0860e07311bccbfcd2cfe4803b9a36a642d2 100644 (file)
--- a/doc/plan
+++ b/doc/plan
@@ -43,19 +43,19 @@ like dnswall does.  Allow certain subdomains to do it, config options.
        note in config/man that we may consider turning on by default.
 
 *** Remote control feature
-* remote control using a TCP unbound-control commandline app.  
-* secure remote control w. TSIG.  Or TLS.
-* Nicer statistics (over that unbound-control app for ease)
++ remote control using a TCP unbound-control commandline app.  
++ secure remote control w. TSIG.  Or TLS.
++ Nicer statistics (over that unbound-control app for ease)
     stats display added over threads, displayed in rddtool easy format.
-* option for extended statistics. If enabled (not by default) collect print
++ option for extended statistics. If enabled (not by default) collect print
         rcode, uptime, spoofnearmisses, cache size, qtype,
        bits(RD, CD, DO, EDNS-present, AD)query, (Secure, Bogus)reply.
-       perhaps also see which slow auth servers cause >1sec values.
        stats-file possible with key: value or key=value lines in it.
-       stats on SIGUSR1. addup stats over threads.
+       addup stats over threads.
+not    stats on SIGUSR1. perhaps also see which slow auth servers cause >1sec values.
 * remote control to add/remove localinfo, redirects.
 * remote control to load/store cache contents
-* remote control to start, stop, reload.
++ remote control to start, stop, reload.
 * remote control to flush names or domains (all under a name) from the 
    cache. Include NSes. And the A, AAAA for its NSes.
 * remote control to see delegation; what servers would be used to get 
index 593dcdd132eaceafc44c78ce7aca7421f05ce22a..0cecc5c811dca88e28e794729cf266851f8c4529 100644 (file)
@@ -60,8 +60,30 @@ a reload (taken from config file again), or the next verbosity control command.
 Print statistics. Resets the internal counters to zero, this can be 
 controlled using the \fBstatistics\-cumulative\fR config statement. 
 Statistics are printed with one [name]: [value] per line.
+.TP
+.B local_zone \fIname\fR \fItype
+Add new local zone with name and type. Like \fBlocal\-zone\fR config statement.
+If the zone already exists, the type is changed to the given argument.
+.TP
+.B local_zone_remove \fIname
+Remove the local zone with the given name.  Removes all local data inside
+it.  If the zone does not exist, the command succeeds.
+.TP
+.B local_data \fIRR data...
+Add new local data, the given resource record. Like \fBlocal\-data\fR
+config statement, except for when no covering zone exists.  In that case
+this remote control command creates a transparent zone with the same 
+name as this record.  This command is not good at returning detailed syntax 
+errors.
+.TP
+.B local_data_remove \fIname
+Remove all RR data from local name.  If the name already has no items,
+nothing happens.  Often results in NXDOMAIN for the name (in a static zone),
+but if the name has become an empty nonterminal (there is still data in 
+domain names below the removed name), NOERROR nodata answers are the 
+result for that name.
 .SH "EXIT CODE"
-The unbound-control program exits with status code 1 on error.
+The unbound-control program exits with status code 1 on error, 0 on success.
 .SH "SET UP"
 The setup requires a self\-signed certificate and private keys for both 
 the server and client.  The script \fIunbound\-control\-setup\fR generates
index 2712bab7a62997eed9eeb2dc304bb60837abcd9d..289d3435eabec7332f53e3d8787b810d6a630db0 100644 (file)
@@ -57,6 +57,9 @@ local_zones_create()
        if(!zones)
                return NULL;
        rbtree_init(&zones->ztree, &local_zone_cmp);
+       lock_quick_init(&zones->lock);
+       lock_protect(&zones->lock, &zones->ztree, sizeof(zones->ztree));
+       /* also lock protects the rbnode's in struct local_zone */
        return zones;
 }
 
@@ -73,6 +76,7 @@ local_zones_delete(struct local_zones* zones)
 {
        if(!zones)
                return;
+       lock_quick_destroy(&zones->lock);
        /* walk through zones and delete them all */
        traverse_postorder(&zones->ztree, lzdel, NULL);
        free(zones);
@@ -83,6 +87,7 @@ local_zone_delete(struct local_zone* z)
 {
        if(!z)
                return;
+       lock_rw_destroy(&z->lock);
        regional_destroy(z->region);
        free(z->name);
        free(z);
@@ -137,14 +142,13 @@ parse_dname(const char* str, uint8_t** res, size_t* len, int* labs)
        return 1;
 }
 
-/** enter a new zone with allocated dname */
+/** create a new localzone */
 static struct local_zone*
-lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len, 
+local_zone_create(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;
@@ -153,19 +157,46 @@ lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
        z->name = nm;
        z->namelen = len;
        z->namelabs = labs;
+       lock_rw_init(&z->lock);
        z->region = regional_create();
        if(!z->region) {
-               log_err("out of memory");
                free(z);
                return NULL;
        }
        rbtree_init(&z->data, &local_data_cmp);
+       lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_t));
+       lock_protect(&zones->lock, &z->node, sizeof(z->node));
+       lock_protect(&zones->lock, &z->parent, sizeof(z->parent));
+       lock_protect(&zones->lock, &z->name, sizeof(z->name));
+       lock_protect(&zones->lock, &z->namelen, sizeof(z->namelen));
+       lock_protect(&zones->lock, &z->namelabs, sizeof(z->namelabs));
+       lock_protect(&zones->lock, &z->dclass, sizeof(z->dclass));
+       (void)zones; /* avoid argument unused warning if no lock checks */
+       return z;
+}
+
+/** enter a new zone with allocated dname returns with WRlock */
+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 c)
+{
+       struct local_zone* z = local_zone_create(zones, nm, len, labs, t, c);
+       if(!z) {
+               log_err("out of memory");
+               return NULL;
+       }
+
        /* add to rbtree */
+       lock_quick_lock(&zones->lock);
+       lock_rw_wrlock(&z->lock);
        if(!rbtree_insert(&zones->ztree, &z->node)) {
                log_warn("duplicate local-zone");
+               lock_rw_unlock(&z->lock);
                local_zone_delete(z);
+               lock_quick_unlock(&zones->lock);
                return NULL;
        }
+       lock_quick_unlock(&zones->lock);
        return z;
 }
 
@@ -183,17 +214,7 @@ lz_enter_zone(struct local_zones* zones, const char* name, const char* type,
                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 {
+       if(!local_zone_str2type(type, &t)) {
                log_err("bad lz_enter_zone type %s %s", name, type);
                free(nm);
                return NULL;
@@ -373,18 +394,24 @@ insert_rr(struct regional* region, struct packed_rrset_data* pd,
        return 1;
 }
 
-/** find a node, create it if not and all its empty nonterminal parents */
-static int
-lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, 
-       int nmlabs, struct local_data** res)
+/** find a data node by exact name */
+static struct local_data* 
+lz_find_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs)
 {
        struct local_data key;
-       struct local_data* ld;
        key.node.key = &key;
        key.name = nm;
        key.namelen = nmlen;
        key.namelabs = nmlabs;
-       ld = (struct local_data*)rbtree_search(&z->data, &key.node);
+       return (struct local_data*)rbtree_search(&z->data, &key.node);
+}
+
+/** find a node, create it if not and all its empty nonterminal parents */
+static int
+lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, 
+       int nmlabs, struct local_data** res)
+{
+       struct local_data* ld = lz_find_node(z, nm, nmlen, nmlabs);
        if(!ld) {
                /* create a domain name to store rr. */
                ld = (struct local_data*)regional_alloc_zero(z->region,
@@ -480,16 +507,24 @@ lz_enter_rr_str(struct local_zones* zones, const char* rr, ldns_buffer* buf)
        size_t len;
        int labs;
        struct local_zone* z;
+       int r;
        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);
+       lock_quick_lock(&zones->lock);
        z = local_zones_lookup(zones, rr_name, len, labs, rr_class);
-       if(!z)
+       if(!z) {
+               lock_quick_unlock(&zones->lock);
                fatal_exit("internal error: no zone for rr %s", rr);
+       }
+       lock_rw_wrlock(&z->lock);
+       lock_quick_unlock(&zones->lock);
        free(rr_name);
-       return lz_enter_rr_into_zone(z, buf, rr);
+       r = lz_enter_rr_into_zone(z, buf, rr);
+       lock_rw_unlock(&z->lock);
+       return r;
 }
 
 /** parse local-zone: statements */
@@ -497,9 +532,12 @@ static int
 lz_enter_zones(struct local_zones* zones, struct config_file* cfg)
 {
        struct config_str2list* p;
+       struct local_zone* z;
        for(p = cfg->local_zones; p; p = p->next) {
-               if(!lz_enter_zone(zones, p->str, p->str2, LDNS_RR_CLASS_IN))
+               if(!(z=lz_enter_zone(zones, p->str, p->str2, 
+                       LDNS_RR_CLASS_IN)))
                        return 0;
+               lock_rw_unlock(&z->lock);
        }
        return 1;
 }
@@ -515,10 +553,13 @@ lz_exists(struct local_zones* zones, const char* name)
                log_err("bad name %s", name);
                return 0;
        }
+       lock_quick_lock(&zones->lock);
        if(rbtree_search(&zones->ztree, &z.node)) {
+               lock_quick_unlock(&zones->lock);
                free(z.name);
                return 1;
        }
+       lock_quick_unlock(&zones->lock);
        free(z.name);
        return 0;
 }
@@ -555,11 +596,16 @@ add_as112_default(struct local_zones* zones, struct config_file* cfg,
                return 0;
        snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. "
                "nobody.invalid. 1 3600 1200 604800 10800", name);
-       if(!lz_enter_rr_into_zone(z, buf, str))
+       if(!lz_enter_rr_into_zone(z, buf, str)) {
+               lock_rw_unlock(&z->lock);
                return 0;
+       }
        snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name);
-       if(!lz_enter_rr_into_zone(z, buf, str))
+       if(!lz_enter_rr_into_zone(z, buf, str)) {
+               lock_rw_unlock(&z->lock);
                return 0;
+       }
+       lock_rw_unlock(&z->lock);
        return 1;
 }
 
@@ -585,8 +631,10 @@ lz_enter_defaults(struct local_zones* zones, struct config_file* cfg,
                   !lz_enter_rr_into_zone(z, buf,
                        "localhost. 10800 IN AAAA ::1")) {
                        log_err("out of memory adding default zone");
+                       if(z) { lock_rw_unlock(&z->lock); }
                        return 0;
                }
+               lock_rw_unlock(&z->lock);
        }
        /* reverse ip4 zone */
        if(!lz_exists(zones, "127.in-addr.arpa.") &&
@@ -601,8 +649,10 @@ lz_enter_defaults(struct local_zones* zones, struct config_file* cfg,
                   !lz_enter_rr_into_zone(z, buf,
                        "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) {
                        log_err("out of memory adding default zone");
+                       if(z) { lock_rw_unlock(&z->lock); }
                        return 0;
                }
+               lock_rw_unlock(&z->lock);
        }
        /* reverse ip6 zone */
        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.") &&
@@ -617,8 +667,10 @@ lz_enter_defaults(struct local_zones* zones, struct config_file* cfg,
                   !lz_enter_rr_into_zone(z, buf,
                        "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.")) {
                        log_err("out of memory adding default zone");
+                       if(z) { lock_rw_unlock(&z->lock); }
                        return 0;
                }
+               lock_rw_unlock(&z->lock);
        }
        if (    !add_as112_default(zones, cfg, buf, "10.in-addr.arpa.") ||
                !add_as112_default(zones, cfg, buf, "16.172.in-addr.arpa.") ||
@@ -661,10 +713,13 @@ init_parents(struct local_zones* zones)
 {
         struct local_zone* node, *prev = NULL, *p;
         int m;
+       lock_quick_lock(&zones->lock);
         RBTREE_FOR(node, struct local_zone*, &zones->ztree) {
+               lock_rw_wrlock(&node->lock);
                 node->parent = NULL;
                 if(!prev || prev->dclass != node->dclass) {
                         prev = node;
+                       lock_rw_unlock(&node->lock);
                         continue;
                 }
                 (void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
@@ -681,7 +736,9 @@ init_parents(struct local_zones* zones)
                                 break;
                         }
                 prev = node;
+               lock_rw_unlock(&node->lock);
         }
+       lock_quick_unlock(&zones->lock);
 }
 
 /** enter implicit transparent zone for local-data: without local-zone: */
@@ -711,6 +768,7 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
                        return 0;
                }
                labs = dname_count_size_labels(rr_name, &len);
+               lock_quick_lock(&zones->lock);
                if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) {
                        if(!have_name) {
                                dclass = rr_class;
@@ -725,6 +783,7 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
                                        /* process other classes later */
                                        free(rr_name);
                                        have_other_classes = 1;
+                                       lock_quick_unlock(&zones->lock);
                                        continue;
                                }
                                /* find smallest shared topdomain */
@@ -735,9 +794,11 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
                                        match = m;
                        }
                } else free(rr_name);
+               lock_quick_unlock(&zones->lock);
        }
        if(have_name) {
                uint8_t* n2;
+               struct local_zone* z;
                /* allocate zone of smallest shared topdomain to contain em */
                n2 = nm;
                dname_remove_labels(&n2, &nmlen, nmlabs - match);
@@ -749,10 +810,11 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
                }
                log_nametypeclass(VERB_ALGO, "implicit transparent local-zone", 
                        n2, 0, dclass);
-               if(!lz_enter_zone_dname(zones, n2, nmlen, match, 
-                       local_zone_transparent, dclass)) {
+               if(!(z=lz_enter_zone_dname(zones, n2, nmlen, match, 
+                       local_zone_transparent, dclass))) {
                        return 0;
                }
+               lock_rw_unlock(&z->lock);
        }
        if(have_other_classes) { 
                /* restart to setup other class */
@@ -854,6 +916,20 @@ local_zones_lookup(struct local_zones* zones,
        }
 }
 
+struct local_zone* 
+local_zones_find(struct local_zones* zones,
+        uint8_t* name, size_t len, int labs, uint16_t dclass)
+{
+       struct local_zone key;
+       key.node.key = &key;
+       key.dclass = dclass;
+       key.name = name;
+       key.namelen = len;
+       key.namelabs = labs;
+       /* exact */
+       return (struct local_zone*)rbtree_search(&zones->ztree, &key);
+}
+
 /** print all RRsets in local zone */
 static void 
 local_zone_out(struct local_zone* z)
@@ -872,8 +948,10 @@ local_zone_out(struct local_zone* z)
 void local_zones_print(struct local_zones* zones)
 {
        struct local_zone* z;
+       lock_quick_lock(&zones->lock);
        log_info("number of auth zones %u", (unsigned)zones->ztree.count);
        RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
+               lock_rw_rdlock(&z->lock);
                switch(z->type) {
                case local_zone_deny:
                        log_nametypeclass(0, "deny zone", 
@@ -901,7 +979,9 @@ void local_zones_print(struct local_zones* zones)
                        break;
                }
                local_zone_out(z);
+               lock_rw_unlock(&z->lock);
        }
+       lock_quick_unlock(&zones->lock);
 }
 
 /** encode answer consisting of 1 rrset */
@@ -1032,10 +1112,203 @@ local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
         *              - look at zone type for negative response. */
        int labs = dname_count_labels(qinfo->qname);
        struct local_data* ld;
-       struct local_zone* z = local_zones_lookup(zones, qinfo->qname,
+       struct local_zone* z;
+       int r;
+       lock_quick_lock(&zones->lock);
+       z = local_zones_lookup(zones, qinfo->qname,
                qinfo->qname_len, labs, qinfo->qclass);
-       if(!z) return 0;
-       if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld))
+       if(!z) {
+               lock_quick_unlock(&zones->lock);
+               return 0;
+       }
+       lock_rw_rdlock(&z->lock);
+       lock_quick_unlock(&zones->lock);
+
+       if(local_data_answer(z, qinfo, edns, buf, temp, labs, &ld)) {
+               lock_rw_unlock(&z->lock);
                return 1;
-       return lz_zone_answer(z, qinfo, edns, buf, temp, ld);
+       }
+       r = lz_zone_answer(z, qinfo, edns, buf, temp, ld);
+       lock_rw_unlock(&z->lock);
+       return r;
+}
+
+int local_zone_str2type(const char* type, enum localzone_type* t)
+{
+       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 return 0;
+       return 1;
+}
+
+/** iterate over the kiddies of the given name and set their parent ptr */
+static void
+set_kiddo_parents(struct local_zone* z, struct local_zone* match, 
+       struct local_zone* newp)
+{
+       /* both zones and z are locked already */
+       /* in the sorted rbtree, the kiddies of z are located after z */
+       /* z must be present in the tree */
+       struct local_zone* p = z;
+       p = (struct local_zone*)rbtree_next(&p->node);
+       while(p!=(struct local_zone*)RBTREE_NULL &&
+               p->dclass == z->dclass && dname_strict_subdomain(p->name,
+               p->namelabs, z->name, z->namelabs)) {
+               /* update parent ptr */
+               /* only when matches with existing parent pointer, so that
+                * deeper child structures are not touched, i.e.
+                * update of x, and a.x, b.x, f.b.x, g.b.x, c.x, y
+                * gets to update a.x, b.x and c.x */
+               lock_rw_wrlock(&p->lock);
+               if(p->parent == match)
+                       p->parent = newp;
+               lock_rw_unlock(&p->lock);
+               p = (struct local_zone*)rbtree_next(&p->node);
+       }
+}
+
+struct local_zone* local_zones_add_zone(struct local_zones* zones,
+       uint8_t* name, size_t len, int labs, uint16_t dclass,
+       enum localzone_type tp)
+{
+       /* create */
+       struct local_zone* z = local_zone_create(zones, name, len, labs, tp,
+               dclass);
+       if(!z) return NULL;
+       lock_rw_wrlock(&z->lock);
+
+       /* find the closest parent */
+       z->parent = local_zones_find(zones, name, len, labs, dclass);
+
+       /* insert into the tree */
+       if(!rbtree_insert(&zones->ztree, &z->node)) {
+               /* duplicate entry! */
+               lock_rw_unlock(&z->lock);
+               local_zone_delete(z);
+               log_err("internal: duplicate entry in local_zones_add_zone");
+               return NULL;
+       }
+
+       /* set parent pointers right */
+       set_kiddo_parents(z, z->parent, z);
+
+       lock_rw_unlock(&z->lock);
+       return z;
+}
+
+void local_zones_del_zone(struct local_zones* zones, struct local_zone* z)
+{
+       /* fix up parents in tree */
+       lock_rw_wrlock(&z->lock);
+       set_kiddo_parents(z, z, z->parent);
+
+       /* remove from tree */
+       (void)rbtree_delete(&zones->ztree, z);
+
+       /* delete the zone */
+       lock_rw_unlock(&z->lock);
+       local_zone_delete(z);
+}
+
+int
+local_zones_add_RR(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;
+       int r;
+       if(!get_rr_nameclass(rr, &rr_name, &rr_class)) {
+               return 0;
+       }
+       labs = dname_count_size_labels(rr_name, &len);
+       lock_quick_lock(&zones->lock);
+       z = local_zones_lookup(zones, rr_name, len, labs, rr_class);
+       if(!z) {
+               z = local_zones_add_zone(zones, rr_name, len, labs, rr_class,
+                       local_zone_transparent);
+               if(!z) {
+                       lock_quick_unlock(&zones->lock);
+                       return 0;
+               }
+       }
+       lock_rw_wrlock(&z->lock);
+       lock_quick_unlock(&zones->lock);
+       free(rr_name);
+       r = lz_enter_rr_into_zone(z, buf, rr);
+       lock_rw_unlock(&z->lock);
+       return r;
+}
+
+/** returns true if the node is terminal so no deeper domain names exist */
+static int
+is_terminal(struct local_data* d)
+{
+       /* for empty nonterminals, the deeper domain names are sorted
+        * right after them, so simply check the next name in the tree 
+        */
+       struct local_data* n = (struct local_data*)rbtree_next(&d->node);
+       if(n == (struct local_data*)RBTREE_NULL)
+               return 1; /* last in tree, no deeper node */
+       if(dname_strict_subdomain(n->name, n->namelabs, d->name, d->namelabs))
+               return 0; /* there is a deeper node */
+       return 1;
+}
+
+/** delete empty terminals from tree when final data is deleted */
+static void 
+del_empty_term(struct local_zone* z, struct local_data* d, 
+       uint8_t* name, size_t len, int labs)
+{
+       while(d && d->rrsets == NULL && is_terminal(d)) {
+               /* is this empty nonterminal? delete */
+               /* note, no memory recycling in zone region */
+               (void)rbtree_delete(&z->data, d);
+
+               /* go up and to the next label */
+               if(dname_is_root(name))
+                       return;
+               dname_remove_label(&name, &len);
+               labs--;
+               d = lz_find_node(z, name, len, labs);
+       }
+}
+
+void local_zones_del_data(struct local_zones* zones, 
+       uint8_t* name, size_t len, int labs, uint16_t dclass)
+{
+       /* find zone */
+       struct local_zone* z;
+       struct local_data* d;
+       lock_quick_lock(&zones->lock);
+       z = local_zones_lookup(zones, name, len, labs, dclass);
+       if(!z) {
+               /* no such zone, we're done */
+               lock_quick_unlock(&zones->lock);
+               return;
+       }
+       lock_rw_wrlock(&z->lock);
+       lock_quick_unlock(&zones->lock);
+
+       /* find the domain */
+       d = lz_find_node(z, name, len, labs);
+       /* no memory recycling for zone deletions ... */
+       d->rrsets = NULL;
+       /* did we delete the soa record ? */
+       if(query_dname_compare(d->name, z->name) == 0)
+               z->soa = NULL;
+
+       /* cleanup the empty nonterminals for this name */
+       del_empty_term(z, d, name, len, labs);
+
+       lock_rw_unlock(&z->lock);
 }
index 4e6a54f98a0607e43ace14926748beca54eeb64d..742405c743f2c3f8416590c825108b475d5e8781 100644 (file)
@@ -42,6 +42,7 @@
 #ifndef SERVICES_LOCALZONE_H
 #define SERVICES_LOCALZONE_H
 #include "util/rbtree.h"
+#include "util/locks.h"
 struct ub_packed_rrset_key;
 struct regional;
 struct config_file;
@@ -74,6 +75,8 @@ enum localzone_type {
  * This tree is fixed at startup, so, readonly, no locks or mutexes necessary.
  */
 struct local_zones {
+       /** lock on the localzone tree */
+       lock_quick_t lock;
        /** rbtree of struct local_zone */
        rbtree_t ztree;
 };
@@ -97,6 +100,12 @@ struct local_zone {
         * uses 'dclass' to not conflict with c++ keyword class. */
        uint16_t dclass;
 
+       /** lock on the data in the structure
+        * For the node, parent, name, namelen, namelabs, dclass, you
+        * need to also hold the zones_tree lock to change them (or to
+        * delete this zone) */
+       lock_rw_t lock;
+
        /** how to process zone */
        enum localzone_type type;
 
@@ -151,6 +160,7 @@ void local_zones_delete(struct local_zones* zones);
 
 /**
  * Apply config settings; setup the local authoritative data. 
+ * Takes care of locking.
  * @param zones: is set up.
  * @param cfg: config data.
  * @return false on error.
@@ -182,6 +192,7 @@ void local_zone_delete(struct local_zone* z);
 
 /**
  * Lookup zone that contains the given name, class.
+ * User must lock the tree or result zone.
  * @param zones: the zones tree
  * @param name: dname to lookup
  * @param len: length of name.
@@ -194,12 +205,14 @@ struct local_zone* local_zones_lookup(struct local_zones* zones,
 
 /**
  * Debug helper. Print all zones 
+ * Takes care of locking.
  * @param zones: the zones tree
  */
 void local_zones_print(struct local_zones* zones);
 
 /**
  * Answer authoritatively for local zones.
+ * Takes care of locking.
  * @param zones: the stored zones (shared, read only).
  * @param qinfo: query info (parsed).
  * @param edns: edns info (parsed).
@@ -212,4 +225,74 @@ void local_zones_print(struct local_zones* zones);
 int local_zones_answer(struct local_zones* zones, struct query_info* qinfo,
        struct edns_data* edns, ldns_buffer* buf, struct regional* temp);
 
+/**
+ * Parse the string into localzone type.
+ *
+ * @param str: string to parse
+ * @param t: local zone type returned here.
+ * @return 0 on parse error.
+ */
+int local_zone_str2type(const char* str, enum localzone_type* t);
+
+/**
+ * Find zone that with exactly given name, class.
+ * User must lock the tree or result zone.
+ * @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 the exact local_zone or NULL.
+ */
+struct local_zone* local_zones_find(struct local_zones* zones, 
+       uint8_t* name, size_t len, int labs, uint16_t dclass);
+
+/**
+ * Add a new zone. Caller must hold the zones lock.
+ * Adjusts the other zones as well (parent pointers) after insertion.
+ * The zone must NOT exist (returns NULL and logs error).
+ * @param zones: the zones tree
+ * @param name: dname to add
+ * @param len: length of name.
+ * @param labs: labelcount of name.
+ * @param dclass: class to add.
+ * @param tp: type.
+ * @return local_zone or NULL on error, caller must printout memory error.
+ */
+struct local_zone* local_zones_add_zone(struct local_zones* zones, 
+       uint8_t* name, size_t len, int labs, uint16_t dclass, 
+       enum localzone_type tp);
+
+/**
+ * Delete a zone. Caller must hold the zones lock.
+ * Adjusts the other zones as well (parent pointers) after insertion.
+ * @param zones: the zones tree
+ * @param zone: the zone to delete from tree. Also deletes zone from memory.
+ */
+void local_zones_del_zone(struct local_zones* zones, struct local_zone* zone);
+
+/**
+ * Add RR data into the localzone data.
+ * Looks up the zone, if no covering zone, a transparent zone with the
+ * name of the RR is created.
+ * @param zones: the zones tree. Not locked by caller.
+ * @param rr: string with on RR.
+ * @param buf: buffer for scratch.
+ * @return false on failure.
+ */
+int local_zones_add_RR(struct local_zones* zones, const char* rr, 
+       ldns_buffer* buf);
+
+/**
+ * Remove data from domain name in the tree.
+ * All types are removed. No effect if zone or name does not exist.
+ * @param zones: zones tree.
+ * @param name: dname to remove
+ * @param len: length of name.
+ * @param labs: labelcount of name.
+ * @param dclass: class to remove.
+ */
+void local_zones_del_data(struct local_zones* zones, 
+       uint8_t* name, size_t len, int labs, uint16_t dclass);
+
 #endif /* SERVICES_LOCALZONE_H */
index b50410f73a8ab485b14cbafce0b39f97907b6b7f..53964ff52dbc55c5185bf50ce425a86f3d4ed40a 100644 (file)
@@ -62,7 +62,12 @@ usage()
        printf("  stop          stops the server\n");
        printf("  reload        reloads the server\n");
        printf("  stats         print statistics\n");
-       printf("  verbosity [number]    change logging detail\n");
+       printf("  verbosity [number]            change logging detail\n");
+       printf("  local_zone [name] [type]      add new local zone\n");
+       printf("  local_zone_remove [name]      remove local zone and its contents\n");
+       printf("  local_data [RR data...]       add local data, for example\n");
+       printf("                                local_data www.example.com A 192.0.2.1\n");
+       printf("  local_data_remove [name]      remove local RR data from name\n");
        printf("Version %s\n", PACKAGE_VERSION);
        printf("BSD licensed, see LICENSE in source package for details.\n");
        printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
index 4f5023269799ff5ca8e772e14946c9d067314a67..64873defc04738b2459b65a9a0fdad60e163c7e9 100644 (file)
@@ -487,9 +487,12 @@ read_entry(FILE* in, const char* name, int *lineno, uint32_t* default_ttl,
                        /* it must be a RR, parse and add to packet. */
                        ldns_rr* n = NULL;
                        ldns_status status;
-                       status = ldns_rr_new_frm_str(&n, parse, *default_ttl, 
-                               *origin, prev_rr);
-                       if (status != LDNS_STATUS_OK)
+                       if(add_section == LDNS_SECTION_QUESTION)
+                               status = ldns_rr_new_question_frm_str(
+                                       &n, parse, *origin, prev_rr);
+                       else status = ldns_rr_new_frm_str(&n, parse, 
+                               *default_ttl, *origin, prev_rr);
+                       if(status != LDNS_STATUS_OK)
                                error("%s line %d:\n\t%s: %s", name, *lineno,
                                        ldns_get_errorstr_by_id(status), parse);
                        ldns_pkt_push_rr(cur_reply->reply, add_section, n);
@@ -637,7 +640,7 @@ match_all(ldns_pkt* q, ldns_pkt* p, bool mttl)
        { verbose(3, "allmatch: nscount different"); return 0;}
        if(ldns_pkt_arcount(q) != ldns_pkt_arcount(p))
        { verbose(3, "allmatch: arcount different"); return 0;}
-       if(!match_list(ldns_pkt_question(q), ldns_pkt_question(p), mttl))
+       if(!match_list(ldns_pkt_question(q), ldns_pkt_question(p), 0))
        { verbose(3, "allmatch: qd section different"); return 0;}
        if(!match_list(ldns_pkt_answer(q), ldns_pkt_answer(p), mttl))
        { verbose(3, "allmatch: an section different"); return 0;}
index 1f7d9c07ec6f6b7056569a2e3ab0234d0cbaa59e..1873b89361277c0a81a2a0c1e95f217ddb291195 100644 (file)
@@ -156,7 +156,7 @@ val_init(struct module_env* env, int id)
        val_env->permissive_mode = 0;
        lock_basic_init(&val_env->bogus_lock);
        lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus,
-               sizeof(val->env->num_rrset_bogus));
+               sizeof(val_env->num_rrset_bogus));
        if(!val_apply_cfg(env, val_env, env->cfg)) {
                log_err("validator: could not apply configuration settings.");
                return 0;