mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
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:
parent
1a90ff7b67
commit
b461dc4111
13 changed files with 501 additions and 150 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
if(event == module_event_new) {
|
||||
if(!iter_new(qstate, id))
|
||||
qstate->ext_state[id] = module_error;
|
||||
return;
|
||||
}
|
||||
/* 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_new) {
|
||||
/* send UDP query to forwarder address */
|
||||
(*env->send_query)(qstate->buf, &ie->fwd_addr,
|
||||
ie->fwd_addrlen, UDP_QUERY_TIMEOUT, qstate, 0);
|
||||
qstate->ext_state[id] = module_wait_reply;
|
||||
qstate->minfo[id] = NULL;
|
||||
return;
|
||||
}
|
||||
if(event == module_event_timeout) {
|
||||
/* try TCP if UDP fails */
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
86
services/outbound_list.c
Normal 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
105
services/outbound_list.h
Normal 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 */
|
||||
|
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
69
testdata/fwd_trunc.rpl
vendored
69
testdata/fwd_trunc.rpl
vendored
|
|
@ -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
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue