- Work on local root zone code.

git-svn-id: file:///svn/unbound/trunk@4376 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2017-10-17 15:16:31 +00:00
parent c5c2cb13d4
commit b37bc47eaa
14 changed files with 4134 additions and 2991 deletions

View file

@ -823,9 +823,11 @@ shm_main.lo shm_main.o: $(srcdir)/util/shm_side/shm_main.c config.h $(srcdir)/ut
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h
authzone.lo authzone.o: $(srcdir)/services/authzone.c config.h $(srcdir)/services/authzone.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/config_file.h $(srcdir)/services/cache/dns.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgencode.h \
$(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/dnscrypt/cert.h $(srcdir)/util/config_file.h \
$(srcdir)/util/module.h $(srcdir)/services/cache/dns.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h \
$(srcdir)/sldns/wire2str.h $(srcdir)/sldns/parseutil.h $(srcdir)/validator/val_nsec3.h \
$(srcdir)/validator/val_secalgo.h
fptr_wlist.lo fptr_wlist.o: $(srcdir)/util/fptr_wlist.c config.h $(srcdir)/util/fptr_wlist.h \
@ -1313,8 +1315,8 @@ unbound-checkconf.lo unbound-checkconf.o: $(srcdir)/smallapp/unbound-checkconf.c
$(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/iterator/iter_fwd.h \
$(srcdir)/util/rbtree.h $(srcdir)/iterator/iter_hints.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h $(srcdir)/services/localzone.h \
$(srcdir)/services/view.h $(srcdir)/respip/respip.h $(srcdir)/sldns/sbuffer.h $(PYTHONMOD_HEADER) \
$(srcdir)/edns-subnet/subnet-whitelist.h
$(srcdir)/services/view.h $(srcdir)/services/authzone.h $(srcdir)/respip/respip.h $(srcdir)/sldns/sbuffer.h \
$(PYTHONMOD_HEADER) $(srcdir)/edns-subnet/subnet-whitelist.h
worker_cb.lo worker_cb.o: $(srcdir)/smallapp/worker_cb.c config.h $(srcdir)/libunbound/context.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \

View file

@ -16,6 +16,7 @@
The `dnscrypt-provider-cert-rotated` allow to instruct unbound to not
publish the cert as part of the DNS's provider_name's TXT answer.
- Better documentation for cache-max-negative-ttl.
- Work on local root zone code.
10 October 2017: Wouter
- tag 1.6.7

View file

@ -808,6 +808,30 @@ remote-control:
# name: "example.org"
# forward-host: fwd.example.com
# Authority zones
# The data for these zones is kept locally, from a file or downloaded.
# The data can be served to downstream clients, or used instead of the
# upstream (which saves a lookup to the upstream). The first example
# has a copy of the root for local usage. The second serves example.org
# authoritatively. zonefile: reads from file (and writes to it if you also
# download it), master: fetches with AXFR, url: fetches zonefile over http.
# auth-zone:
# name: "."
# for-downstream: no
# for-upstream: yes
# master: b.root-servers.net
# master: c.root-servers.net
# master: e.root-servers.net
# master: f.root-servers.net
# master: g.root-servers.net
# master: k.root-servers.net
# auth-zone:
# name: "example.org"
# for-downstream: yes
# for-upstream: yes
# zonefile: "example.org.zone"
# url: "http://www.example.com/example.org.zone"
# Views
# Create named views. Name must be unique. Map views to requests using
# the access-control-view option. Views can contain zero or more local-zone

View file

@ -44,12 +44,16 @@
#include "config.h"
#include "services/authzone.h"
#include "util/data/dname.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
#include "util/data/msgencode.h"
#include "util/data/packed_rrset.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "util/netevent.h"
#include "util/config_file.h"
#include "util/log.h"
#include "util/module.h"
#include "services/cache/dns.h"
#include "sldns/rrdef.h"
#include "sldns/pkthdr.h"
@ -252,9 +256,11 @@ struct auth_zones* auth_zones_create(void)
return NULL;
}
rbtree_init(&az->ztree, &auth_zone_cmp);
rbtree_init(&az->xtree, &auth_xfer_cmp);
lock_rw_init(&az->lock);
lock_protect(&az->lock, &az->ztree, sizeof(az->ztree));
/* also lock protects the rbnode's in struct auth_zone */
lock_protect(&az->lock, &az->xtree, sizeof(az->xtree));
/* also lock protects the rbnode's in struct auth_zone, auth_xfer */
return az;
}
@ -285,6 +291,23 @@ int auth_data_cmp(const void* z1, const void* z2)
b->namelabs, &m);
}
int auth_xfer_cmp(const void* z1, const void* z2)
{
/* first sort on class, so that hierarchy can be maintained within
* a class */
struct auth_xfer* a = (struct auth_xfer*)z1;
struct auth_xfer* b = (struct auth_xfer*)z2;
int m;
if(a->dclass != b->dclass) {
if(a->dclass < b->dclass)
return -1;
return 1;
}
/* sorted such that higher zones sort before lower zones (their
* contents) */
return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m);
}
/** delete auth rrset node */
static void
auth_rrset_delete(struct auth_rrset* rrset)
@ -374,6 +397,19 @@ auth_zone_find(struct auth_zones* az, uint8_t* nm, size_t nmlen,
return (struct auth_zone*)rbtree_search(&az->ztree, &key);
}
struct auth_xfer*
auth_xfer_find(struct auth_zones* az, uint8_t* nm, size_t nmlen,
uint16_t dclass)
{
struct auth_xfer key;
key.node.key = &key;
key.dclass = dclass;
key.name = nm;
key.namelen = nmlen;
key.namelabs = dname_count_labels(nm);
return (struct auth_xfer*)rbtree_search(&az->xtree, &key);
}
/** find an auth zone or sorted less-or-equal, return true if exact */
static int
auth_zone_find_less_equal(struct auth_zones* az, uint8_t* nm, size_t nmlen,
@ -437,6 +473,23 @@ auth_zones_find_or_add_zone(struct auth_zones* az, char* name)
return z;
}
/** find or create xfer zone with name str. caller must have lock on az.
* returns a locked xfer */
static struct auth_xfer*
auth_zones_find_or_add_xfer(struct auth_zones* az, struct auth_zone* z)
{
struct auth_xfer* x;
x = auth_xfer_find(az, z->name, z->namelen, z->dclass);
if(!x) {
/* not found, create the zone */
x = auth_xfer_create(az, z);
lock_basic_lock(&x->lock);
} else {
lock_basic_lock(&x->lock);
}
return x;
}
int
auth_zone_set_zonefile(struct auth_zone* z, char* zonefile)
{
@ -1052,6 +1105,13 @@ auth_zone_read_zonefile(struct auth_zone* z)
in = fopen(z->zonefile, "r");
if(!in) {
char* n = sldns_wire2str_dname(z->name, z->namelen);
if(z->zone_is_slave && errno == ENOENT) {
/* we fetch the zone contents later, no file yet */
verbose(VERB_ALGO, "no zonefile %s for %s",
z->zonefile, n?n:"error");
free(n);
return 1;
}
log_err("cannot open zonefile %s for %s: %s",
z->zonefile, n?n:"error", strerror(errno));
free(n);
@ -1185,68 +1245,177 @@ auth_zones_read_zones(struct auth_zones* az)
return 1;
}
/** set str2list with (zonename, zonefile) config items and create zones */
static int
auth_zones_cfg_zonefile(struct auth_zones* az, struct config_str2list* zlist)
/** setup all auth zones */
int
auth_zones_setup_zones(struct auth_zones* az, struct module_env* env)
{
struct auth_zone* z;
while(zlist) {
lock_rw_wrlock(&az->lock);
if(!(z=auth_zones_find_or_add_zone(az, zlist->str))) {
struct auth_xfer* x;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_wrlock(&z->lock);
x = auth_xfer_find(az, z->name, z->namelen, z->dclass);
if(x) {
lock_basic_lock(&x->lock);
}
if(!auth_xfer_setup(z, x, env)) {
if(x) {
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&z->lock);
lock_rw_unlock(&az->lock);
return 0;
}
lock_rw_unlock(&az->lock);
if(!auth_zone_set_zonefile(z, zlist->str2)) {
lock_rw_unlock(&z->lock);
return 0;
if(x) {
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&z->lock);
zlist = zlist->next;
}
lock_rw_unlock(&az->lock);
return 1;
}
/** set str2list with (zonename, fallback) config items and create zones */
/** set config items and create zones */
static int
auth_zones_cfg_fallback(struct auth_zones* az, struct config_str2list* zlist)
auth_zones_cfg(struct auth_zones* az, struct config_auth* c)
{
struct auth_zone* z;
while(zlist) {
lock_rw_wrlock(&az->lock);
if(!(z=auth_zones_find_or_add_zone(az, zlist->str))) {
lock_rw_unlock(&az->lock);
return 0;
}
struct auth_xfer* x = NULL;
/* create zone */
lock_rw_wrlock(&az->lock);
if(!(z=auth_zones_find_or_add_zone(az, c->name))) {
lock_rw_unlock(&az->lock);
if(!auth_zone_set_fallback(z, zlist->str2)) {
return 0;
}
if(c->masters || c->urls) {
if(!(x=auth_zones_find_or_add_xfer(az, z))) {
lock_rw_unlock(&az->lock);
lock_rw_unlock(&z->lock);
return 0;
}
lock_rw_unlock(&z->lock);
zlist = zlist->next;
}
if(c->for_downstream)
az->have_downstream = 1;
lock_rw_unlock(&az->lock);
/* set options */
if(!auth_zone_set_zonefile(z, c->zonefile)) {
if(x) {
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&z->lock);
return 0;
}
z->for_downstream = c->for_downstream;
z->for_upstream = c->for_upstream;
/* TODO fallback option */
//if(!auth_zone_set_fallback(z, zlist->str2)) {
/* TODO other options */
/* xfer zone */
if(x) {
z->zone_is_slave = 1;
/* set options on xfer zone */
if(!xfer_set_masters(&x->task_probe->masters, c)) {
lock_basic_unlock(&x->lock);
lock_rw_unlock(&z->lock);
return 0;
}
if(!xfer_set_masters(&x->task_transfer->masters, c)) {
lock_basic_unlock(&x->lock);
lock_rw_unlock(&z->lock);
return 0;
}
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&z->lock);
return 1;
}
int auth_zones_apply_config(struct auth_zones* az, struct config_file* cfg)
int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg)
{
(void)cfg;
/* TODO cfg str2lists */
/* create config items for
* auth-zone: name: "example.com"
* zonefile: "zones/example.com"
* fallback: yes
*/
if(!auth_zones_cfg_zonefile(az, NULL /*cfg->auth_zones*/))
return 0;
if(!auth_zones_cfg_fallback(az, NULL /*cfg->auth_zones*/))
return 0;
struct config_auth* p;
for(p = cfg->auths; p; p = p->next) {
if(!p->name || p->name[0] == 0) {
log_warn("auth-zone without a name, skipped");
continue;
}
if(!auth_zones_cfg(az, p)) {
log_err("cannot config auth zone %s", p->name);
return 0;
}
}
if(!auth_zones_read_zones(az))
return 0;
return 1;
}
/** delete chunks
* @param at: transfer structure with chunks list. The chunks and their
* data are freed.
*/
void
auth_chunks_delete(struct auth_transfer* at)
{
if(at->chunks_first) {
struct auth_chunk* c, *cn;
c = at->chunks_first;
while(c) {
cn = c->next;
free(c->data);
free(c);
c = cn;
}
}
at->chunks_first = NULL;
at->chunks_last = NULL;
}
/** free the masters list */
static void
auth_free_masters(struct auth_master* list)
{
struct auth_master* n;
while(list) {
n = list->next;
free(list->host);
free(list->file);
free(list);
list = n;
}
}
/** delete auth xfer structure
* @param xfr: delete this xfer and its tasks.
*/
void
auth_xfer_delete(struct auth_xfer* xfr)
{
if(!xfr) return;
lock_basic_destroy(&xfr->lock);
free(xfr->name);
if(xfr->task_nextprobe) {
comm_timer_delete(xfr->task_nextprobe->timer);
free(xfr->task_nextprobe);
}
if(xfr->task_probe) {
auth_free_masters(xfr->task_probe->masters);
comm_point_delete(xfr->task_probe->cp);
free(xfr->task_probe);
}
if(xfr->task_transfer) {
auth_free_masters(xfr->task_transfer->masters);
comm_point_delete(xfr->task_transfer->cp);
if(xfr->task_transfer->chunks_first) {
auth_chunks_delete(xfr->task_transfer);
}
free(xfr->task_transfer);
}
free(xfr);
}
/** helper traverse to delete zones */
static void
auth_zone_del(rbnode_type* n, void* ATTR_UNUSED(arg))
@ -1255,11 +1424,20 @@ auth_zone_del(rbnode_type* n, void* ATTR_UNUSED(arg))
auth_zone_delete(z);
}
/** helper traverse to delete xfer zones */
static void
auth_xfer_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct auth_xfer* z = (struct auth_xfer*)n->key;
auth_xfer_delete(z);
}
void auth_zones_delete(struct auth_zones* az)
{
if(!az) return;
lock_rw_destroy(&az->lock);
traverse_postorder(&az->ztree, auth_zone_del, NULL);
traverse_postorder(&az->xtree, auth_xfer_del, NULL);
free(az);
}
@ -1848,7 +2026,7 @@ az_nsec3_find_cover(struct auth_zone* z, uint8_t* nm, size_t nmlen,
!az_domain_rrset(node, LDNS_RR_TYPE_NSEC3)) {
node = (struct auth_data*)rbtree_previous(&node->node);
}
if((rbnode_type*)node == RBTREE_NULL)
if((rbnode_type*)node == RBTREE_NULL)
node = NULL;
return node;
}
@ -2367,3 +2545,301 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo,
lock_rw_unlock(&z->lock);
return r;
}
/** encode auth answer */
static void
auth_answer_encode(struct query_info* qinfo, struct module_env* env,
struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
struct dns_msg* msg)
{
uint16_t udpsize;
udpsize = edns->udp_size;
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_local_call(env, qinfo, NULL, msg->rep,
FLAGS_GET_RCODE(msg->rep->flags), edns, temp)
|| !reply_info_answer_encode(qinfo, msg->rep,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns,
(int)(edns->bits&EDNS_DO), 0)) {
error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
}
}
/** encode auth error answer */
static void
auth_error_encode(struct query_info* qinfo, struct module_env* env,
struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
int rcode)
{
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL,
rcode, edns, temp))
edns->opt_list = NULL;
error_encode(buf, rcode|BIT_AA, qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
}
int auth_zones_answer(struct auth_zones* az, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns, struct sldns_buffer* buf,
struct regional* temp)
{
struct dns_msg* msg = NULL;
struct auth_zone* z;
int r;
int fallback = 0;
lock_rw_rdlock(&az->lock);
if(!az->have_downstream) {
/* no downstream auth zones */
lock_rw_unlock(&az->lock);
return 0;
}
z = auth_zones_find_zone(az, qinfo);
if(!z) {
/* no zone above it */
lock_rw_unlock(&az->lock);
return 0;
}
lock_rw_unlock(&az->lock);
if(!z->for_downstream) {
lock_rw_unlock(&z->lock);
return 0;
}
/* answer it from zone z */
r = auth_zone_generate_answer(z, qinfo, temp, &msg, &fallback);
lock_rw_unlock(&z->lock);
if(fallback) {
/* fallback to regular answering (recursive) */
return 0;
}
/* encode answer */
if(!r)
auth_error_encode(qinfo, env, edns, buf, temp,
LDNS_RCODE_SERVFAIL);
else auth_answer_encode(qinfo, env, edns, buf, temp, msg);
return 1;
}
/** the current transfer has finished, apply the results.
* set timer for future probe. See if zone is expired now. */
void
xfr_master_transferresult(struct auth_xfer* xfr)
{
(void)xfr;
/* TODO */
}
/** the current probe has finished, inspect the results.
* move on to the next master or start a transfer, or at last master,
* set timer for future probe. See if zone is expired now. */
void
xfr_master_proberesult(struct auth_xfer* xfr)
{
(void)xfr;
/* TODO */
}
/** with current master selected, start the probe, or transfer */
int
xfr_master_start(struct auth_xfer* xfr)
{
(void)xfr;
/* TODO */
return 0;
}
/** Find auth_zone SOA and populate the values in xfr(soa values). */
static int
xfr_find_soa(struct auth_zone* z, struct auth_xfer* xfr)
{
struct auth_data* apex;
struct auth_rrset* soa;
struct packed_rrset_data* d;
apex = az_find_name(z, z->name, z->namelen);
if(!apex) return 0;
soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA);
if(!soa || soa->data->count==0)
return 0; /* no RRset or no RRs in rrset */
if(soa->data->rr_len[0] < 2+4*5) return 0; /* SOA too short */
/* SOA record ends with serial, refresh, retry, expiry, minimum,
* as 4 byte fields */
d = soa->data;
xfr->have_zone = 1;
xfr->serial = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-20));
xfr->retry = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-16));
xfr->refresh = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-12));
xfr->expiry = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-8));
/* soa minimum at d->rr_len[0]-4 */
return 1;
}
/** determine next timeout for auth_xfer. Also (re)sets timer. */
void
xfr_set_timeout(struct auth_xfer* xfr)
{
(void)xfr;
/* TODO */
}
/**
* malloc the xfer and tasks
* @param z: auth_zone with name of zone.
*/
static struct auth_xfer*
auth_xfer_new(struct auth_zone* z)
{
struct auth_xfer* xfr;
xfr = (struct auth_xfer*)calloc(1, sizeof(*xfr));
if(!xfr) return NULL;
xfr->name = memdup(z->name, z->namelen);
if(!xfr->name) {
free(xfr);
return NULL;
}
xfr->node.key = xfr;
xfr->namelen = z->namelen;
xfr->namelabs = z->namelabs;
xfr->dclass = z->dclass;
xfr->task_nextprobe = (struct auth_nextprobe*)calloc(1,
sizeof(struct auth_nextprobe));
if(!xfr->task_nextprobe) {
free(xfr->name);
free(xfr);
return NULL;
}
xfr->task_nextprobe->workernum = -1;
xfr->task_probe = (struct auth_probe*)calloc(1,
sizeof(struct auth_probe));
if(!xfr->task_probe) {
free(xfr->task_nextprobe);
free(xfr->name);
free(xfr);
return NULL;
}
xfr->task_probe->workernum = -1;
xfr->task_transfer = (struct auth_transfer*)calloc(1,
sizeof(struct auth_transfer));
if(!xfr->task_transfer) {
free(xfr->task_probe);
free(xfr->task_nextprobe);
free(xfr->name);
free(xfr);
return NULL;
}
xfr->task_transfer->workernum = -1;
lock_basic_init(&xfr->lock);
lock_protect(&xfr->lock, xfr, sizeof(*xfr));
lock_protect(&xfr->lock, &xfr->task_nextprobe->workernum,
sizeof(xfr->task_nextprobe->workernum));
lock_protect(&xfr->lock, &xfr->task_probe->workernum,
sizeof(xfr->task_probe->workernum));
lock_protect(&xfr->lock, &xfr->task_transfer->workernum,
sizeof(xfr->task_transfer->workernum));
return xfr;
}
/**
* Setup auth_xfer zone
* This populates the have_zone, soa values, next_probe and so on times.
* Doesn't do network traffic yet, sets the timeout.
*/
int
auth_xfer_setup(struct auth_zone* z, struct auth_xfer* x, struct module_env* env)
{
if(!z || !x) return 1;
if(!xfr_find_soa(z, x)) {
return 1;
}
/* nextprobe setup */
x->task_nextprobe->next_probe = 0;
if(x->have_zone)
x->task_nextprobe->lease_time = *env->now;
/* nothing for probe and transfer tasks */
return 1;
}
/** Create auth_xfer structure.
* This populates the have_zone, soa values, next_probe and so on times.
* and sets the timeout, if a zone transfer is needed a short timeout is set.
* For that the auth_zone itself must exist (and read in zonefile)
* returns false on alloc failure. */
struct auth_xfer*
auth_xfer_create(struct auth_zones* az, struct auth_zone* z)
{
struct auth_xfer* xfr;
/* malloc it */
xfr = auth_xfer_new(z);
if(!xfr) {
log_err("malloc failure");
return NULL;
}
/* insert in tree */
(void)rbtree_insert(&az->xtree, &xfr->node);
return xfr;
}
/** create new auth_master structure */
static struct auth_master*
auth_master_new(struct auth_master*** list)
{
struct auth_master *m;
m = (struct auth_master*)calloc(1, sizeof(*m));
if(!m) {
log_err("malloc failure");
return NULL;
}
/* set first pointer to m, or next pointer of previous element to m */
(**list) = m;
/* store m's next pointer as future point to store at */
(*list) = &(m->next);
return m;
}
int
xfer_set_masters(struct auth_master** list, struct config_auth* c)
{
struct auth_master* m;
struct config_strlist* p;
/* list points to the first, or next pointer for the new element */
while(*list) {
list = &( (*list)->next );
}
for(p = c->masters; p; p = p->next) {
m = auth_master_new(&list);
m->ixfr = 1;
m->host = strdup(p->str);
if(!m->host) {
log_err("malloc failure");
return 0;
}
}
for(p = c->urls; p; p = p->next) {
m = auth_master_new(&list);
m->http = 1;
/* TODO parse url, get host, file */
m->host = strdup(p->str);
if(!m->host) {
log_err("malloc failure");
return 0;
}
}
return 1;
}

View file

@ -48,17 +48,33 @@
struct ub_packed_rrset_key;
struct regional;
struct config_file;
struct config_auth;
struct query_info;
struct dns_msg;
struct edns_data;
struct module_env;
struct worker;
struct comm_point;
struct comm_timer;
struct auth_rrset;
struct auth_nextprobe;
struct auth_probe;
struct auth_transfer;
struct auth_master;
struct auth_chunk;
/**
* Authoritative zones, shared.
*/
struct auth_zones {
/** lock on the authzone tree */
/** lock on the authzone trees */
lock_rw_type lock;
/** rbtree of struct auth_zone */
rbtree_type ztree;
/** rbtree of struct auth_xfer */
rbtree_type xtree;
/** do we have downstream enabled */
int have_downstream;
};
/**
@ -89,10 +105,21 @@ struct auth_zone {
* rbtree of struct auth_data */
rbtree_type data;
/* zonefile name (or NULL for no zonefile) */
/** zonefile name (or NULL for no zonefile) */
char* zonefile;
/* fallback to the internet on failure or ttl-expiry of auth zone */
/** fallback to the internet on failure or ttl-expiry of auth zone */
int fallback_enabled;
/** the zone has expired (enabled by the xfer worker), fallback
* happens if that option is enabled. */
int zone_expired;
/** zone is a slave zone (it has masters) */
int zone_is_slave;
/** for downstream: this zone answers queries towards the downstream
* clients */
int for_downstream;
/** for upstream: this zone answers queries that unbound intends to
* send upstream. */
int for_upstream;
};
/**
@ -127,6 +154,217 @@ struct auth_rrset {
struct packed_rrset_data* data;
};
/**
* Authoritative zone transfer structure.
* Create and destroy needs the auth_zones* biglock.
* The structure consists of different tasks. Each can be unowned (-1) or
* owner by a worker (worker-num). A worker can pick up a task and then do
* it. This means the events (timeouts, sockets) are for that worker.
*
* (move this to tasks).
* They don't have locks themselves, the worker (that owns it) uses it,
* also as part of callbacks, hence it has separate zonename pointers for
* lookup in the main zonetree. If the zone has no transfers, this
* structure is not created.
*/
struct auth_xfer {
/** rbtree node, key is name and class */
rbnode_type node;
/** lock on this structure, and on the workernum elements of the
* tasks. First hold the tree-lock in auth_zones, find the auth_xfer,
* lock this lock. Then a worker can reassign itself to fill up
* one of the tasks.
* Once it has the task assigned to it, the worker can access the
* other elements of the task structure without a lock, because that
* is necessary for the eventloop and callbacks from that. */
lock_basic_type lock;
/** zone name, in uncompressed wireformat */
uint8_t* name;
/** length of zone name */
size_t namelen;
/** number of labels in zone name */
int namelabs;
/** the class of this zone, in host byteorder.
* uses 'dclass' to not conflict with c++ keyword class. */
uint16_t dclass;
/** task to wait for next-probe-timeout,
* once timeouted, see if a SOA probe is needed, or already
* in progress */
struct auth_nextprobe* task_nextprobe;
/** task for SOA probe. Check if the zone can be updated */
struct auth_probe* task_probe;
/** Task for transfer. Transferring and updating the zone. This
* includes trying (potentially) several upstream masters. Downloading
* and storing the zone */
struct auth_transfer* task_transfer;
/** a notify was received, but a zone transfer or probe was already
* acted on.
* However, the zone transfer could signal a newer serial number.
* The serial number of that notify is saved below. The transfer and
* probe tasks should check this once done to see if they need to
* restart the transfer task for the newer notify serial.
* Hold the lock to access this member (and the serial).
*/
int notify_received;
/** serial number of the notify */
uint32_t notify_serial;
/* protected by the lock on the structure, information about
* the loaded authority zone. */
/** is the zone currently considered expired? after expiry also older
* serial numbers are allowed (not just newer) */
int zone_expired;
/** do we have a zone (if 0, no zone data at all) */
int have_zone;
/** current serial (from SOA), if we have no zone, 0 */
uint32_t serial;
/** retry time (from SOA), time to wait with next_probe
* if no master responds */
time_t retry;
/** refresh time (from SOA), time to wait with next_probe
* if everything is fine */
time_t refresh;
/** expiry time (from SOA), time until zone data is not considered
* valid any more, if no master responds within this time, either
* with the current zone or a new zone. */
time_t expiry;
};
/**
* The next probe task.
* This task consists of waiting for the probetimeout. It is a task because
* it needs an event in the eventtable. Once the timeout has passed, that
* worker can (potentially) become the auth_probe worker, or if another worker
* is already doing that, do nothing. Tasks becomes unowned.
* The probe worker, if it detects nothing has to be done picks up this task,
* if unowned.
*/
struct auth_nextprobe {
/** worker num (or -1 unowned) that is performing this task */
int workernum;
/* Worker pointer. Used by the worker during callbacks. */
struct worker* worker;
/** Timeout for next probe (for SOA) */
time_t next_probe;
/** zone lease start time (start+expiry is expiration time).
* this is renewed every SOA probe and transfer. On zone load
* from zonefile it is also set (with probe set soon to check) */
time_t lease_time;
/** timeout callback for next_probe or expiry(if that is sooner).
* it is on the worker's event_base */
struct comm_timer* timer;
};
/**
* The probe task.
* Send a SOA UDP query to see if the zone needs to be updated (or similar,
* potential, HTTP probe query) and check serial number.
* If yes, start the auth_transfer task. If no, make sure auth_nextprobe
* timeout wait task is running.
* Needs to be a task, because the UDP query needs an event entry.
* This task could also be started by eg. a NOTIFY being received, even though
* another worker is performing the nextprobe task (and that worker keeps
* waiting uninterrupted).
*/
struct auth_probe {
/** worker num (or -1 unowned) that is performing this task */
int workernum;
/* Worker pointer. Used by the worker during callbacks. */
struct worker* worker;
/** list of upstream masters for this zone, from config */
struct auth_master* masters;
/** once notified, or the timeout has been reached. a scan starts. */
/** the scan specific target (notify source), or NULL if none */
struct auth_master* scan_specific;
/** scan tries all the upstream masters. the scan current target.
* or NULL if not working on sequential scan */
struct auth_master* scan_target;
/** the SOA probe udp event.
* on the workers event base. */
struct comm_point* cp;
};
/**
* The transfer task.
* Once done, make sure the nextprobe waiting task is running, whether done
* with failure or success. If failure, use shorter timeout for wait time.
*/
struct auth_transfer {
/** worker num (or -1 unowned) that is performing this task */
int workernum;
/* Worker pointer. Used by the worker during callbacks. */
struct worker* worker;
/** xfer data that has been transferred, the data is applied
* once the transfer has completed correctly */
struct auth_chunk* chunks_first;
/** last element in chunks list (to append new data at the end) */
struct auth_chunk* chunks_last;
/** list of upstream masters for this zone, from config */
struct auth_master* masters;
/** once notified, or the timeout has been reached. a scan starts. */
/** the scan specific target (notify source), or NULL if none */
struct auth_master* scan_specific;
/** scan tries all the upstream masters. the scan current target.
* or NULL if not working on sequential scan */
struct auth_master* scan_target;
/** the zone transfer in progress (or NULL if in scan). It is
* from this master */
struct auth_master* master;
/** failed ixfr transfer, retry with axfr (to the current master),
* the IXFR was 'REFUSED', 'SERVFAIL', 'NOTIMPL' or the contents of
* the IXFR did not apply cleanly (out of sync, delete of nonexistent
* data or add of duplicate data). Flag is cleared once the retry
* with axfr is done. */
int ixfr_fail;
/** the transfer (TCP) to the master.
* on the workers event base. */
struct comm_point* cp;
};
/** auth zone master upstream, and the config settings for it */
struct auth_master {
/** next master in list */
struct auth_master* next;
/** master IP address (and port), or hostname, string */
char* host;
/** for http, filename */
char* file;
/** use HTTP for this master */
int http;
/** use IXFR for this master */
int ixfr;
/** use ssl for channel */
int ssl;
};
/** auth zone master zone transfer data chunk */
struct auth_chunk {
/** next chunk in list */
struct auth_chunk* next;
/** the data from this chunk, this is what was received.
* for an IXFR that means results from comm_net tcp actions,
* packets. also for an AXFR. For HTTP a zonefile chunk. */
uint8_t* data;
/** length of allocated data */
size_t len;
};
/**
* Create auth zones structure
*/
@ -135,7 +373,7 @@ struct auth_zones* auth_zones_create(void);
/**
* Apply configuration to auth zones. Reads zonefiles.
*/
int auth_zones_apply_config(struct auth_zones* az, struct config_file* cfg);
int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg);
/**
* Delete auth zones structure
@ -170,6 +408,20 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo,
struct regional* region, struct dns_msg** msg, int* fallback,
uint8_t* dp_nm, size_t dp_nmlen);
/**
* Answer query from auth zone. Create authoritative answer.
* @param az: auth zones structure.
* @param env: the module environment.
* @param qinfo: query info (parsed).
* @param edns: edns info (parsed).
* @param buf: buffer with query ID and flags, also for reply.
* @param temp: temporary storage region.
* @return false if not answered
*/
int auth_zones_answer(struct auth_zones* az, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns, struct sldns_buffer* buf,
struct regional* temp);
/**
* Find the auth zone that is above the given qname.
* Return NULL when there is no auth_zone above the give name, otherwise
@ -185,6 +437,11 @@ struct auth_zone* auth_zones_find_zone(struct auth_zones* az,
struct auth_zone* auth_zone_find(struct auth_zones* az, uint8_t* nm,
size_t nmlen, uint16_t dclass);
/** find an xfer zone by name (exact match by name or NULL returned) */
struct auth_xfer* auth_xfer_find(struct auth_zones* az, uint8_t* nm,
size_t nmlen, uint16_t dclass);
/** create an auth zone. returns wrlocked zone. caller must have wrlock
* on az. returns NULL on malloc failure */
struct auth_zone* auth_zone_create(struct auth_zones* az, uint8_t* nm,
@ -206,4 +463,41 @@ int auth_zone_cmp(const void* z1, const void* z2);
/** compare auth_data for sorted rbtree */
int auth_data_cmp(const void* z1, const void* z2);
/** compare auth_xfer for sorted rbtree */
int auth_xfer_cmp(const void* z1, const void* z2);
/** Create auth_xfer structure.
* Caller must have wrlock on az. Returns locked xfer zone.
* @param az: zones structure.
* @param z: zone with name and class
* @return xfer zone or NULL
*/
struct auth_xfer* auth_xfer_create(struct auth_zones* az, struct auth_zone* z);
/**
* Set masters in auth xfer structure from config.
* @param list: pointer to start of list. The malloced list is returned here.
* @param c: the config items to copy over.
* @return false on failure.
*/
int xfer_set_masters(struct auth_master** list, struct config_auth* c);
/**
* Setup auth_xfer zone. Populates timeouts.
* @param z: locked by caller, and modified for setup
* @param x: locked by caller, and modified, timers and timeouts.
* @param env: module env with time.
* @return false on failure.
*/
int auth_xfer_setup(struct auth_zone* z, struct auth_xfer* xfr,
struct module_env* env);
/**
* Setup all zones
* @param az: auth zones structure
* @param env: module env with time.
* @return false on failure.
*/
int auth_zones_setup_zones(struct auth_zones* az, struct module_env* env);
#endif /* SERVICES_AUTHZONE_H */

View file

@ -54,6 +54,7 @@
#include "validator/validator.h"
#include "services/localzone.h"
#include "services/view.h"
#include "services/authzone.h"
#include "respip/respip.h"
#include "sldns/sbuffer.h"
#ifdef HAVE_GETOPT_H
@ -573,6 +574,17 @@ check_hints(struct config_file* cfg)
hints_delete(hints);
}
/** check auth zones */
static void
check_auth(struct config_file* cfg)
{
struct auth_zones* az = auth_zones_create();
if(!az || !auth_zones_apply_cfg(az, cfg)) {
fatal_exit("Could not setup authority zones");
}
auth_zones_delete(az);
}
/** check config file */
static void
checkconf(const char* cfgfile, const char* opt, int final)
@ -607,6 +619,7 @@ checkconf(const char* cfgfile, const char* opt, int final)
#endif
check_fwd(cfg);
check_hints(cfg);
check_auth(cfg);
printf("unbound-checkconf: no errors in %s\n", cfgfile);
config_delete(cfg);
}

View file

@ -177,6 +177,7 @@ config_create(void)
cfg->out_ifs = NULL;
cfg->stubs = NULL;
cfg->forwards = NULL;
cfg->auths = NULL;
#ifdef CLIENT_SUBNET
cfg->client_subnet = NULL;
cfg->client_subnet_zone = NULL;
@ -634,7 +635,7 @@ int config_set_option(struct config_file* cfg, const char* opt,
* interface, outgoing-interface, access-control,
* stub-zone, name, stub-addr, stub-host, stub-prime
* forward-first, stub-first, forward-ssl-upstream,
* stub-ssl-upstream, forward-zone,
* stub-ssl-upstream, forward-zone, auth-zone
* name, forward-addr, forward-host,
* ratelimit-for-domain, ratelimit-below-domain,
* local-zone-tag, access-control-view,
@ -1164,6 +1165,28 @@ config_deltrplstrlist(struct config_str3list* p)
}
}
void
config_delauth(struct config_auth* p)
{
if(!p) return;
free(p->name);
config_delstrlist(p->masters);
config_delstrlist(p->urls);
free(p->zonefile);
free(p);
}
void
config_delauths(struct config_auth* p)
{
struct config_auth* np;
while(p) {
np = p->next;
config_delauth(p);
p = np;
}
}
void
config_delstub(struct config_stub* p)
{
@ -1249,6 +1272,7 @@ config_delete(struct config_file* cfg)
config_del_strarray(cfg->out_ifs, cfg->num_out_ifs);
config_delstubs(cfg->stubs);
config_delstubs(cfg->forwards);
config_delauths(cfg->auths);
config_delviews(cfg->views);
config_delstrlist(cfg->donotqueryaddrs);
config_delstrlist(cfg->root_hints);

View file

@ -42,6 +42,7 @@
#ifndef UTIL_CONFIG_FILE_H
#define UTIL_CONFIG_FILE_H
struct config_stub;
struct config_auth;
struct config_view;
struct config_strlist;
struct config_str2list;
@ -170,6 +171,8 @@ struct config_file {
struct config_stub* stubs;
/** the forward zone definitions, linked list */
struct config_stub* forwards;
/** the auth zone definitions, linked list */
struct config_auth* auths;
/** the views definitions, linked list */
struct config_view* views;
/** list of donotquery addresses, linked list */
@ -530,6 +533,26 @@ struct config_stub {
int ssl_upstream;
};
/**
* Auth config options
*/
struct config_auth {
/** next in list */
struct config_auth* next;
/** domain name (in text) of the auth apex domain */
char* name;
/** list of masters */
struct config_strlist* masters;
/** list of urls */
struct config_strlist* urls;
/** zonefile (or NULL) */
char* zonefile;
/** provide downstream answers */
int for_downstream;
/** provide upstream answers */
int for_upstream;
};
/**
* View config options
*/
@ -820,6 +843,18 @@ void config_delstub(struct config_stub* p);
*/
void config_delstubs(struct config_stub* list);
/**
* Delete an auth item
* @param p: auth item
*/
void config_delauth(struct config_auth* p);
/**
* Delete items in config auth list.
* @param list: list.
*/
void config_delauths(struct config_auth* list);
/**
* Delete a view item
* @param p: view item

File diff suppressed because it is too large Load diff

View file

@ -295,6 +295,12 @@ forward-addr{COLON} { YDVAR(1, VAR_FORWARD_ADDR) }
forward-host{COLON} { YDVAR(1, VAR_FORWARD_HOST) }
forward-first{COLON} { YDVAR(1, VAR_FORWARD_FIRST) }
forward-ssl-upstream{COLON} { YDVAR(1, VAR_FORWARD_SSL_UPSTREAM) }
auth-zone{COLON} { YDVAR(0, VAR_AUTH_ZONE) }
zonefile{COLON} { YDVAR(1, VAR_ZONEFILE) }
master{COLON} { YDVAR(1, VAR_MASTER) }
url{COLON} { YDVAR(1, VAR_URL) }
for-downstream{COLON} { YDVAR(1, VAR_FOR_DOWNSTREAM) }
for-upstream{COLON} { YDVAR(1, VAR_FOR_UPSTREAM) }
view{COLON} { YDVAR(0, VAR_VIEW) }
view-first{COLON} { YDVAR(1, VAR_VIEW_FIRST) }
do-not-query-address{COLON} { YDVAR(1, VAR_DO_NOT_QUERY_ADDRESS) }

File diff suppressed because it is too large Load diff

View file

@ -266,7 +266,13 @@ extern int yydebug;
VAR_CACHEDB = 476,
VAR_CACHEDB_BACKEND = 477,
VAR_CACHEDB_SECRETSEED = 478,
VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM = 479
VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM = 479,
VAR_FOR_UPSTREAM = 480,
VAR_AUTH_ZONE = 481,
VAR_ZONEFILE = 482,
VAR_MASTER = 483,
VAR_URL = 484,
VAR_FOR_DOWNSTREAM = 485
};
#endif
/* Tokens. */
@ -492,6 +498,12 @@ extern int yydebug;
#define VAR_CACHEDB_BACKEND 477
#define VAR_CACHEDB_SECRETSEED 478
#define VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM 479
#define VAR_FOR_UPSTREAM 480
#define VAR_AUTH_ZONE 481
#define VAR_ZONEFILE 482
#define VAR_MASTER 483
#define VAR_URL 484
#define VAR_FOR_DOWNSTREAM 485
/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
@ -502,7 +514,7 @@ union YYSTYPE
char* str;
#line 506 "util/configparser.h" /* yacc.c:1909 */
#line 518 "util/configparser.h" /* yacc.c:1909 */
};
typedef union YYSTYPE YYSTYPE;

View file

@ -152,16 +152,16 @@ extern struct config_parser_state* cfg_parser;
%token VAR_IPSECMOD_ENABLED VAR_IPSECMOD_HOOK VAR_IPSECMOD_IGNORE_BOGUS
%token VAR_IPSECMOD_MAX_TTL VAR_IPSECMOD_WHITELIST VAR_IPSECMOD_STRICT
%token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED
%token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM
%token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM
%token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
toplevelvar: serverstart contents_server | stubstart contents_stub |
forwardstart contents_forward | pythonstart contents_py |
rcstart contents_rc | dtstart contents_dt | viewstart
contents_view |
dnscstart contents_dnsc |
cachedbstart contents_cachedb
rcstart contents_rc | dtstart contents_dt | viewstart contents_view |
dnscstart contents_dnsc | cachedbstart contents_cachedb |
authstart contents_auth
;
/* server: declaration */
@ -297,6 +297,26 @@ contents_view: contents_view content_view
content_view: view_name | view_local_zone | view_local_data | view_first |
view_response_ip | view_response_ip_data | view_local_data_ptr
;
authstart: VAR_AUTH_ZONE
{
struct config_auth* s;
OUTYY(("\nP(auth_zone:)\n"));
s = (struct config_auth*)calloc(1, sizeof(struct config_auth));
if(s) {
s->next = cfg_parser->cfg->auths;
cfg_parser->cfg->auths = s;
/* defaults for auth zone */
s->for_downstream = 1;
s->for_upstream = 1;
} else
yyerror("out of memory");
}
;
contents_auth: contents_auth content_auth
| ;
content_auth: auth_name | auth_zonefile | auth_master | auth_url |
auth_for_downstream | auth_for_upstream
;
server_num_threads: VAR_NUM_THREADS STRING_ARG
{
OUTYY(("P(server_num_threads:%s)\n", $2));
@ -1999,6 +2019,57 @@ forward_ssl_upstream: VAR_FORWARD_SSL_UPSTREAM STRING_ARG
free($2);
}
;
auth_name: VAR_NAME STRING_ARG
{
OUTYY(("P(name:%s)\n", $2));
if(cfg_parser->cfg->auths->name)
yyerror("auth name override, there must be one name "
"for one auth-zone");
free(cfg_parser->cfg->auths->name);
cfg_parser->cfg->auths->name = $2;
}
;
auth_zonefile: VAR_ZONEFILE STRING_ARG
{
OUTYY(("P(zonefile:%s)\n", $2));
free(cfg_parser->cfg->auths->zonefile);
cfg_parser->cfg->auths->zonefile = $2;
}
;
auth_master: VAR_MASTER STRING_ARG
{
OUTYY(("P(master:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->auths->masters, $2))
yyerror("out of memory");
}
;
auth_url: VAR_URL STRING_ARG
{
OUTYY(("P(url:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->auths->urls, $2))
yyerror("out of memory");
}
;
auth_for_downstream: VAR_FOR_DOWNSTREAM STRING_ARG
{
OUTYY(("P(for-downstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->for_downstream =
(strcmp($2, "yes")==0);
free($2);
}
;
auth_for_upstream: VAR_FOR_UPSTREAM STRING_ARG
{
OUTYY(("P(for-upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->for_upstream =
(strcmp($2, "yes")==0);
free($2);
}
;
view_name: VAR_NAME STRING_ARG
{
OUTYY(("P(name:%s)\n", $2));

View file

@ -215,6 +215,7 @@ fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *))
else if(fptr == &view_cmp) return 1;
else if(fptr == &auth_zone_cmp) return 1;
else if(fptr == &auth_data_cmp) return 1;
else if(fptr == &auth_xfer_cmp) return 1;
return 0;
}