]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
outbound queries via serviced outside_network queries.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 22 May 2007 12:36:02 +0000 (12:36 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Tue, 22 May 2007 12:36:02 +0000 (12:36 +0000)
git-svn-id: file:///svn/unbound/trunk@327 be551aaa-1e26-0410-a405-d3ace91eadb9

13 files changed:
daemon/daemon.c
daemon/worker.c
daemon/worker.h
doc/Changelog
iterator/iterator.c
iterator/iterator.h
services/outbound_list.c [new file with mode: 0644]
services/outbound_list.h [new file with mode: 0644]
services/outside_network.c
testcode/fake_event.c
testcode/replay.h
testdata/fwd_trunc.rpl [deleted file]
util/module.h

index b3670e74564bff358cb0c43532891de646f373ac..26b42a53fe415aa869551ea55cd45eb004e346ba 100644 (file)
@@ -162,6 +162,7 @@ static void daemon_setup_modules(struct daemon* daemon)
        daemon->env->cfg = daemon->cfg;
        daemon->env->alloc = &daemon->superalloc;
        daemon->env->worker = NULL;
+       daemon->env->send_packet = &worker_send_packet;
        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);
index 57dd3ae0780b3bb038c5894d0de0cba1d7cf6fd3..f3d51d1e374e6ee698fc9a8b108091eb5aa860ac 100644 (file)
 #include "daemon/daemon.h"
 #include "util/netevent.h"
 #include "util/config_file.h"
+#include "util/module.h"
 #include "util/region-allocator.h"
 #include "util/storage/slabhash.h"
 #include "services/listen_dnsport.h"
 #include "services/outside_network.h"
+#include "services/outbound_list.h"
 #include "services/cache/rrset.h"
 #include "util/data/msgparse.h"
 
@@ -136,7 +138,7 @@ replyerror(int r, struct work_query* w)
 /** process incoming request */
 static void 
 worker_process_query(struct worker* worker, struct work_query* w, 
-       enum module_ev event) 
+       enum module_ev event, struct outbound_entry* entry
 {
        int i;
        if(event == module_event_new) {
@@ -146,7 +148,7 @@ worker_process_query(struct worker* worker, struct work_query* w,
        }
        /* allow current module to run */
        (*worker->daemon->modfunc[w->state.curmod]->operate)(&w->state, event,
-               w->state.curmod);
+               w->state.curmod, entry);
        /* TODO examine results, start further modules, etc.
         * assume it went to sleep
         */
@@ -178,7 +180,7 @@ worker_handle_reply(struct comm_point* c, void* arg, int error,
 
        w->state.reply = reply_info;
        if(error != 0) {
-               worker_process_query(worker, w, module_event_timeout);
+               worker_process_query(worker, w, module_event_timeout, NULL);
                w->state.reply = NULL;
                return 0;
        }
@@ -189,11 +191,42 @@ worker_handle_reply(struct comm_point* c, void* arg, int error,
                || 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);
+               worker_process_query(worker, w, module_event_timeout, NULL);
                w->state.reply = NULL;
                return 0;
        }
-       worker_process_query(worker, w, module_event_reply);
+       worker_process_query(worker, w, module_event_reply, NULL);
+       w->state.reply = NULL;
+       return 0;
+}
+
+/** process incoming serviced query replies from the network */
+static int 
+worker_handle_service_reply(struct comm_point* c, void* arg, int error, 
+       struct comm_reply* reply_info)
+{
+       struct outbound_entry* e = (struct outbound_entry*)arg;
+       struct work_query* w = e->qstate->work_info;
+       struct worker* worker = w->state.env->worker;
+
+       w->state.reply = reply_info;
+       if(error != 0) {
+               worker_process_query(worker, w, module_event_timeout, e);
+               w->state.reply = NULL;
+               return 0;
+       }
+       /* sanity check. */
+       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, e);
+               w->state.reply = NULL;
+               return 0;
+       }
+       worker_process_query(worker, w, module_event_reply, e);
        w->state.reply = NULL;
        return 0;
 }
@@ -444,7 +477,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
 
        /* answer it */
        w->state.buf = c->buffer;
-       worker_process_query(worker, w, module_event_new);
+       worker_process_query(worker, w, module_event_new, NULL);
        return 0;
 }
 
@@ -688,7 +721,7 @@ worker_delete(struct worker* worker)
 }
 
 int 
-worker_send_query(ldns_buffer* pkt, struct sockaddr_storage* addr,
+worker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr,
         socklen_t addrlen, int timeout, struct module_qstate* q, int use_tcp)
 {
        struct worker* worker = q->env->worker;
@@ -700,3 +733,24 @@ worker_send_query(ldns_buffer* pkt, struct sockaddr_storage* addr,
        return pending_udp_query(worker->back, pkt, addr, addrlen, timeout,
                worker_handle_reply, q->work_info, worker->rndstate) != 0;
 }
+
+struct outbound_entry*
+worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
+       uint16_t qclass, uint16_t flags, int dnssec,
+       struct sockaddr_storage* addr, socklen_t addrlen,
+       struct module_qstate* q)
+{
+       struct worker* worker = q->env->worker;
+       struct outbound_entry* e = (struct outbound_entry*)malloc(sizeof(*e));
+       if(!e) 
+               return NULL;
+       e->qstate = q;
+       e->qsent = outnet_serviced_query(worker->back, qname,
+               qnamelen, qtype, qclass, flags, dnssec, addr, addrlen, 
+               worker_handle_service_reply, e, worker->back->udp_buff);
+       if(!e->qsent) {
+               free(e);
+               return NULL;
+       }
+       return e;
+}
index 0d21b3e7c13918bc50d8b8d1aafd68a149dbb8bb..557c123193c974a5f7531f9a62d1a777c1e7f259 100644 (file)
@@ -188,10 +188,29 @@ void worker_sighandler(int sig, void* arg);
  * @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
+ * @return: false on failure (memory or socket related). no query was
  *      sent.
  */
-int worker_send_query(ldns_buffer* pkt, struct sockaddr_storage* addr,
+int worker_send_packet(ldns_buffer* pkt, struct sockaddr_storage* addr,
        socklen_t addrlen, int timeout, struct module_qstate* q, int use_tcp);
 
+/**
+ * Worker service routine to send serviced queries to authoritative servers.
+ * @param qname: query name. (host order)
+ * @param qnamelen: length in bytes of qname, including trailing 0.
+ * @param qtype: query type. (host order)
+ * @param qclass: query class. (host order)
+ * @param flags: host order flags word, with opcode and CD bit.
+ * @param dnssec: if set, EDNS record will have DO bit set.
+ * @param addr: where to.
+ * @param addrlen: length of addr.
+ * @param q: wich query state to reactivate upon return.
+ * @return: false on failure (memory or socket related). no query was
+ *      sent.
+ */
+struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen, 
+       uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, 
+       struct sockaddr_storage* addr, socklen_t addrlen,
+       struct module_qstate* q);
+
 #endif /* DAEMON_WORKER_H */
index 66e5c0477defd1960e0fd383dd4f6ac9d9a33966..3ba2be7dea57ae4e45c72252d4c99c590d35c134 100644 (file)
@@ -1,3 +1,9 @@
+22 May 2007: Wouter
+       - outbound query list for modules and support to callback with the
+         outbound entry to the module.
+       - testbound support for new serviced queries.
+       - test for retry to TCP cannot use testbound any longer.
+
 21 May 2007: Wouter
        - small comment on hash table locking.
        - outside network serviced queries, contain edns and tcp fallback,
index fb62cea9df98aaa50cb4084ca506340f0d624c16..07c16e37b73f1648b1997e5e8682a75a4841d894 100644 (file)
@@ -47,6 +47,7 @@
 #include "util/config_file.h"
 #include "util/net_help.h"
 #include "util/storage/slabhash.h"
+#include "util/region-allocator.h"
 #include "services/cache/rrset.h"
 
 /** 
@@ -162,74 +163,87 @@ store_msg(struct module_qstate* qstate, struct query_info* qinfo,
                &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)
+/** new query for iterator */
+static int
+iter_new(struct module_qstate* qstate, int id)
 {
+       struct iter_qstate* iq = (struct iter_qstate*)region_alloc(
+               qstate->region, sizeof(struct iter_qstate));
        struct module_env* env = qstate->env;
        struct iter_env* ie = (struct iter_env*)env->modinfo[id];
+       struct outbound_entry* e;
+       uint16_t flags = 0; /* opcode=query, no flags */
+       int dnssec = 1; /* always get dnssec info */
+       qstate->minfo[id] = iq;
+       if(!iq) 
+               return 0;
+       outbound_list_init(&iq->outlist);
+       if(qstate->qinfo.has_cd)
+               flags |= BIT_CD;
+       e = (*env->send_query)(qstate->qinfo.qname, qstate->qinfo.qnamesize,
+               qstate->qinfo.qtype, qstate->qinfo.qclass, flags, dnssec, 
+               &ie->fwd_addr, ie->fwd_addrlen, qstate);
+       if(!e) 
+               return 0;
+       outbound_list_insert(&iq->outlist, e);
+       qstate->ext_state[id] = module_wait_reply;
+       return 1;
+}
+
+/** iterator handle reply from authoritative server */
+static int
+iter_handlereply(struct module_qstate* qstate, int id,
+        struct outbound_entry* ATTR_UNUSED(outbound))
+{
+       struct module_env* env = qstate->env;
+       uint16_t us = qstate->edns.udp_size;
+       struct query_info reply_qinfo;
+       struct reply_info* reply_msg;
+       struct edns_data reply_edns;
+       int r;
+       if((r=reply_info_parse(qstate->reply->c->buffer, env->alloc, 
+               &reply_qinfo, &reply_msg, qstate->scratch, 
+               &reply_edns))!=0)
+               return 0;
+
+       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))
+               return 0;
+       store_msg(qstate, &reply_qinfo, reply_msg);
+       qstate->ext_state[id] = module_finished;
+       return 1;
+}
+
+/** iterator operate on a query */
+static void 
+iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
+       struct outbound_entry* outbound)
+{
        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;
+       if(event == module_event_new) {
+               if(!iter_new(qstate, id))
+                       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;
+       /* it must be a query reply */
+       if(!outbound) {
+               verbose(VERB_ALGO, "query reply was not serviced");
+               qstate->ext_state[id] = module_error;
                return;
        }
-       if(event == module_event_timeout) {
-               /* try TCP if UDP fails */
-               /* TODO: disabled now, make better retry with EDNS.
-               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;
-               }
-               */
+       if(event == module_event_timeout || event == module_event_error) {
                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) {
+               if(!iter_handlereply(qstate, id, outbound))
                        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");
@@ -240,9 +254,11 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id)
 static void 
 iter_clear(struct module_qstate* qstate, int id)
 {
+       struct iter_qstate* iq;
        if(!qstate)
                return;
-       /* allocated in region, so nothing to do */
+       iq = (struct iter_qstate*)qstate->minfo[id];
+       outbound_list_clear(&iq->outlist);
        qstate->minfo[id] = NULL;
 }
 
index 0fc8c26fde3eb4c6747bfca6ca52757f6a78740c..5a075e66345a02d9c9b46061638248d8711c5fba 100644 (file)
@@ -42,6 +42,7 @@
 
 #ifndef ITERATOR_ITERATOR_H
 #define ITERATOR_ITERATOR_H
+#include "services/outbound_list.h"
 struct module_func_block;
 
 /**
@@ -58,6 +59,8 @@ struct iter_env {
  * Per query state for the iterator module.
  */
 struct iter_qstate {
+       /** list of pending queries to authoritative servers. */
+       struct outbound_list outlist;
 };
 
 /**
diff --git a/services/outbound_list.c b/services/outbound_list.c
new file mode 100644 (file)
index 0000000..789877a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * services/outbound_list.c - keep list of outbound serviced queries.
+ *
+ * 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 functions to help a module keep track of the
+ * queries it has outstanding to authoritative servers.
+ */
+#include "config.h"
+#include "services/outbound_list.h"
+#include "services/outside_network.h"
+
+void 
+outbound_list_init(struct outbound_list* list)
+{
+       list->first = NULL;
+}
+
+void 
+outbound_list_clear(struct outbound_list* list)
+{
+       struct outbound_entry *p, *np;
+       p = list->first;
+       while(p) {
+               np = p->next;
+               outnet_serviced_query_stop(p->qsent, p);
+               free(p);
+               p = np;
+       }
+       outbound_list_init(list);
+}
+
+void 
+outbound_list_insert(struct outbound_list* list, struct outbound_entry* e)
+{
+       e->next = list->first;
+       e->prev = NULL;
+       list->first = e;
+}
+
+void 
+outbound_list_remove(struct outbound_list* list, struct outbound_entry* e)
+{
+       if(!e)
+               return;
+       outnet_serviced_query_stop(e->qsent, e);
+       if(e->next)
+               e->next->prev = e->prev;
+       if(e->prev)
+               e->prev->next = e->next;
+       else    list->first = e->next;
+       free(e);
+}
diff --git a/services/outbound_list.h b/services/outbound_list.h
new file mode 100644 (file)
index 0000000..5631910
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * services/outbound_list.h - keep list of outbound serviced queries.
+ *
+ * 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 functions to help a module keep track of the
+ * queries it has outstanding to authoritative servers.
+ */
+#ifndef SERVICES_OUTBOUND_LIST_H
+#define SERVICES_OUTBOUND_LIST_H
+struct outbound_entry;
+struct serviced_query;
+struct module_qstate;
+
+/**
+ * The outbound list. This structure is part of the module specific query
+ * state.
+ */
+struct outbound_list {
+       /** The linked list of outbound query entries. */
+       struct outbound_entry* first;
+};
+
+/**
+ * Outbound list entry. A serviced query sent by a module processing the
+ * query from the qstate. Double linked list to aid removal.
+ */
+struct outbound_entry {
+       /** next in list */
+       struct outbound_entry* next;
+       /** prev in list */
+       struct outbound_entry* prev;
+       /** The query that was sent out */
+       struct serviced_query* qsent;
+       /** the module query state that sent it */
+       struct module_qstate* qstate;
+};
+
+/**
+ * Init the user allocated outbound list structure
+ * @param list: the list structure.
+ */
+void outbound_list_init(struct outbound_list* list);
+
+/**
+ * Clear the user owner outbound list structure.
+ * Deletes serviced queries.
+ * @param list: the list structure. It is cleared, but the list struct itself
+ *     is callers responsability to delete.
+ */
+void outbound_list_clear(struct outbound_list* list);
+
+/**
+ * Insert new entry into the list. Caller must allocate the entry with malloc.
+ * qstate and qsent are set by caller.
+ * @param list: the list to add to.
+ * @param e: entry to add, it is only half initialised at call start, fully
+ *     initialised at call end.
+ */
+void outbound_list_insert(struct outbound_list* list, 
+       struct outbound_entry* e);
+
+/**
+ * Remove an entry from the list, and deletes it. 
+ * Deletes serviced query in the entry.
+ * @param list: the list to remove from.
+ * @param e: the entry to remove.
+ */
+void outbound_list_remove(struct outbound_list* list, 
+       struct outbound_entry* e);
+
+#endif /* SERVICES_OUTBOUND_LIST_H */
index eb9e15bb85fb7ad41e0dd00fcee9982f1d0cad69..b515af281e588cb3ba6891415d606e4c92be3976 100644 (file)
@@ -953,6 +953,7 @@ serviced_encode(struct serviced_query* sq, ldns_buffer* buff, int with_edns)
        ldns_buffer_clear(buff);
        ldns_buffer_write_u16(buff, 0); /* id placeholder */
        ldns_buffer_write(buff, sq->qbuf, sq->qbuflen);
+       ldns_buffer_flip(buff);
        if(with_edns) {
                /* add edns section */
                struct edns_data edns;
@@ -965,7 +966,6 @@ serviced_encode(struct serviced_query* sq, ldns_buffer* buff, int with_edns)
                        edns.bits = EDNS_DO;
                attach_edns_record(buff, &edns);
        }
-       ldns_buffer_flip(buff);
 }
 
 /**
@@ -1002,11 +1002,12 @@ serviced_udp_send(struct serviced_query* sq, ldns_buffer* buff)
 
 /** call the callbacks for a serviced query */
 static void
-serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c)
+serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
+       struct comm_reply* rep)
 {
        struct service_callback* p = sq->cblist;
        while(p) {
-               (void)(*p->cb)(c, p->cb_arg, error, NULL);
+               (void)(*p->cb)(c, p->cb_arg, error, rep);
                p = p->next;
        }
 }
@@ -1014,7 +1015,7 @@ serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c)
 /** TCP reply or error callback for serviced queries */
 static int 
 serviced_tcp_callback(struct comm_point* c, void* arg, int error,
-        struct comm_reply* ATTR_UNUSED(rep))
+        struct comm_reply* rep)
 {
        struct serviced_query* sq = (struct serviced_query*)arg;
        sq->pending = NULL; /* removed after this callback */
@@ -1030,7 +1031,7 @@ serviced_tcp_callback(struct comm_point* c, void* arg, int error,
        }
 
        (void)rbtree_delete(sq->outnet->serviced, sq);
-       serviced_callbacks(sq, error, c);
+       serviced_callbacks(sq, error, c, rep);
        serviced_delete(sq);
        return 0;
 }
@@ -1048,14 +1049,14 @@ serviced_tcp_initiate(struct outside_network* outnet,
                 * clash with this entry */
                log_err("serviced_tcp_initiate: failed to send tcp query");
                (void)rbtree_delete(outnet->serviced, sq);
-               serviced_callbacks(sq, NETEVENT_CLOSED, NULL);
+               serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL);
                serviced_delete(sq);
        }
 }
 
 static int 
 serviced_udp_callback(struct comm_point* c, void* arg, int error,
-        struct comm_reply* ATTR_UNUSED(rep))
+        struct comm_reply* rep)
 {
        struct serviced_query* sq = (struct serviced_query*)arg;
        struct outside_network* outnet = sq->outnet;
@@ -1070,7 +1071,7 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
                if(sq->retry < OUTBOUND_UDP_RETRY) {
                        if(!serviced_udp_send(sq, c->buffer)) {
                                (void)rbtree_delete(outnet->serviced, sq);
-                               serviced_callbacks(sq, NETEVENT_CLOSED, c);
+                               serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
                                serviced_delete(sq);
                        }
                        return 0;
@@ -1090,7 +1091,7 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
                sq->retry = 0;
                if(!serviced_udp_send(sq, c->buffer)) {
                        (void)rbtree_delete(outnet->serviced, sq);
-                       serviced_callbacks(sq, NETEVENT_CLOSED, c);
+                       serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
                        serviced_delete(sq);
                }
                return 0;
@@ -1112,7 +1113,7 @@ serviced_udp_callback(struct comm_point* c, void* arg, int error,
                        roundtime*1000, now))
                        log_err("out of memory noting rtt.");
        (void)rbtree_delete(outnet->serviced, sq);
-       serviced_callbacks(sq, error, c);
+       serviced_callbacks(sq, error, c, rep);
        serviced_delete(sq);
        return 0;
 }
@@ -1175,7 +1176,7 @@ void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
                return;
        callback_list_remove(sq, cb_arg);
        if(!sq->cblist) {
-               (void)rbtree_delete(sq->outnet->serviced, sq);
-               serviced_delete(sq);
+               if(rbtree_delete(sq->outnet->serviced, sq))
+                       serviced_delete(sq); /* safe against double delete */
        }
 }
index f4a3a73264acb3e38f28c2767b7004ce61a440de..0255c9df31646d609946d16e870db0ac19c07ba2 100644 (file)
@@ -48,6 +48,8 @@
 #include "testcode/fake_event.h"
 #include "util/netevent.h"
 #include "util/net_help.h"
+#include "util/data/msgparse.h"
+#include "util/data/msgreply.h"
 #include "services/listen_dnsport.h"
 #include "services/outside_network.h"
 #include "testcode/replay.h"
@@ -688,6 +690,7 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
        pend->timeout = timeout;
        pend->transport = transport_udp;
        pend->pkt = NULL;
+       pend->runtime = runtime;
        status = ldns_buffer2pkt_wire(&pend->pkt, packet);
        if(status != LDNS_STATUS_OK) {
                log_err("ldns error parsing udp output packet: %s",
@@ -736,6 +739,7 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
        pend->timeout = timeout;
        pend->transport = transport_tcp;
        pend->pkt = NULL;
+       pend->runtime = runtime;
        status = ldns_buffer2pkt_wire(&pend->pkt, packet);
        if(status != LDNS_STATUS_OK) {
                log_err("ldns error parsing tcp output packet: %s",
@@ -761,6 +765,100 @@ pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet,
        return (struct waiting_tcp*)pend;
 }
 
+struct serviced_query* outnet_serviced_query(struct outside_network* outnet,
+        uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass,
+       uint16_t flags, int dnssec, struct sockaddr_storage* addr,
+       socklen_t addrlen, comm_point_callback_t* callback,
+       void* callback_arg, ldns_buffer* ATTR_UNUSED(buff))
+{
+       struct replay_runtime* runtime = (struct replay_runtime*)outnet->base;
+       struct fake_pending* pend = (struct fake_pending*)calloc(1,
+               sizeof(struct fake_pending));
+       ldns_status status;
+       log_assert(pend);
+
+       /* create packet with EDNS */
+       pend->buffer = ldns_buffer_new(512);
+       log_assert(pend->buffer);
+       ldns_buffer_write_u16(pend->buffer, 0); /* id */
+       ldns_buffer_write_u16(pend->buffer, flags);
+       ldns_buffer_write_u16(pend->buffer, 1); /* qdcount */
+       ldns_buffer_write_u16(pend->buffer, 0); /* ancount */
+       ldns_buffer_write_u16(pend->buffer, 0); /* nscount */
+       ldns_buffer_write_u16(pend->buffer, 0); /* arcount */
+       ldns_buffer_write(pend->buffer, qname, qnamelen);
+       ldns_buffer_write_u16(pend->buffer, qtype);
+       ldns_buffer_write_u16(pend->buffer, qclass);
+       ldns_buffer_flip(pend->buffer);
+       if(1) {
+               /* add edns */
+               struct edns_data edns;
+               edns.edns_present = 1;
+               edns.ext_rcode = 0;
+               edns.edns_version = EDNS_ADVERTISED_VERSION;
+               edns.udp_size = EDNS_ADVERTISED_SIZE;
+               edns.bits = 0;
+               if(dnssec)
+                       edns.bits = EDNS_DO;
+               attach_edns_record(pend->buffer, &edns);
+       }
+       memcpy(&pend->addr, addr, addrlen);
+       pend->addrlen = addrlen;
+       pend->callback = callback;
+       pend->cb_arg = callback_arg;
+       pend->timeout = UDP_QUERY_TIMEOUT;
+       pend->transport = transport_udp; /* pretend UDP */
+       pend->pkt = NULL;
+       pend->runtime = runtime;
+       status = ldns_buffer2pkt_wire(&pend->pkt, pend->buffer);
+       if(status != LDNS_STATUS_OK) {
+               log_err("ldns error parsing serviced output packet: %s",
+                       ldns_get_errorstr_by_id(status));
+               fatal_exit("internal error");
+       }
+       log_pkt("pending serviced query: ", pend->pkt);
+
+       /* see if it matches the current moment */
+       if(runtime->now && runtime->now->evt_type == repevt_back_query &&
+               find_match(runtime->now->match, pend->pkt, pend->transport)) {
+               log_info("testbound: matched pending to event. "
+                       "advance time between events.");
+               log_info("testbound: do STEP %d %s", runtime->now->time_step,
+                       repevt_string(runtime->now->evt_type));
+               advance_moment(runtime);
+               /* still create the pending, because we need it to callback */
+       } 
+       log_info("testbound: created fake pending");
+       /* add to list */
+       pend->next = runtime->pending_list;
+       runtime->pending_list = pend;
+       return (struct serviced_query*)pend;
+}
+
+void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
+{
+       struct fake_pending* pend = (struct fake_pending*)sq;
+       struct replay_runtime* runtime = pend->runtime;
+       /* delete from the list */
+       struct fake_pending* p = runtime->pending_list, *prev=NULL;
+       while(p) {
+               if(p == pend) {
+                       log_assert(p->cb_arg == cb_arg);
+                       if(prev)
+                               prev->next = p->next;
+                       else    runtime->pending_list = p->next;
+                       log_pkt("deleted pending serviced query.", p->pkt);
+                       ldns_buffer_free(p->buffer);
+                       ldns_pkt_free(p->pkt);
+                       free(p);
+                       return;
+               }
+               prev = p;
+               p = p->next;
+       }
+       log_pkt("double delet of pending serviced query", p->pkt);
+}
+
 struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg))
 {
        return calloc(1, 1);
index a873e5042e30dd76a1cedeeabf9758213eb256e2..9c101490a886f5463f58595a96932007a73b8026 100644 (file)
@@ -245,6 +245,8 @@ struct fake_pending {
        ldns_pkt* pkt;
        /** by what transport was the query sent out */
        enum transport_type transport;
+       /** the runtime structure this is part of */
+       struct replay_runtime* runtime;
 };
 
 /**
diff --git a/testdata/fwd_trunc.rpl b/testdata/fwd_trunc.rpl
deleted file mode 100644 (file)
index 4fa7b05..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-; This is a comment.
-; config options go here.
-CONFIG_END
-
-SCENARIO_BEGIN Query forwarded, receives truncated reply.
-
-STEP 1 QUERY
-ENTRY_BEGIN
-       REPLY RD
-       SECTION QUESTION
-       www.example.com. IN A
-ENTRY_END
-; the query is sent to the forwarder - UDP
-STEP 2 CHECK_OUT_QUERY
-ENTRY_BEGIN
-       MATCH qname qtype opcode UDP
-       SECTION QUESTION
-       www.example.com. IN A
-ENTRY_END
-; reply TC bit
-STEP 3 REPLY
-ENTRY_BEGIN
-       MATCH opcode qtype qname UDP
-       ADJUST copy_id
-       ; authoritative answer
-       REPLY QR AA RD RA TC NOERROR
-       SECTION QUESTION
-       www.example.com. IN A
-       SECTION ANSWER
-       www.example.com. IN A 10.20.30.40
-ENTRY_END
-; retry in TCP mode.
-STEP 4 CHECK_OUT_QUERY
-ENTRY_BEGIN
-       MATCH qname qtype opcode TCP
-       SECTION QUESTION
-       www.example.com. IN A
-ENTRY_END
-STEP 5 REPLY
-ENTRY_BEGIN
-       MATCH opcode qtype qname TCP
-       ADJUST copy_id
-       ; authoritative answer
-       REPLY QR AA RD RA NOERROR
-       SECTION QUESTION
-       www.example.com. IN A
-       SECTION ANSWER
-       www.example.com. IN A 10.20.30.40
-       SECTION AUTHORITY
-       www.example.com. IN NS ns.example.com.
-       SECTION ADDITIONAL
-       ns.example.com. IN A 10.20.30.50
-ENTRY_END
-STEP 6 CHECK_ANSWER
-ENTRY_BEGIN
-       MATCH all 
-       ; first reply, have AA set.
-       REPLY QR AA RD RA
-       SECTION QUESTION
-       www.example.com. IN A
-       SECTION ANSWER
-       www.example.com. IN A 10.20.30.40
-       SECTION AUTHORITY
-       www.example.com. IN NS ns.example.com.
-       SECTION ADDITIONAL
-       ns.example.com. IN A 10.20.30.50
-ENTRY_END
-
-SCENARIO_END
index ba2927459c838a53eeaaa88fecc75345e24daa85..b3cb8bca26cafd023782141043898960bba99c1e 100644 (file)
@@ -74,22 +74,48 @@ struct module_env {
 
        /* --- services --- */
        /** 
+        * Direct access to the network, this packet gets sent to destination.
         * 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.
+        * timeout or reply. Replied packet is then in the query buffer.
         * @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
+        * @return: false on failure (memory or socket related). no query was
         *      sent.
         */
-       int (*send_query)(ldns_buffer* pkt, struct sockaddr_storage* addr,
+       int (*send_packet)(ldns_buffer* pkt, struct sockaddr_storage* addr,
                socklen_t addrlen, int timeout, struct module_qstate* q,
                int use_tcp);
 
+       /** 
+        * Send serviced DNS query to server. UDP/TCP and EDNS is handled.
+        * operate() should return with wait_reply. Later on a callback 
+        * will cause operate() to be called with event timeout or reply.
+        * The time until a timeout is calculated from roundtrip timing,
+        * several UDP retries are attempted.
+        * @param qname: query name. (host order)
+        * @param qnamelen: length in bytes of qname, including trailing 0.
+        * @param qtype: query type. (host order)
+        * @param qclass: query class. (host order)
+        * @param flags: host order flags word, with opcode and CD bit.
+        * @param dnssec: if set, EDNS record will have DO bit set.
+        * @param addr: where to.
+        * @param addrlen: length of addr.
+        * @param q: wich query state to reactivate upon return.
+        * @return: false on failure (memory or socket related). no query was
+        *      sent. Or returns an outbound entry with qsent and qstate set.
+        *      This outbound_entry will be used on later module invocations
+        *      that involve this query (timeout, error or reply).
+        */
+       struct outbound_entry* (*send_query)(uint8_t* qname, size_t qnamelen, 
+               uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, 
+               struct sockaddr_storage* addr, socklen_t addrlen, 
+               struct module_qstate* q);
+
        /** create a subquery. operate should then return with wait_subq */
 
        /** allocation service */
@@ -210,6 +236,9 @@ struct module_func_block {
         * @param ev: event that causes the module state machine to 
         *      (re-)activate.
         * @param qstate: the query state. 
+        * @param id: module id number that operate() is called on. 
+        * @param outbound: if not NULL this event is due to the reply/timeout
+        *      or error on this outbound query.
         * @return: if at exit the ext_state is:
         *      o wait_module: next module is started. (with pass event).
         *      o error or finished: previous module is resumed.
@@ -218,7 +247,7 @@ struct module_func_block {
         *        have been called.
         */
        void (*operate)(struct module_qstate* qstate, enum module_ev event, 
-               int id);
+               int id, struct outbound_entry* outbound);
        /**
         * clear module specific data
         */