add remove local data and local zone with remote control

(ldns-testpkts sync with ldns trunk).


git-svn-id: file:///svn/unbound/trunk@1261 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2008-09-19 14:49:29 +00:00
parent c77be35670
commit cf4b56d883
10 changed files with 586 additions and 52 deletions

View file

@ -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.

View file

@ -54,8 +54,10 @@
#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);
}

View 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.

View file

@ -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

View 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

View 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);
/* add to rbtree */
if(!rbtree_insert(&zones->ztree, &z->node)) {
log_warn("duplicate local-zone");
local_zone_delete(z);
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 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;
key.node.key = &key;
key.name = nm;
key.namelen = nmlen;
key.namelabs = nmlabs;
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 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);
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);
}

View 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 */

View file

@ -63,6 +63,11 @@ usage()
printf(" reload reloads the server\n");
printf(" stats print statistics\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);

View 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;}

View 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;