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);
#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"
/** 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) {
}
/* 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
*/
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;
}
|| 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;
}
/* 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;
}
}
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;
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;
+}
* @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 */
+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,
#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"
/**
&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");
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;
}
#ifndef ITERATOR_ITERATOR_H
#define ITERATOR_ITERATOR_H
+#include "services/outbound_list.h"
struct module_func_block;
/**
* Per query state for the iterator module.
*/
struct iter_qstate {
+ /** list of pending queries to authoritative servers. */
+ struct outbound_list outlist;
};
/**
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
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;
edns.bits = EDNS_DO;
attach_edns_record(buff, &edns);
}
- ldns_buffer_flip(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;
}
}
/** 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 */
}
(void)rbtree_delete(sq->outnet->serviced, sq);
- serviced_callbacks(sq, error, c);
+ serviced_callbacks(sq, error, c, rep);
serviced_delete(sq);
return 0;
}
* 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;
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;
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;
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;
}
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 */
}
}
#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"
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",
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",
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);
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;
};
/**
+++ /dev/null
-; 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
/* --- 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 */
* @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.
* 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
*/