INSTALL=$(srcdir)/install-sh
-COMMON_SRC=$(wildcard services/*.c util/*.c util/data/*.c util/storage/*.c) util/configparser.c util/configlexer.c testcode/checklocks.c
+COMMON_SRC=$(wildcard services/*.c util/*.c util/data/*.c util/storage/*.c iterator/*.c) util/configparser.c util/configlexer.c testcode/checklocks.c
COMMON_OBJ=$(addprefix $(BUILD),$(COMMON_SRC:.c=.o))
COMPAT_OBJ=$(addprefix $(BUILD)compat/,$(LIBOBJS))
UNITTEST_SRC=$(wildcard testcode/unit*.c) testcode/readhex.c $(COMMON_SRC)
#include "util/data/msgreply.h"
#include "util/storage/slabhash.h"
#include "services/listen_dnsport.h"
+#include "util/module.h"
+#include "iterator/iterator.h"
#include <signal.h>
/** How many quit requests happened. */
return NULL;
}
alloc_init(&daemon->superalloc, NULL, 0);
+ if(!(daemon->env = (struct module_env*)calloc(1,
+ sizeof(*daemon->env)))) {
+ daemon_delete(daemon);
+ return NULL;
+ }
return daemon;
}
return 1;
}
+/**
+ * Setup modules. Assigns ids and calls module_init.
+ * @param daemon: the daemon
+ */
+static void daemon_setup_modules(struct daemon* daemon)
+{
+ int i;
+ /* fixed setup of the modules */
+ daemon->num_modules = 1;
+ daemon->modfunc = (struct module_func_block**)calloc((size_t)
+ daemon->num_modules, sizeof(struct module_func_block*));
+ if(!daemon->modfunc) {
+ fatal_exit("malloc failure allocating function callbacks");
+ }
+ daemon->modfunc[0] = iter_get_funcblock();
+ daemon->env->cfg = daemon->cfg;
+ daemon->env->msg_cache = daemon->msg_cache;
+ daemon->env->rrset_cache = daemon->rrset_cache;
+ daemon->env->alloc = &daemon->superalloc;
+ daemon->env->worker = NULL;
+ daemon->env->send_query = &worker_send_query;
+ for(i=0; i<daemon->num_modules; i++) {
+ log_info("init module %d: %s", i, daemon->modfunc[i]->name);
+ if(!(*daemon->modfunc[i]->init)(daemon->env, i)) {
+ fatal_exit("module init for module %s failed",
+ daemon->modfunc[i]->name);
+ }
+ }
+
+}
+
/**
* Allocate empty worker structures. With backptr and thread-number,
* from 0..numthread initialised. Used as user arguments to new threads.
}
}
+/**
+ * Desetup the modules, deinit, delete.
+ * @param daemon: the daemon.
+ */
+static void
+daemon_desetup_modules(struct daemon* daemon)
+{
+ int i;
+ for(i=0; i<daemon->num_modules; i++) {
+ (*daemon->modfunc[i]->deinit)(daemon->env, i);
+ }
+ daemon->num_modules = 0;
+ free(daemon->modfunc);
+ daemon->modfunc = 0;
+}
+
void
daemon_fork(struct daemon* daemon)
{
log_assert(daemon);
+
+ /* setup modules */
+ daemon_setup_modules(daemon);
+
/* first create all the worker structures, so we can pass
* them to the newly created threads.
*/
/* we exited! a signal happened! Stop other threads */
daemon_stop_others(daemon);
+ /* de-setup modules */
+ daemon_desetup_modules(daemon);
+
if(daemon->workers[0]->need_to_restart)
daemon->need_to_exit = 0;
else daemon->need_to_exit = 1;
alloc_clear(&daemon->superalloc);
free(daemon->cwd);
free(daemon->pidfile);
+ free(daemon->env);
free(daemon);
checklock_stop();
}
struct worker;
struct listen_port;
struct slabhash;
+struct module_env;
/**
* Structure holding worker list.
struct slabhash* msg_cache;
/** the rrset cache, content is struct ub_packed_rrset_key* */
struct slabhash* rrset_cache;
+ /** the module environment master value, copied and changed by threads*/
+ 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;
};
/**
#include <netdb.h>
#include <signal.h>
-/** size of ID+FLAGS in a DNS message */
-#define DNS_ID_AND_FLAGS 4
-/** timeout in seconds for UDP queries to auth servers. TODO: proper rtt */
-#define UDP_QUERY_TIMEOUT 4
-/** timeout in seconds for TCP queries to auth servers. TODO: proper rtt */
-#define TCP_QUERY_TIMEOUT 30
-/** Advertised version of EDNS capabilities */
-#define EDNS_ADVERTISED_VERSION 0
-/** Advertised size of EDNS capabilities */
-#define EDNS_ADVERTISED_SIZE 4096
-
-
void
worker_send_cmd(struct worker* worker, ldns_buffer* buffer,
enum worker_commands cmd)
static void
req_release(struct work_query* w)
{
- if(w->worker->num_requests == w->worker->request_size) {
+ struct worker* worker = w->state.env->worker;
+ if(worker->num_requests == worker->request_size) {
/* no longer at max, start accepting again. */
- listen_resume(w->worker->front);
+ listen_resume(worker->front);
}
- log_assert(w->worker->num_requests >= 1);
- w->worker->num_requests --;
- w->next = w->worker->free_queries;
- w->worker->free_queries = w;
+ log_assert(worker->num_requests >= 1);
+ worker->num_requests --;
+ w->next = worker->free_queries;
+ worker->free_queries = w;
}
/** create error and fill into buffer */
static void
replyerror(int r, struct work_query* w)
{
- w->edns.edns_version = EDNS_ADVERTISED_VERSION;
- w->edns.udp_size = EDNS_ADVERTISED_SIZE;
- w->edns.ext_rcode = 0;
- w->edns.bits &= EDNS_DO;
- replyerror_fillbuf(r, &w->query_reply, w->query_id, w->query_flags,
- &w->qinfo);
- attach_edns_record(w->query_reply.c->buffer, &w->edns);
+ w->state.edns.edns_version = EDNS_ADVERTISED_VERSION;
+ w->state.edns.udp_size = EDNS_ADVERTISED_SIZE;
+ w->state.edns.ext_rcode = 0;
+ w->state.edns.bits &= EDNS_DO;
+ replyerror_fillbuf(r, &w->query_reply, w->query_id,
+ w->state.query_flags, &w->state.qinfo);
+ attach_edns_record(w->query_reply.c->buffer, &w->state.edns);
comm_point_send_reply(&w->query_reply);
req_release(w);
- query_info_clear(&w->qinfo);
-}
-
-/** see if rrset needs to be updated in the cache */
-static int
-need_to_update_rrset(struct packed_rrset_data* newd,
- struct packed_rrset_data* cached)
-{
- /* o if current RRset is more trustworthy - insert it */
- if( newd->trust > cached->trust )
- return 1;
- /* o same trust, but different in data - insert it */
- if( newd->trust == cached->trust &&
- !rrsetdata_equal(newd, cached))
- return 1;
- /* o see if TTL is better than TTL in cache. */
- /* if so, see if rrset+rdata is the same */
- /* if so, update TTL in cache, even if trust is worse. */
- if( newd->ttl > cached->ttl &&
- rrsetdata_equal(newd, cached))
- return 1;
- return 0;
+ query_info_clear(&w->state.qinfo);
}
-/** store rrsets in the rrset cache. */
-static void
-worker_store_rrsets(struct worker* worker, struct reply_info* rep)
+/** process incoming request */
+static void
+worker_process_query(struct worker* worker, struct work_query* w,
+ enum module_ev event)
{
- struct lruhash_entry* e;
- size_t i;
- /* see if rrset already exists in cache, if not insert it. */
- /* if it does exist: check to insert it */
- for(i=0; i<rep->rrset_count; i++) {
- rep->ref[i].key = rep->rrsets[i];
- rep->ref[i].id = rep->rrsets[i]->id;
- /* looks up item with a readlock - no editing! */
- if((e=slabhash_lookup(worker->daemon->rrset_cache,
- rep->rrsets[i]->entry.hash, rep->rrsets[i]->entry.key,
- 0)) != 0) {
- struct packed_rrset_data* data =
- (struct packed_rrset_data*)e->data;
- struct packed_rrset_data* rd =
- (struct packed_rrset_data*)
- rep->rrsets[i]->entry.data;
- rep->ref[i].key = (struct ub_packed_rrset_key*)e->key;
- rep->ref[i].id = rep->rrsets[i]->id;
- /* found in cache, do checks above */
- if(!need_to_update_rrset(rd, data)) {
- lock_rw_unlock(&e->lock);
- ub_packed_rrset_parsedelete(rep->rrsets[i],
- &worker->alloc);
- rep->rrsets[i] = rep->ref[i].key;
- continue; /* use cached item instead */
- }
- if(rd->trust < data->trust)
- rd->trust = data->trust;
- lock_rw_unlock(&e->lock);
- /* small gap here, where entry is not locked.
- * possibly entry is updated with something else.
- * this is just too bad, its cache anyway. */
- /* use insert to update entry to manage lruhash
- * cache size values nicely. */
- }
- slabhash_insert(worker->daemon->rrset_cache,
- rep->rrsets[i]->entry.hash, &rep->rrsets[i]->entry,
- rep->rrsets[i]->entry.data, &worker->alloc);
- if(e) rep->rrsets[i] = rep->ref[i].key;
+ int i;
+ if(event == module_event_new) {
+ w->state.curmod = 0;
+ for(i=0; i<worker->daemon->num_modules; i++)
+ w->state.ext_state[i] = module_state_initial;
+ }
+ /* allow current module to run */
+ (*worker->daemon->modfunc[w->state.curmod]->operate)(&w->state, event,
+ w->state.curmod);
+ /* TODO examine results, start further modules, etc.
+ * assume it went to sleep
+ */
+ region_free_all(worker->scratchpad);
+ if(w->state.ext_state[w->state.curmod] == module_error) {
+ region_free_all(w->state.region);
+ replyerror(LDNS_RCODE_SERVFAIL, w);
+ return;
+ }
+ if(w->state.ext_state[w->state.curmod] == module_finished) {
+ memcpy(ldns_buffer_begin(w->query_reply.c->buffer),
+ &w->query_id, sizeof(w->query_id));
+ comm_point_send_reply(&w->query_reply);
+ region_free_all(w->state.region);
+ req_release(w);
+ query_info_clear(&w->state.qinfo);
+ return;
}
+ /* suspend, waits for wakeup callback */
}
/** process incoming replies from the network */
static int
worker_handle_reply(struct comm_point* c, void* arg, int error,
- struct comm_reply* ATTR_UNUSED(reply_info))
+ struct comm_reply* reply_info)
{
struct work_query* w = (struct work_query*)arg;
- struct query_info qinf;
- struct reply_info* rep;
- struct msgreply_entry* e;
- struct edns_data svr_edns; /* unused server edns advertisement */
- uint16_t us;
- int r;
+ struct worker* worker = w->state.env->worker;
- verbose(VERB_DETAIL, "reply to query with stored ID %d",
- ntohs(w->query_id)); /* byteswapped so same as dig prints */
+ w->state.reply = reply_info;
if(error != 0) {
- replyerror(LDNS_RCODE_SERVFAIL, w);
+ worker_process_query(worker, w, module_event_timeout);
return 0;
}
/* sanity check. */
- if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer)))
- return 0; /* not a reply. */
- if(LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) != LDNS_PACKET_QUERY)
- return 0; /* not a reply to a query. */
- if(LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1)
- return 0; /* too much in the query section */
- /* see if it is truncated */
- if(LDNS_TC_WIRE(ldns_buffer_begin(c->buffer)) && c->type == comm_udp) {
- log_info("TC: truncated. retry in TCP mode.");
- qinfo_query_encode(w->worker->back->udp_buff, &w->qinfo);
- pending_tcp_query(w->worker->back, w->worker->back->udp_buff,
- &w->worker->fwd_addr, w->worker->fwd_addrlen,
- TCP_QUERY_TIMEOUT, worker_handle_reply, w,
- w->worker->rndstate);
+ if(!LDNS_QR_WIRE(ldns_buffer_begin(c->buffer))
+ || LDNS_OPCODE_WIRE(ldns_buffer_begin(c->buffer)) !=
+ LDNS_PACKET_QUERY
+ || LDNS_QDCOUNT(ldns_buffer_begin(c->buffer)) > 1) {
+ /* error becomes timeout for the module as if this reply
+ * never arrived. */
+ worker_process_query(worker, w, module_event_timeout);
return 0;
}
- /* woohoo a reply! */
- if((r=reply_info_parse(c->buffer, &w->worker->alloc, &qinf, &rep,
- w->worker->scratchpad, &svr_edns))!=0) {
- if(r == LDNS_RCODE_SERVFAIL)
- log_err("reply_info_parse: out of memory");
- /* formerr on my parse gives servfail to my client */
- replyerror(LDNS_RCODE_SERVFAIL, w);
- region_free_all(w->worker->scratchpad);
- return 0;
- }
- us = w->edns.udp_size;
- w->edns.edns_version = EDNS_ADVERTISED_VERSION;
- w->edns.udp_size = EDNS_ADVERTISED_SIZE;
- w->edns.ext_rcode = 0;
- w->edns.bits &= EDNS_DO;
- if(!reply_info_answer_encode(&qinf, rep, w->query_id, w->query_flags,
- w->query_reply.c->buffer, 0, 0, w->worker->scratchpad, us,
- &w->edns)) {
- replyerror(LDNS_RCODE_SERVFAIL, w);
- query_info_clear(&qinf);
- reply_info_parsedelete(rep, &w->worker->alloc);
- region_free_all(w->worker->scratchpad);
- return 0;
- }
- comm_point_send_reply(&w->query_reply);
- region_free_all(w->worker->scratchpad);
- req_release(w);
- query_info_clear(&w->qinfo);
- if(rep->ttl == 0) {
- log_info("TTL 0: dropped");
- query_info_clear(&qinf);
- reply_info_parsedelete(rep, &w->worker->alloc);
- return 0;
- }
- reply_info_set_ttls(rep, time(0));
- worker_store_rrsets(w->worker, rep);
- reply_info_sortref(rep);
- /* store msg in the cache */
- if(!(e = query_info_entrysetup(&qinf, rep, w->query_hash))) {
- query_info_clear(&qinf);
- reply_info_parsedelete(rep, &w->worker->alloc);
- return 0;
- }
- slabhash_insert(w->worker->daemon->msg_cache, w->query_hash,
- &e->entry, rep, &w->worker->alloc);
+ worker_process_query(worker, w, module_event_reply);
return 0;
}
-/** process incoming request */
-static void
-worker_process_query(struct worker* worker, struct work_query* w)
-{
- /* query the forwarding address */
- verbose(VERB_DETAIL, "process_query ID %d", ntohs(w->query_id));
- pending_udp_query(worker->back, w->query_reply.c->buffer,
- &worker->fwd_addr, worker->fwd_addrlen, UDP_QUERY_TIMEOUT,
- worker_handle_reply, w, worker->rndstate);
-}
-
/** check request sanity. Returns error code, 0 OK, or -1 discard.
* @param pkt: the wire packet to examine for sanity.
*/
query_info_clear(&qinfo);
return 0;
}
- w->edns = edns;
+ w->state.edns = edns;
worker->free_queries = w->next;
worker->num_requests ++;
log_assert(worker->num_requests <= worker->request_size);
/* init request */
w->next = NULL;
- w->query_hash = h;
+ w->state.query_hash = h;
memcpy(&w->query_reply, repinfo, sizeof(struct comm_reply));
- memcpy(&w->qinfo, &qinfo, sizeof(struct query_info));
+ memcpy(&w->state.qinfo, &qinfo, sizeof(struct query_info));
memcpy(&w->query_id, ldns_buffer_begin(c->buffer), sizeof(uint16_t));
- w->query_flags = ldns_buffer_read_u16_at(c->buffer, 2);
+ w->state.query_flags = ldns_buffer_read_u16_at(c->buffer, 2);
/* answer it */
- worker_process_query(worker, w);
+ w->state.buf = c->buffer;
+ worker_process_query(worker, w, module_event_new);
return 0;
}
struct work_query* q = (struct work_query*)calloc(1,
sizeof(struct work_query));
if(!q) return 0;
- q->worker = worker;
+ q->state.buf = worker->front->udp_buff;
+ q->state.scratch = worker->scratchpad;
+ q->state.region = region_create_custom(malloc, free, 1024,
+ 64, 16, 0);
+ if(!q->state.region) {
+ free(q);
+ return 0;
+ }
+ q->state.env = &worker->env;
+ q->state.work_info = q;
q->next = worker->free_queries;
worker->free_queries = q;
q->all_next = worker->all_queries;
struct work_query* n;
while(q) {
n = q->all_next;
- log_assert(q->worker == worker);
+ log_assert(q->state.env->worker == worker);
/* comm_reply closed in outside_network_delete */
- query_info_clear(&q->qinfo);
+ query_info_clear(&q->state.qinfo);
+ region_destroy(q->state.region);
free(q);
q = n;
}
return 0;
}
}
+ worker->scratchpad = region_create_custom(malloc, free,
+ 65536, 8192, 32, 1);
+ if(!worker->scratchpad) {
+ log_err("malloc failure");
+ worker_delete(worker);
+ return 0;
+ }
worker->request_size = cfg->num_queries_per_thread;
if(!reqs_init(worker)) {
worker_delete(worker);
return 0;
}
- /* set forwarder address */
- if(cfg->fwd_address && cfg->fwd_address[0]) {
- if(!worker_set_fwd(worker, cfg->fwd_address, cfg->fwd_port)) {
- worker_delete(worker);
- fatal_exit("could not set forwarder address");
- }
- }
server_stats_init(&worker->stats);
alloc_init(&worker->alloc, &worker->daemon->superalloc,
worker->thread_num);
- worker->scratchpad = region_create_custom(malloc, free,
- 65536, 8192, 32, 1);
+ worker->env = *worker->daemon->env;
+ worker->env.worker = worker;
+ worker->env.alloc = &worker->alloc;
return 1;
}
}
int
-worker_set_fwd(struct worker* worker, const char* ip, int port)
+worker_send_query(ldns_buffer* pkt, struct sockaddr_storage* addr,
+ socklen_t addrlen, int timeout, struct module_qstate* q, int use_tcp)
{
- uint16_t p;
- log_assert(worker && ip);
- p = (uint16_t) port;
- if(str_is_ip6(ip)) {
- struct sockaddr_in6* sa =
- (struct sockaddr_in6*)&worker->fwd_addr;
- worker->fwd_addrlen = (socklen_t)sizeof(struct sockaddr_in6);
- memset(sa, 0, worker->fwd_addrlen);
- sa->sin6_family = AF_INET6;
- sa->sin6_port = (in_port_t)htons(p);
- if(inet_pton((int)sa->sin6_family, ip, &sa->sin6_addr) <= 0) {
- log_err("Bad ip6 address %s", ip);
- return 0;
- }
- } else { /* ip4 */
- struct sockaddr_in* sa =
- (struct sockaddr_in*)&worker->fwd_addr;
- worker->fwd_addrlen = (socklen_t)sizeof(struct sockaddr_in);
- memset(sa, 0, worker->fwd_addrlen);
- sa->sin_family = AF_INET;
- sa->sin_port = (in_port_t)htons(p);
- if(inet_pton((int)sa->sin_family, ip, &sa->sin_addr) <= 0) {
- log_err("Bad ip4 address %s", ip);
- return 0;
- }
- }
- verbose(VERB_ALGO, "fwd queries to: %s %d", ip, p);
- return 1;
+ struct worker* worker = q->env->worker;
+ if(use_tcp) {
+ return pending_tcp_query(worker->back, pkt, addr, addrlen,
+ timeout, worker_handle_reply, q->work_info,
+ worker->rndstate);
+ }
+ return pending_udp_query(worker->back, pkt, addr, addrlen, timeout,
+ worker_handle_reply, q->work_info, worker->rndstate);
}
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
#include "daemon/stats.h"
+#include "util/module.h"
struct listen_dnsport;
struct outside_network;
struct config_file;
struct work_query {
/** next query in freelist */
struct work_query* next;
- /** the worker for this query */
- struct worker* worker;
+ /** query state */
+ struct module_qstate state;
/** the query reply destination, packet buffer and where to send. */
struct comm_reply query_reply;
- /** the query_info structure from the query */
- struct query_info qinfo;
- /** hash value of the query qinfo */
- hashvalue_t query_hash;
- /** next query in all-list */
- struct work_query* all_next;
/** id of query, in network byteorder. */
uint16_t query_id;
- /** flags uint16 from query */
- uint16_t query_flags;
- /** edns data from the query */
- struct edns_data edns;
+ /** next query in all-list */
+ struct work_query* all_next;
};
/**
/** list of all working queries */
struct work_query* all_queries;
- /** address to forward to */
- struct sockaddr_storage fwd_addr;
- /** length of fwd_addr */
- socklen_t fwd_addrlen;
-
/** random() table for this worker. */
struct ub_randstate* rndstate;
/** do we need to restart (instead of exit) ? */
struct server_stats stats;
/** thread scratch region */
struct region* scratchpad;
+
+ /** module environment passed to modules, changed for this thread */
+ struct module_env env;
};
/**
*/
void worker_delete(struct worker* worker);
-/**
- * Set forwarder
- * @param worker: the worker to modify.
- * @param ip: the server name.
- * @param port: port on server or NULL for default 53.
- * @return: false on error.
- */
-int worker_set_fwd(struct worker* worker, const char* ip, int port);
-
/**
* Send a command to a worker. Uses blocking writes.
* @param worker: worker to send command to.
*/
void worker_sighandler(int sig, void* arg);
+/**
+ * Worker service routine to send udp messages for modules.
+ * @param pkt: packet to send.
+ * @param addr: where to.
+ * @param addrlen: length of addr.
+ * @param timeout: seconds to wait until timeout.
+ * @param q: wich query state to reactivate upon return.
+ * @param use_tcp: true to use TCP, false for UDP.
+ * return: false on failure (memory or socket related). no query was
+ * sent.
+ */
+int worker_send_query(ldns_buffer* pkt, struct sockaddr_storage* addr,
+ socklen_t addrlen, int timeout, struct module_qstate* q, int use_tcp);
+
#endif /* DAEMON_WORKER_H */
+11 May 2007: Wouter
+ - iterator/iterator.c module.
+
10 May 2007: Wouter
- created release-0.3 svn tag.
- util/module.h
--- /dev/null
+/*
+ * iterator/iterator.c - iterative resolver DNS query response module
+ *
+ * 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 a module that performs recusive iterative DNS query
+ * processing.
+ */
+
+#include "config.h"
+#include "iterator/iterator.h"
+#include "util/module.h"
+#include "util/netevent.h"
+#include "util/config_file.h"
+#include "util/net_help.h"
+#include "util/storage/slabhash.h"
+
+/**
+ * Set forwarder address
+ * @param ie: iterator global state.
+ * @param ip: the server name.
+ * @param port: port on server or NULL for default 53.
+ * @return: false on error.
+ */
+static int
+iter_set_fwd(struct iter_env* ie, const char* ip, int port)
+{
+ uint16_t p;
+ log_assert(ie && ip);
+ p = (uint16_t) port;
+ if(str_is_ip6(ip)) {
+ struct sockaddr_in6* sa =
+ (struct sockaddr_in6*)&ie->fwd_addr;
+ ie->fwd_addrlen = (socklen_t)sizeof(struct sockaddr_in6);
+ memset(sa, 0, ie->fwd_addrlen);
+ sa->sin6_family = AF_INET6;
+ sa->sin6_port = (in_port_t)htons(p);
+ if(inet_pton((int)sa->sin6_family, ip, &sa->sin6_addr) <= 0) {
+ log_err("Bad ip6 address %s", ip);
+ return 0;
+ }
+ } else { /* ip4 */
+ struct sockaddr_in* sa =
+ (struct sockaddr_in*)&ie->fwd_addr;
+ ie->fwd_addrlen = (socklen_t)sizeof(struct sockaddr_in);
+ memset(sa, 0, ie->fwd_addrlen);
+ sa->sin_family = AF_INET;
+ sa->sin_port = (in_port_t)htons(p);
+ if(inet_pton((int)sa->sin_family, ip, &sa->sin_addr) <= 0) {
+ log_err("Bad ip4 address %s", ip);
+ return 0;
+ }
+ }
+ verbose(VERB_ALGO, "iterator: fwd queries to: %s %d", ip, p);
+ return 1;
+}
+
+/** iterator init */
+static int
+iter_init(struct module_env* env, int id)
+{
+ struct iter_env* iter_env = (struct iter_env*)calloc(1,
+ sizeof(struct iter_env));
+ if(!iter_env) {
+ log_err("malloc failure");
+ return 0;
+ }
+ env->modinfo[id] = (void*)iter_env;
+ /* set forwarder address */
+ if(env->cfg->fwd_address && env->cfg->fwd_address[0]) {
+ if(!iter_set_fwd(iter_env, env->cfg->fwd_address,
+ env->cfg->fwd_port)) {
+ log_err("iterator: could not set forwarder address");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/** iterator deinit */
+static void
+iter_deinit(struct module_env* env, int id)
+{
+ struct iter_env* iter_env;
+ if(!env || !env->modinfo)
+ return;
+ iter_env = (struct iter_env*)env->modinfo[id];
+ if(iter_env)
+ free(iter_env);
+}
+
+/** see if rrset needs to be updated in the cache */
+static int
+need_to_update_rrset(struct packed_rrset_data* newd,
+ struct packed_rrset_data* cached)
+{
+ /* o if current RRset is more trustworthy - insert it */
+ if( newd->trust > cached->trust )
+ return 1;
+ /* o same trust, but different in data - insert it */
+ if( newd->trust == cached->trust &&
+ !rrsetdata_equal(newd, cached))
+ return 1;
+ /* o see if TTL is better than TTL in cache. */
+ /* if so, see if rrset+rdata is the same */
+ /* if so, update TTL in cache, even if trust is worse. */
+ if( newd->ttl > cached->ttl &&
+ rrsetdata_equal(newd, cached))
+ return 1;
+ return 0;
+}
+
+/** store rrsets in the rrset cache. */
+static void
+worker_store_rrsets(struct module_env* env, struct reply_info* rep)
+{
+ struct lruhash_entry* e;
+ size_t i;
+ /* see if rrset already exists in cache, if not insert it. */
+ /* if it does exist: check to insert it */
+ for(i=0; i<rep->rrset_count; i++) {
+ rep->ref[i].key = rep->rrsets[i];
+ rep->ref[i].id = rep->rrsets[i]->id;
+ /* looks up item with a readlock - no editing! */
+ if((e=slabhash_lookup(env->rrset_cache,
+ rep->rrsets[i]->entry.hash, rep->rrsets[i]->entry.key,
+ 0)) != 0) {
+ struct packed_rrset_data* data =
+ (struct packed_rrset_data*)e->data;
+ struct packed_rrset_data* rd =
+ (struct packed_rrset_data*)
+ rep->rrsets[i]->entry.data;
+ rep->ref[i].key = (struct ub_packed_rrset_key*)e->key;
+ rep->ref[i].id = rep->rrsets[i]->id;
+ /* found in cache, do checks above */
+ if(!need_to_update_rrset(rd, data)) {
+ lock_rw_unlock(&e->lock);
+ ub_packed_rrset_parsedelete(rep->rrsets[i],
+ env->alloc);
+ rep->rrsets[i] = rep->ref[i].key;
+ continue; /* use cached item instead */
+ }
+ if(rd->trust < data->trust)
+ rd->trust = data->trust;
+ lock_rw_unlock(&e->lock);
+ /* small gap here, where entry is not locked.
+ * possibly entry is updated with something else.
+ * this is just too bad, its cache anyway. */
+ /* use insert to update entry to manage lruhash
+ * cache size values nicely. */
+ }
+ slabhash_insert(env->rrset_cache,
+ rep->rrsets[i]->entry.hash, &rep->rrsets[i]->entry,
+ rep->rrsets[i]->entry.data, env->alloc);
+ if(e) rep->rrsets[i] = rep->ref[i].key;
+ }
+}
+
+
+/** store message in the cache */
+static void
+store_msg(struct module_qstate* qstate, struct query_info* qinfo,
+ struct reply_info* rep)
+{
+ struct msgreply_entry* e;
+ reply_info_set_ttls(rep, time(0));
+ worker_store_rrsets(qstate->env, rep);
+ if(rep->ttl == 0) {
+ log_info("TTL 0: dropped msg from cache");
+ return;
+ }
+ reply_info_sortref(rep);
+ /* store msg in the cache */
+ if(!(e = query_info_entrysetup(qinfo, rep, qstate->query_hash))) {
+ log_err("store_msg: malloc failed");
+ return;
+ }
+ slabhash_insert(qstate->env->msg_cache, qstate->query_hash,
+ &e->entry, rep, &qstate->env->alloc);
+}
+
+/** iterator operate on a query */
+static void
+iter_operate(struct module_qstate* qstate, enum module_ev event, int id)
+{
+ struct module_env* env = qstate->env;
+ struct iter_env* ie = (struct iter_env*)env->modinfo[id];
+ verbose(VERB_ALGO, "iterator[module %d] operate: extstate:%s event:%s",
+ id, strextstate(qstate->ext_state[id]), strmodulevent(event));
+ if(event == module_event_error) {
+ qstate->ext_state[id] = module_error;
+ return;
+ }
+ if(event == module_event_new) {
+ /* send UDP query to forwarder address */
+ (*env->send_query)(qstate->buf, &ie->fwd_addr,
+ ie->fwd_addrlen, UDP_QUERY_TIMEOUT, qstate, 0);
+ qstate->ext_state[id] = module_wait_reply;
+ qstate->minfo[id] = NULL;
+ return;
+ }
+ if(event == module_event_timeout) {
+ /* try TCP if UDP fails */
+ if(qstate->reply->c->type == comm_udp) {
+ qinfo_query_encode(qstate->buf, &qstate->qinfo);
+ (*env->send_query)(qstate->buf, &ie->fwd_addr,
+ ie->fwd_addrlen, TCP_QUERY_TIMEOUT, qstate, 1);
+ return;
+ }
+ qstate->ext_state[id] = module_error;
+ return;
+ }
+ if(event == module_event_reply) {
+ uint16_t us = qstate->edns.udp_size;
+ struct query_info reply_qinfo;
+ struct reply_info* reply_msg;
+ struct edns_data reply_edns;
+ int r;
+ /* see if it is truncated */
+ if(LDNS_TC_WIRE(ldns_buffer_begin(qstate->reply->c->buffer))
+ && qstate->reply->c->type == comm_udp) {
+ log_info("TC: truncated. retry in TCP mode.");
+ qinfo_query_encode(qstate->buf, &qstate->qinfo);
+ (*env->send_query)(qstate->buf, &ie->fwd_addr,
+ ie->fwd_addrlen, TCP_QUERY_TIMEOUT, qstate, 1);
+ /* stay in wait_reply state */
+ return;
+ }
+ if((r=reply_info_parse(qstate->reply->c->buffer, env->alloc,
+ &reply_qinfo, &reply_msg, qstate->scratch,
+ &reply_edns))!=0) {
+ qstate->ext_state[id] = module_error;
+ return;
+ }
+
+ qstate->edns.edns_version = EDNS_ADVERTISED_VERSION;
+ qstate->edns.udp_size = EDNS_ADVERTISED_SIZE;
+ qstate->edns.ext_rcode = 0;
+ qstate->edns.bits &= EDNS_DO;
+ if(!reply_info_answer_encode(&reply_qinfo, reply_msg, 0,
+ qstate->query_flags, qstate->buf, 0, 0,
+ qstate->scratch, us, &qstate->edns)) {
+ qstate->ext_state[id] = module_error;
+ return;
+ }
+ store_msg(qstate, &reply_qinfo, reply_msg);
+ qstate->ext_state[id] = module_finished;
+ return;
+ }
+ log_err("bad event for iterator");
+ qstate->ext_state[id] = module_error;
+}
+
+/** iterator cleanup query state */
+static void
+iter_clear(struct module_qstate* qstate, int id)
+{
+ if(!qstate)
+ return;
+ /* allocated in region, so nothing to do */
+ qstate->minfo[id] = NULL;
+}
+
+/**
+ * The iterator function block
+ */
+static struct module_func_block iter_block = {
+ "iterator",
+ &iter_init, &iter_deinit, &iter_operate, &iter_clear
+};
+
+struct module_func_block*
+iter_get_funcblock()
+{
+ return &iter_block;
+}
--- /dev/null
+/*
+ * iterator/iterator.h - iterative resolver DNS query response module
+ *
+ * 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 a module that performs recusive iterative DNS query
+ * processing.
+ */
+
+#ifndef ITERATOR_ITERATOR_H
+#define ITERATOR_ITERATOR_H
+struct module_func_block;
+
+/**
+ * Global state for the iterator.
+ */
+struct iter_env {
+ /** address to forward to */
+ struct sockaddr_storage fwd_addr;
+ /** length of fwd_addr */
+ socklen_t fwd_addrlen;
+};
+
+/**
+ * Per query state for the iterator module.
+ */
+struct iter_qstate {
+};
+
+/**
+ * Get the iterator function block.
+ */
+struct module_func_block* iter_get_funcblock();
+
+#endif /* ITERATOR_ITERATOR_H */
}
/** use next free buffer to service a tcp query */
-static void
+static int
outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt)
{
struct pending_tcp* pend = w->outnet->tcp_free;
if(s == -1) {
log_err("outgoing tcp: socket: %s", strerror(errno));
log_addr(&w->addr, w->addrlen);
- (void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL);
- waiting_tcp_delete(w);
- return;
+ return 0;
}
fd_set_nonblock(s);
if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
log_err("outgoing tcp: connect: %s", strerror(errno));
log_addr(&w->addr, w->addrlen);
close(s);
- (void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL);
- waiting_tcp_delete(w);
- return;
+ return 0;
}
}
w->pkt = NULL;
pend->c->tcp_is_reading = 0;
pend->c->tcp_byte_count = 0;
comm_point_start_listening(pend->c, s, -1);
- return;
+ return 1;
}
/** see if buffers can be used to service TCP queries. */
outnet->tcp_wait_first = w->next_waiting;
if(outnet->tcp_wait_last == w)
outnet->tcp_wait_last = NULL;
- outnet_tcp_take_into_use(w, w->pkt);
+ if(!outnet_tcp_take_into_use(w, w->pkt)) {
+ (void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL);
+ waiting_tcp_delete(w);
+ }
}
}
}
-void
+int
pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
comm_point_callback_t* cb, void* cb_arg, struct ub_randstate* rnd)
/* create pending struct and change ID to be unique */
if(!(pend=new_pending(outnet, packet, addr, addrlen, cb, cb_arg,
rnd))) {
- /* callback user for the error */
- (void)(*cb)(NULL, cb_arg, NETEVENT_CLOSED, NULL);
- return;
+ return 0;
}
select_port(outnet, pend, rnd);
/* send it over the commlink */
if(!comm_point_send_udp_msg(pend->c, packet, (struct sockaddr*)addr,
addrlen)) {
- /* callback user for the error */
- (void)(*pend->cb)(pend->c, pend->cb_arg, NETEVENT_CLOSED, NULL);
pending_delete(outnet, pend);
- return;
+ return 0;
}
/* system calls to set timeout after sending UDP to make roundtrip
tv.tv_sec = timeout;
tv.tv_usec = 0;
comm_timer_set(pend->timer, &tv);
+ return 1;
}
/** callback for outgoing TCP timer event */
use_free_buffer(outnet);
}
-void
+int
pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
comm_point_callback_t* callback, void* callback_arg,
w = (struct waiting_tcp*)malloc(sizeof(struct waiting_tcp)
+ (pend?0:ldns_buffer_limit(packet)));
if(!w) {
- /* callback user for the error */
- (void)(*callback)(NULL, callback_arg, NETEVENT_CLOSED, NULL);
- return;
+ return 0;
}
if(!(w->timer = comm_timer_create(outnet->base, outnet_tcptimer, w))) {
free(w);
- (void)(*callback)(NULL, callback_arg, NETEVENT_CLOSED, NULL);
- return;
+ return 0;
}
w->pkt = NULL;
w->pkt_len = ldns_buffer_limit(packet);
comm_timer_set(w->timer, &tv);
if(pend) {
/* we have a buffer available right now */
- outnet_tcp_take_into_use(w, ldns_buffer_begin(packet));
+ if(!outnet_tcp_take_into_use(w, ldns_buffer_begin(packet))) {
+ waiting_tcp_delete(w);
+ return 0;
+ }
} else {
/* queue up */
w->pkt = (uint8_t*)w + sizeof(struct waiting_tcp);
else outnet->tcp_wait_first = w;
outnet->tcp_wait_last = w;
}
+ return 1;
}
* @param addrlen: length of addr.
* @param timeout: in seconds from now.
* @param callback: function to call on error, timeout or reply.
- * The routine does not return an error, instead it calls the callback,
- * with an error code if an error happens.
* @param callback_arg: user argument for callback function.
* @param rnd: random state for generating ID and port.
+ * @return: false on error for malloc or socket.
*/
-void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
+int pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
comm_point_callback_t* callback, void* callback_arg,
struct ub_randstate* rnd);
* Timer starts running now. Timer may expire if all buffers are used,
* without any query been sent to the server yet.
* @param callback: function to call on error, timeout or reply.
- * The routine does not return an error, instead it calls the callback,
- * with an error code if an error happens.
* @param callback_arg: user argument for callback function.
* @param rnd: random state for generating ID.
+ * @return: false on error for malloc or socket.
*/
-void pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
+int pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
comm_point_callback_t* callback, void* callback_arg,
struct ub_randstate* rnd);
free(outnet);
}
-void
+int
pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
comm_point_callback_t* callback, void* callback_arg,
/* add to list */
pend->next = runtime->pending_list;
runtime->pending_list = pend;
+ return 1;
}
-void
+int
pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
struct sockaddr_storage* addr, socklen_t addrlen, int timeout,
comm_point_callback_t* callback, void* callback_arg,
/* add to list */
pend->next = runtime->pending_list;
runtime->pending_list = pend;
+ return 1;
}
struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg))
#include "config.h"
#include "util/module.h"
+
+const char*
+strextstate(enum module_ext_state s)
+{
+ switch(s) {
+ case module_state_initial: return "module_state_initial";
+ case module_wait_reply: return "module_wait_reply";
+ case module_wait_module: return "module_wait_module";
+ case module_wait_subquery: return "module_wait_subquery";
+ case module_error: return "module_error";
+ case module_finished: return "module_finished";
+ }
+ return "bad_extstate_value";
+}
+
+const char*
+strmodulevent(enum module_ev e)
+{
+ switch(e) {
+ case module_event_new: return "module_event_new";
+ case module_event_pass: return "module_event_pass";
+ case module_event_reply: return "module_event_reply";
+ case module_event_timeout: return "module_event_timeout";
+ case module_event_mod_done: return "module_event_mod_done";
+ case module_event_subq_done: return "module_event_subq_done";
+ case module_event_error: return "module_event_error";
+ }
+ return "bad_event_value";
+}
#ifndef UTIL_MODULE_H
#define UTIL_MODULE_H
#include "util/storage/lruhash.h"
+#include "util/data/msgreply.h"
+#include "util/data/msgparse.h"
+struct alloc_cache;
struct config_file;
struct slabhash;
struct query_info;
struct edns_data;
struct region;
struct worker;
+struct module_qstate;
/** Maximum number of modules in operation */
#define MAX_MODULE 2
struct slabhash* rrset_cache;
/* --- services --- */
- /** send DNS query to server. operate() should return with wait_reply */
+ /**
+ * Send DNS query to server. operate() should return with wait_reply.
+ * Later on a callback will cause operate() to be called with event
+ * timeout or reply.
+ * @param pkt: packet to send.
+ * @param addr: where to.
+ * @param addrlen: length of addr.
+ * @param timeout: seconds to wait until timeout.
+ * @param q: wich query state to reactivate upon return.
+ * @param use_tcp: set to true to send over TCP. 0 for UDP.
+ * return: false on failure (memory or socket related). no query was
+ * sent.
+ */
+ int (*send_query)(ldns_buffer* pkt, struct sockaddr_storage* addr,
+ socklen_t addrlen, int timeout, struct module_qstate* q,
+ int use_tcp);
+
/** create a subquery. operate should then return with wait_subq */
/** allocation service */
- struct alloc* alloc;
+ struct alloc_cache* alloc;
/** internal data for daemon - worker thread. */
struct worker* worker;
/** module specific data. indexed by module id. */
*/
struct module_qstate {
/** which query is being answered: name, type, class */
- struct query_info* qinfo;
+ struct query_info qinfo;
/** hash value of the query qinfo */
hashvalue_t query_hash;
/** flags uint16 from query */
uint16_t query_flags;
/** edns data from the query */
- struct edns_data* edns;
+ struct edns_data edns;
- /** buffer, contains server replies, store resulting reply here.
+ /** buffer, store resulting reply here.
* May be cleared when module blocks. */
ldns_buffer* buf;
- /** parsed message from server */
- struct msg_parse* msg_parse;
+ /** comm_reply contains server replies */
+ struct comm_reply* reply;
/** region for temporary usage. May be cleared when module blocks. */
struct region* scratch;
/** region for this query. Cleared when query process finishes. */
struct region* region;
+ /** which module is executing */
+ int curmod;
/** module states */
enum module_ext_state ext_state[MAX_MODULE];
/** module specific data for query. indexed by module id. */
void* minfo[MAX_MODULE];
/** environment for this query */
- struct module_env* module_env;
+ struct module_env* env;
+ /** worker related state for this query */
+ struct work_query* work_info;
/** parent query, only nonNULL for subqueries */
struct module_qstate* parent;
char* name;
/**
- * init the module
+ * init the module. Called once for the global state.
+ * This is the place to apply settings from the config file.
* @param env: module environment.
* @param id: module id number.
* return: 0 on error
*/
int (*init)(struct module_env* env, int id);
/**
- * de-init, delete, the module.
+ * de-init, delete, the module. Called once for the global state.
* @param env: module environment.
* @param id: module id number.
*/
void (*clear)(struct module_qstate* qstate, int id);
};
+/**
+ * Debug utility: module external qstate to string
+ * @param s: the state value.
+ * @return descriptive string.
+ */
+const char* strextstate(enum module_ext_state s);
+
+/**
+ * Debug utility: module event to string
+ * @param e: the module event value.
+ * @return descriptive string.
+ */
+const char* strmodulevent(enum module_ev e);
+
#endif /* UTIL_MODULE_H */
/** QR flag */
#define BIT_QR 0x8000
+/** timeout in seconds for UDP queries to auth servers. TODO: proper rtt */
+#define UDP_QUERY_TIMEOUT 4
+/** timeout in seconds for TCP queries to auth servers. TODO: proper rtt */
+#define TCP_QUERY_TIMEOUT 30
+/** Advertised version of EDNS capabilities */
+#define EDNS_ADVERTISED_VERSION 0
+/** Advertised size of EDNS capabilities */
+#define EDNS_ADVERTISED_SIZE 4096
+
/**
* See if string is ip4 or ip6.
* @param str: IP specification.