outbound queries via serviced outside_network queries.

git-svn-id: file:///svn/unbound/trunk@327 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-05-22 12:36:02 +00:00
parent 1a90ff7b67
commit b461dc4111
13 changed files with 501 additions and 150 deletions

View 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);

View file

@ -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;
}

View 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 */

View 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,

View 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,61 +163,48 @@ 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];
verbose(VERB_ALGO, "iterator[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
if(event == module_event_error) {
qstate->ext_state[id] = module_error;
return;
}
if(event == module_event_new) {
/* send UDP query to forwarder address */
(*env->send_query)(qstate->buf, &ie->fwd_addr,
ie->fwd_addrlen, UDP_QUERY_TIMEOUT, qstate, 0);
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;
qstate->minfo[id] = NULL;
return;
return 1;
}
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;
}
*/
qstate->ext_state[id] = module_error;
return;
}
if(event == module_event_reply) {
/** 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;
/* see if it is truncated */
if(LDNS_TC_WIRE(ldns_buffer_begin(qstate->reply->c->buffer))
&& qstate->reply->c->type == comm_udp) {
log_info("TC: truncated. retry in TCP mode.");
qinfo_query_encode(qstate->buf, &qstate->qinfo);
(*env->send_query)(qstate->buf, &ie->fwd_addr,
ie->fwd_addrlen, TCP_QUERY_TIMEOUT, qstate, 1);
/* stay in wait_reply state */
return;
}
if((r=reply_info_parse(qstate->reply->c->buffer, env->alloc,
&reply_qinfo, &reply_msg, qstate->scratch,
&reply_edns))!=0) {
qstate->ext_state[id] = module_error;
return;
}
&reply_edns))!=0)
return 0;
qstate->edns.edns_version = EDNS_ADVERTISED_VERSION;
qstate->edns.udp_size = EDNS_ADVERTISED_SIZE;
@ -224,12 +212,38 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id)
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->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_new) {
if(!iter_new(qstate, id))
qstate->ext_state[id] = module_error;
return;
}
store_msg(qstate, &reply_qinfo, reply_msg);
qstate->ext_state[id] = module_finished;
/* 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 || event == module_event_error) {
qstate->ext_state[id] = module_error;
return;
}
if(event == module_event_reply) {
if(!iter_handlereply(qstate, id, outbound))
qstate->ext_state[id] = module_error;
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;
}

View 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;
};
/**

86
services/outbound_list.c Normal file
View file

@ -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);
}

105
services/outbound_list.h Normal file
View file

@ -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 */

View 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 */
}
}

View 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);

View 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;
};
/**

View file

@ -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

View 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
*/