- minor Makefile fixup.
- moved module-stack code out of daemon/daemon into services/modstack,
preparing for code-reuse.
+ - move context into own header file.
+ - context query structure.
3 December 2007: Wouter
- changed checkconf/ to smallapp/ to make room for more support tools.
--- /dev/null
+/*
+ * libunbound/context.c - validating context for unbound internal use
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the validator context structure.
+ */
+#include "config.h"
+#include "libunbound/context.h"
+#include "util/module.h"
+#include "util/config_file.h"
+#include "services/modstack.h"
+#include "services/localzone.h"
+#include "services/cache/rrset.h"
+#include "services/cache/infra.h"
+#include "util/data/msgreply.h"
+#include "util/storage/slabhash.h"
+
+int
+context_finalize(struct ub_val_ctx* ctx)
+{
+ struct config_file* cfg = ctx->env->cfg;
+ verbosity = cfg->verbosity;
+ log_init(cfg->logfile, cfg->use_syslog, NULL);
+ config_apply(cfg);
+ if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env))
+ return UB_INITFAIL;
+ ctx->local_zones = local_zones_create();
+ if(!ctx->local_zones)
+ return UB_NOMEM;
+ if(!local_zones_apply_cfg(ctx->local_zones, cfg))
+ return UB_INITFAIL;
+ if(!ctx->env->msg_cache ||
+ cfg->msg_cache_size != slabhash_get_size(ctx->env->msg_cache) ||
+ cfg->msg_cache_slabs != ctx->env->msg_cache->size) {
+ slabhash_delete(ctx->env->msg_cache);
+ ctx->env->msg_cache = slabhash_create(cfg->msg_cache_slabs,
+ HASH_DEFAULT_STARTARRAY, cfg->msg_cache_size,
+ msgreply_sizefunc, query_info_compare,
+ query_entry_delete, reply_info_delete, NULL);
+ if(!ctx->env->msg_cache)
+ return UB_NOMEM;
+ }
+ ctx->env->rrset_cache = rrset_cache_adjust(ctx->env->rrset_cache,
+ ctx->env->cfg, ctx->env->alloc);
+ if(!ctx->env->rrset_cache)
+ return UB_NOMEM;
+ ctx->env->infra_cache = infra_adjust(ctx->env->infra_cache, cfg);
+ if(!ctx->env->infra_cache)
+ return UB_NOMEM;
+ return UB_NOERROR;
+}
+
+int context_query_cmp(const void* a, const void* b)
+{
+ if( *(int*)a < *(int*)b )
+ return -1;
+ if( *(int*)a > *(int*)b )
+ return 1;
+ return 0;
+}
+
+/** How many times to try to find an unused query-id-number for async */
+#define NUM_ID_TRIES 100000
+/** find next useful id number of 0 on error */
+static int
+find_id(struct ub_val_ctx* ctx, int* id)
+{
+ size_t tries = 0;
+ while(rbtree_search(&ctx->queries, &ctx->next_querynum)) {
+ ctx->next_querynum++; /* numerical wraparound is fine */
+ if(tries++ > NUM_ID_TRIES)
+ return 0;
+ }
+ *id = ctx->next_querynum;
+ ctx->next_querynum++;
+ return 1;
+}
+
+struct ctx_query*
+context_new(struct ub_val_ctx* ctx, char* name, int rrtype, int rrclass,
+ ub_val_callback_t cb, void* cbarg)
+{
+ struct ctx_query* q = (struct ctx_query*)calloc(1, sizeof(*q));
+ if(!q) return NULL;
+ if(!find_id(ctx, &q->querynum)) {
+ free(q);
+ return NULL;
+ }
+ q->node.key = &q->querynum;
+ q->async = (cb != NULL);
+ q->cb = cb;
+ q->cb_arg = cbarg;
+ q->res = (struct ub_val_result*)calloc(1, sizeof(*q->res));
+ if(!q->res) {
+ free(q);
+ return NULL;
+ }
+ q->res->qname = strdup(name);
+ if(!q->res->qname) {
+ free(q->res);
+ free(q);
+ return NULL;
+ }
+ q->res->qtype = rrtype;
+ q->res->qclass = rrclass;
+
+ /* add to query list */
+ if(q->async)
+ ctx->num_async ++;
+ (void)rbtree_insert(&ctx->queries, &q->node);
+ return q;
+}
--- /dev/null
+/*
+ * libunbound/context.h - validating context for unbound internal use
+ *
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains the validator context structure.
+ */
+#ifndef LIBUNBOUND_CONTEXT_H
+#define LIBUNBOUND_CONTEXT_H
+#include "util/locks.h"
+#include "util/alloc.h"
+#include "util/rbtree.h"
+#include "services/modstack.h"
+#include "libunbound/unbound.h"
+
+/**
+ * The context structure
+ *
+ * Contains two pipes for async service
+ * qq : write queries to the async service pid/tid.
+ * rr : read results from the async service pid/tid.
+ */
+struct ub_val_ctx {
+ /* --- pipes --- */
+ /** mutex on query write pipe */
+ lock_basic_t qqpipe_lock;
+ /** the query write pipe, [0] read from, [1] write on */
+ int qqpipe[2];
+ /** mutex on result read pipe */
+ lock_basic_t rrpipe_lock;
+ /** the result read pipe, [0] read from, [1] write on */
+ int rrpipe[2];
+
+ /* --- shared data --- */
+ /** mutex for access to env.cfg, finalized and dothread */
+ lock_basic_t cfglock;
+ /**
+ * The context has been finalized
+ * This is after config when the first resolve is done.
+ * The modules are inited (module-init()) and shared caches created.
+ */
+ int finalized;
+
+ /** do threading (instead of forking) for async resolution */
+ int dothread;
+ /** next thread number for new threads */
+ int thr_next_num;
+ /**
+ * List of alloc-cache-id points per threadnum for notinuse threads.
+ * Simply the entire struct alloc_cache with the 'super' member used
+ * to link a simply linked list. Reset super member to the superalloc
+ * before use.
+ */
+ struct alloc_cache* alloc_list;
+
+ /** shared caches, and so on */
+ struct alloc_cache superalloc;
+ /** module env master value */
+ struct module_env* env;
+ /** module stack */
+ struct module_stack mods;
+ /** local authority zones */
+ struct local_zones* local_zones;
+
+ /** next query number (to try) to use */
+ int next_querynum;
+ /** number of async queries outstanding */
+ size_t num_async;
+ /**
+ * Tree of outstanding queries. Indexed by querynum
+ * Used when results come in for async to lookup.
+ * Used when cancel is done for lookup (and delete).
+ * Used to see if querynum is free for use.
+ * Content of type ctx_query.
+ */
+ rbtree_t queries;
+};
+
+/**
+ * The queries outstanding for the libunbound resolver.
+ * These are outstanding for async resolution.
+ * But also, outstanding for sync resolution by one of the threads that
+ * has joined the threadpool.
+ */
+struct ctx_query {
+ /** node in rbtree, must be first entry, key is ptr to the querynum */
+ struct rbnode_t node;
+ /** query id number, key for node */
+ int querynum;
+ /** was this an async query? */
+ int async;
+
+ /** for async query, the callback function */
+ ub_val_callback_t cb;
+ /** for async query, the callback user arg */
+ void* cb_arg;
+
+ /** result structure, also contains original query, type, class.
+ * malloced ptr ready to hand to the client. */
+ struct ub_val_result* res;
+};
+
+/**
+ * The error constants
+ */
+enum ub_ctx_err {
+ /** no error */
+ UB_NOERROR = 0,
+ /** alloc failure */
+ UB_NOMEM,
+ /** socket operation */
+ UB_SOCKET,
+ /** syntax error */
+ UB_SYNTAX,
+ /** DNS service failed */
+ UB_SERVFAIL,
+ /** initialization failed (bad settings) */
+ UB_INITFAIL
+};
+
+/**
+ * finalize a context.
+ * @param ctx: context to finalize. creates shared data.
+ * @return 0 if OK, or errcode.
+ */
+int context_finalize(struct ub_val_ctx* ctx);
+
+/** compare two ctx_query elements */
+int context_query_cmp(const void* a, const void* b);
+
+/**
+ * Create new query in context, add to querynum list.
+ * @param ctx: context
+ * @param name: query name
+ * @param rrtype: type
+ * @param rrclass: class
+ * @param cb: callback for async, or NULL for sync.
+ * @param cbarg: user arg for async queries.
+ * @return new ctx_query or NULL for malloc failure.
+ */
+struct ctx_query* context_new(struct ub_val_ctx* ctx, char* name, int rrtype,
+ int rrclass, ub_val_callback_t cb, void* cbarg);
+
+#endif /* LIBUNBOUND_CONTEXT_H */
/* include the public api first, it should be able to stand alone */
#include "libunbound/unbound.h"
#include "config.h"
+#include "libunbound/context.h"
#include "util/locks.h"
#include "util/config_file.h"
#include "util/alloc.h"
-
-/**
- * The context structure
- *
- * Contains two pipes for async service
- * qq : write queries to the async service pid/tid.
- * rr : read results from the async service pid/tid.
- */
-struct ub_val_ctx {
- /** mutex on query write pipe */
- lock_basic_t qqpipe_lock;
- /** the query write pipe, [0] read from, [1] write on */
- int qqpipe[2];
- /** mutex on result read pipe */
- lock_basic_t rrpipe_lock;
- /** the result read pipe, [0] read from, [1] write on */
- int rrpipe[2];
-
- /** configuration options */
- struct config_file* cfg;
- /** do threading (instead of forking) for async resolution */
- int dothread;
-
- /** shared data */
- /* list of alloc-cache-id points and nextthreadnum */
- /*struct shareddata* shared;*/
- /** outstanding querylist and next querynum (to try) */
-
- /** shared caches, and so on */
- struct alloc_cache superalloc;
- /** module env master value */
- struct module_env* env;
- /** number of modules active, ids from 0 to num-1. */
- int num_modules;
- /** the module callbacks, array of num_modules length */
- struct module_func_block** modfunc;
- /** local authority zones */
- struct local_zones* local_zones;
-
- /** TODO list of outstanding queries */
-};
-
-/**
- * The error constants
- */
-enum ub_ctx_err {
- /** no error */
- UB_NOERROR = 0,
- /** alloc failure */
- UB_NOMEM,
- /** socket operation */
- UB_SOCKET,
- /** syntax error */
- UB_SYNTAX,
- /** DNS service failed */
- UB_SERVFAIL
-};
-
+#include "util/module.h"
+#include "util/log.h"
+#include "services/modstack.h"
+#include "services/localzone.h"
+#include "services/cache/infra.h"
+#include "services/cache/rrset.h"
struct ub_val_ctx*
ub_val_ctx_create()
return NULL;
}
checklock_start();
+ log_ident_set("libunbound");
+ verbosity = 0; /* errors only */
+ log_init(NULL, 0, NULL); /* logs to stderr */
+ alloc_init(&ctx->superalloc, NULL, 0);
if(socketpair(AF_UNIX, SOCK_STREAM, 0, ctx->qqpipe) == -1) {
free(ctx);
return NULL;
}
lock_basic_init(&ctx->qqpipe_lock);
lock_basic_init(&ctx->rrpipe_lock);
- ctx->cfg = config_create();
- if(!ctx->cfg) {
+ lock_basic_init(&ctx->cfglock);
+ ctx->env = (struct module_env*)calloc(1, sizeof(*ctx->env));
+ if(!ctx->env) {
+ ub_val_ctx_delete(ctx);
+ errno = ENOMEM;
+ return NULL;
+ }
+ ctx->env->cfg = config_create();
+ if(!ctx->env->cfg) {
ub_val_ctx_delete(ctx);
errno = ENOMEM;
return NULL;
}
+ ctx->env->alloc = &ctx->superalloc;
+ ctx->env->worker = NULL;
+ ctx->env->need_to_validate = 0;
+ modstack_init(&ctx->mods);
+ rbtree_init(&ctx->queries, &context_query_cmp);
return ctx;
}
+/** delete q */
+static void
+delq(rbnode_t* n, void* ATTR_UNUSED(arg))
+{
+ struct ctx_query* q = (struct ctx_query*)n;
+ if(!q) return;
+ ub_val_result_free(q->res);
+ free(q);
+}
+
void
ub_val_ctx_delete(struct ub_val_ctx* ctx)
{
+ struct alloc_cache* a, *na;
if(!ctx) return;
+ modstack_desetup(&ctx->mods, ctx->env);
+ a = ctx->alloc_list;
+ while(a) {
+ na = a->super;
+ a->super = &ctx->superalloc;
+ alloc_clear(a);
+ a = na;
+ }
+ alloc_clear(&ctx->superalloc);
+ local_zones_delete(ctx->local_zones);
lock_basic_destroy(&ctx->qqpipe_lock);
lock_basic_destroy(&ctx->rrpipe_lock);
+ lock_basic_destroy(&ctx->cfglock);
close(ctx->qqpipe[0]);
close(ctx->qqpipe[1]);
close(ctx->rrpipe[0]);
close(ctx->rrpipe[1]);
- config_delete(ctx->cfg);
+ if(ctx->env) {
+ slabhash_delete(ctx->env->msg_cache);
+ rrset_cache_delete(ctx->env->rrset_cache);
+ infra_delete(ctx->env->infra_cache);
+ config_delete(ctx->env->cfg);
+ free(ctx->env);
+ }
+ traverse_postorder(&ctx->queries, delq, NULL);
free(ctx);
}
int
ub_val_ctx_config(struct ub_val_ctx* ctx, char* fname)
{
- if(!config_read(ctx->cfg, fname)) {
+ lock_basic_lock(&ctx->cfglock);
+ ctx->finalized = 0;
+ if(!config_read(ctx->env->cfg, fname)) {
+ lock_basic_unlock(&ctx->cfglock);
return UB_SYNTAX;
}
+ lock_basic_unlock(&ctx->cfglock);
return UB_NOERROR;
}
{
char* dup = strdup(ta);
if(!dup) return UB_NOMEM;
- if(!cfg_strlist_insert(&ctx->cfg->trust_anchor_list, dup)) {
+ lock_basic_lock(&ctx->cfglock);
+ ctx->finalized = 0;
+ if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, dup)) {
+ lock_basic_unlock(&ctx->cfglock);
free(dup);
return UB_NOMEM;
}
+ lock_basic_unlock(&ctx->cfglock);
return UB_NOERROR;
}
{
char* dup = strdup(fname);
if(!dup) return UB_NOMEM;
- if(!cfg_strlist_insert(&ctx->cfg->trusted_keys_file_list, dup)) {
+ lock_basic_lock(&ctx->cfglock);
+ ctx->finalized = 0;
+ if(!cfg_strlist_insert(&ctx->env->cfg->trusted_keys_file_list, dup)) {
+ lock_basic_unlock(&ctx->cfglock);
free(dup);
return UB_NOMEM;
}
+ lock_basic_unlock(&ctx->cfglock);
return UB_NOERROR;
}
int
ub_val_ctx_async(struct ub_val_ctx* ctx, int dothread)
{
+ lock_basic_lock(&ctx->cfglock);
+ ctx->finalized = 0;
ctx->dothread = dothread;
+ lock_basic_unlock(&ctx->cfglock);
return UB_NOERROR;
}
int
ub_val_ctx_wait(struct ub_val_ctx* ctx)
{
+ lock_basic_lock(&ctx->cfglock);
/* TODO until no more queries outstanding */
- while(1) {
+ while(ctx->num_async > 0) {
+ lock_basic_unlock(&ctx->cfglock);
lock_basic_lock(&ctx->rrpipe_lock);
(void)pollit(ctx, NULL);
lock_basic_unlock(&ctx->rrpipe_lock);
ub_val_ctx_process(ctx);
+ lock_basic_lock(&ctx->cfglock);
}
+ lock_basic_unlock(&ctx->cfglock);
return UB_NOERROR;
}
ub_val_ctx_process(struct ub_val_ctx* ctx)
{
/* TODO */
+ /* ctx->num_asynx-- when handled; */
return UB_NOMEM;
}
ub_val_resolve(struct ub_val_ctx* ctx, char* name, int rrtype,
int rrclass, int* secure, int* data, struct ub_val_result** result)
{
+ struct ctx_query* q;
+
+ lock_basic_lock(&ctx->cfglock);
+ if(!ctx->finalized) {
+ int r = context_finalize(ctx);
+ if(r) {
+ lock_basic_unlock(&ctx->cfglock);
+ return r;
+ }
+ }
+ /* create new ctx_query and attempt to add to the list */
+ q = context_new(ctx, name, rrtype, rrclass, NULL, NULL);
+ lock_basic_unlock(&ctx->cfglock);
+ if(!q)
+ return UB_NOMEM;
/* become a resolver thread for a bit */
+ *secure = 0;
+ *data = 0;
+ *result = NULL;
/* TODO */
return UB_NOMEM;
ub_val_resolve_async(struct ub_val_ctx* ctx, char* name, int rrtype,
int rrclass, void* mydata, ub_val_callback_t callback, int* async_id)
{
- /* TODO */
+ struct ctx_query* q;
+
+ lock_basic_lock(&ctx->cfglock);
+ if(!ctx->finalized) {
+ int r = context_finalize(ctx);
+ if(r) {
+ lock_basic_unlock(&ctx->cfglock);
+ return r;
+ }
+ }
+ /* create new ctx_query and attempt to add to the list */
+ q = context_new(ctx, name, rrtype, rrclass, callback, mydata);
+ lock_basic_unlock(&ctx->cfglock);
+ if(!q)
+ return UB_NOMEM;
+ /* TODO write over pipe to background worker */
+ *async_id = q->querynum;
return UB_NOMEM;
}
int
ub_val_cancel(struct ub_val_ctx* ctx, int async_id)
{
- /* TODO */
+ struct ctx_query* q;
+ lock_basic_lock(&ctx->cfglock);
+ q = (struct ctx_query*)rbtree_search(&ctx->queries, &async_id);
+ lock_basic_unlock(&ctx->cfglock);
+ if(!q || !q->async) /* it is not there, so nothing to do */
+ return UB_NOERROR;
+ /* TODO ; send cancel to background worker */
+
+ lock_basic_lock(&ctx->cfglock);
+ (void)rbtree_delete(&ctx->queries, &async_id);
+ ctx->num_async--;
+ lock_basic_unlock(&ctx->cfglock);
return UB_NOMEM;
}
case UB_SOCKET: return "socket io error";
case UB_SYNTAX: return "syntax error";
case UB_SERVFAIL: return "server failure";
+ case UB_INITFAIL: return "initialization failure";
default: return "unknown error";
}
}