From: Wouter Wijngaards Date: Tue, 22 May 2007 12:36:02 +0000 (+0000) Subject: outbound queries via serviced outside_network queries. X-Git-Tag: release-0.4~139 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b461dc41111f7c7328c7a9facb46417688868c90;p=thirdparty%2Funbound.git outbound queries via serviced outside_network queries. git-svn-id: file:///svn/unbound/trunk@327 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/daemon/daemon.c b/daemon/daemon.c index b3670e745..26b42a53f 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -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; inum_modules; i++) { log_info("init module %d: %s", i, daemon->modfunc[i]->name); diff --git a/daemon/worker.c b/daemon/worker.c index 57dd3ae07..f3d51d1e3 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -47,10 +47,12 @@ #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; +} diff --git a/daemon/worker.h b/daemon/worker.h index 0d21b3e7c..557c12319 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -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 */ diff --git a/doc/Changelog b/doc/Changelog index 66e5c0477..3ba2be7de 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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, diff --git a/iterator/iterator.c b/iterator/iterator.c index fb62cea9d..07c16e37b 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -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; } diff --git a/iterator/iterator.h b/iterator/iterator.h index 0fc8c26fd..5a075e663 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -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 index 000000000..789877a6c --- /dev/null +++ b/services/outbound_list.c @@ -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 index 000000000..5631910f6 --- /dev/null +++ b/services/outbound_list.h @@ -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 */ diff --git a/services/outside_network.c b/services/outside_network.c index eb9e15bb8..b515af281 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -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 */ } } diff --git a/testcode/fake_event.c b/testcode/fake_event.c index f4a3a7326..0255c9df3 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -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); diff --git a/testcode/replay.h b/testcode/replay.h index a873e5042..9c101490a 100644 --- a/testcode/replay.h +++ b/testcode/replay.h @@ -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 index 4fa7b0538..000000000 --- a/testdata/fwd_trunc.rpl +++ /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 diff --git a/util/module.h b/util/module.h index ba2927459..b3cb8bca2 100644 --- a/util/module.h +++ b/util/module.h @@ -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 */