From d0908148d610c4f0af817f507617fafb79c23a4f Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Wed, 9 May 2007 07:00:10 +0000 Subject: [PATCH] TCP fallback if forwarder sends TC bit. git-svn-id: file:///svn/unbound/trunk@295 be551aaa-1e26-0410-a405-d3ace91eadb9 --- daemon/worker.c | 7 ++++++ doc/Changelog | 4 ++++ services/outside_network.c | 37 ++++++++++++++++++++++++------ testcode/fake_event.c | 47 ++++++++++++++++++++++++++++++++++++++ util/data/msgreply.c | 18 +++++++++++++++ util/data/msgreply.h | 7 ++++++ util/netevent.c | 14 +++++++++++- util/netevent.h | 5 ++-- 8 files changed, 129 insertions(+), 10 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index 4e78104f4..9fbd1e5bd 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -63,6 +63,8 @@ #define DNS_ID_AND_FLAGS 4 /** timeout in seconds for UDP queries to auth servers. TODO: proper rtt */ #define UDP_QUERY_TIMEOUT 4 +/** timeout in seconds for TCP queries to auth servers. TODO: proper rtt */ +#define TCP_QUERY_TIMEOUT 30 /** Advertised version of EDNS capabilities */ #define EDNS_ADVERTISED_VERSION 0 /** Advertised size of EDNS capabilities */ @@ -237,6 +239,11 @@ worker_handle_reply(struct comm_point* c, void* arg, int error, /* see if it is truncated */ if(LDNS_TC_WIRE(ldns_buffer_begin(c->buffer)) && c->type == comm_udp) { log_info("TC: truncated. retry in TCP mode."); + qinfo_query_encode(w->worker->back->udp_buff, &w->qinfo); + pending_tcp_query(w->worker->back, w->worker->back->udp_buff, + &w->worker->fwd_addr, w->worker->fwd_addrlen, + TCP_QUERY_TIMEOUT, worker_handle_reply, w, + w->worker->rndstate); return 0; } /* woohoo a reply! */ diff --git a/doc/Changelog b/doc/Changelog index 7acb5cf8f..8b914e8a9 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +9 May 2007: Wouter + - outside network cleans up waiting tcp queries on exit. + - fallback to TCP. + 8 May 2007: Wouter - outgoing network keeps list of available tcp buffers for outgoing tcp queries. diff --git a/services/outside_network.c b/services/outside_network.c index c2a738891..bd39b7daa 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -109,6 +109,18 @@ pending_cmp(const void* key1, const void* key2) } } +/** delete waiting_tcp entry. Does not unlink from waiting list. + * @param w: to delete. + */ +static void +waiting_tcp_delete(struct waiting_tcp* w) +{ + if(!w) return; + if(w->timer) + comm_timer_delete(w->timer); + free(w); +} + /** use next free buffer to service a tcp query */ static void outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt) @@ -128,7 +140,7 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt) log_err("outgoing tcp: socket: %s", strerror(errno)); log_addr(&w->addr, w->addrlen); (void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL); - free(w); + waiting_tcp_delete(w); return; } fd_set_nonblock(s); @@ -138,13 +150,13 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt) log_addr(&w->addr, w->addrlen); close(s); (void)(*w->cb)(NULL, w->cb_arg, NETEVENT_CLOSED, NULL); - free(w); + waiting_tcp_delete(w); return; } } w->pkt = NULL; w->next_waiting = (void*)pend; - memmove(&pend->id, pkt, sizeof(uint16_t)); + pend->id = LDNS_ID_WIRE(pkt); w->outnet->tcp_free = pend->next_free; pend->next_free = NULL; pend->query = w; @@ -186,7 +198,7 @@ outnet_tcp_cb(struct comm_point* c, void* arg, int error, /* check ID */ if(ldns_buffer_limit(c->buffer) < sizeof(uint16_t) || LDNS_ID_WIRE(ldns_buffer_begin(c->buffer))!=pend->id) { - log_info("outnettcp: bad ID in reply"); + log_info("outnettcp: bad ID in reply, from:"); log_addr(&pend->query->addr, pend->query->addrlen); error = NETEVENT_CLOSED; } @@ -195,7 +207,7 @@ outnet_tcp_cb(struct comm_point* c, void* arg, int error, comm_point_close(c); pend->next_free = outnet->tcp_free; outnet->tcp_free = pend; - free(pend->query); + waiting_tcp_delete(pend->query); pend->query = NULL; use_free_buffer(outnet); return 0; @@ -380,7 +392,8 @@ create_pending_tcp(struct outside_network* outnet, size_t bufsize) outnet->tcp_conns[i]->next_free = outnet->tcp_free; outnet->tcp_free = outnet->tcp_conns[i]; outnet->tcp_conns[i]->c = comm_point_create_tcp_out( - bufsize, outnet_tcp_cb, outnet->tcp_conns[i]); + outnet->base, bufsize, outnet_tcp_cb, + outnet->tcp_conns[i]); if(!outnet->tcp_conns[i]->c) return 0; } @@ -499,10 +512,20 @@ outside_network_delete(struct outside_network* outnet) for(i=0; inum_tcp; i++) if(outnet->tcp_conns[i]) { comm_point_delete(outnet->tcp_conns[i]->c); + waiting_tcp_delete(outnet->tcp_conns[i]->query); free(outnet->tcp_conns[i]); } free(outnet->tcp_conns); } + if(outnet->tcp_wait_first) { + struct waiting_tcp* p = outnet->tcp_wait_first, *np; + while(p) { + np = p->next_waiting; + waiting_tcp_delete(p); + p = np; + } + } + free(outnet); } @@ -687,7 +710,7 @@ outnet_tcptimer(void* arg) outnet->tcp_free = pend; } (void)(*w->cb)(NULL, w->cb_arg, NETEVENT_TIMEOUT, NULL); - free(w); + waiting_tcp_delete(w); use_free_buffer(outnet); } diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 9368bb978..97f8f00a3 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -703,6 +703,53 @@ pending_udp_query(struct outside_network* outnet, ldns_buffer* packet, runtime->pending_list = pend; } +void +pending_tcp_query(struct outside_network* outnet, ldns_buffer* packet, + struct sockaddr_storage* addr, socklen_t addrlen, int timeout, + comm_point_callback_t* callback, void* callback_arg, + struct ub_randstate* ATTR_UNUSED(rnd)) +{ + 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); + pend->buffer = ldns_buffer_new(ldns_buffer_capacity(packet)); + log_assert(pend->buffer); + ldns_buffer_write(pend->buffer, ldns_buffer_begin(packet), + ldns_buffer_limit(packet)); + ldns_buffer_flip(pend->buffer); + memcpy(&pend->addr, addr, addrlen); + pend->addrlen = addrlen; + pend->callback = callback; + pend->cb_arg = callback_arg; + pend->timeout = timeout; + pend->transport = transport_tcp; + pend->pkt = NULL; + status = ldns_buffer2pkt_wire(&pend->pkt, packet); + if(status != LDNS_STATUS_OK) { + log_err("ldns error parsing tcp output packet: %s", + ldns_get_errorstr_by_id(status)); + fatal_exit("Sending unparseable DNS packets to servers!"); + } + log_pkt("pending tcp pkt: ", 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; +} + struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg)) { return calloc(1, 1); diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 7c09f89ab..a93f4518d 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -1145,3 +1145,21 @@ query_info_entrysetup(struct query_info* q, struct reply_info* r, q->qname = NULL; return e; } + +void +qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo) +{ + uint16_t flags = 0; /* QUERY, NOERROR */ + if(qinfo->has_cd) + flags |= BIT_CD; + ldns_buffer_clear(pkt); + log_assert(ldns_buffer_remaining(pkt) >= 12+255+4/*max query*/); + ldns_buffer_skip(pkt, 2); /* id done later */ + ldns_buffer_write_u16(pkt, flags); + ldns_buffer_write_u16(pkt, 1); /* query count */ + ldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */ + ldns_buffer_write(pkt, qinfo->qname, qinfo->qnamesize); + ldns_buffer_write_u16(pkt, qinfo->qtype); + ldns_buffer_write_u16(pkt, qinfo->qclass); + ldns_buffer_flip(pkt); +} diff --git a/util/data/msgreply.h b/util/data/msgreply.h index d8e6c94de..4e712d661 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -288,6 +288,13 @@ int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, ldns_buffer* buffer, uint32_t timenow, struct region* region, uint16_t udpsize); +/** + * Encode query packet. Assumes the buffer is large enough. + * @param pkt: where to store the packet. + * @param qinfo: query info. + */ +void qinfo_query_encode(ldns_buffer* pkt, struct query_info* qinfo); + /** * Setup query info entry * @param q: query info to copy. Emptied as if clear is called. diff --git a/util/netevent.c b/util/netevent.c index 184998658..befa2b759 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -712,11 +712,12 @@ comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize, } struct comm_point* -comm_point_create_tcp_out(size_t bufsize, +comm_point_create_tcp_out(struct comm_base *base, size_t bufsize, comm_point_callback_t* callback, void* callback_arg) { struct comm_point* c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); + short evbits; if(!c) return NULL; c->ev = (struct internal_event*)calloc(1, @@ -746,6 +747,17 @@ comm_point_create_tcp_out(size_t bufsize, c->tcp_check_nb_connect = 1; c->callback = callback; c->cb_arg = callback_arg; + evbits = EV_PERSIST | EV_WRITE; + event_set(&c->ev->ev, c->fd, evbits, comm_point_tcp_handle_callback, c); + if(event_base_set(base->eb->base, &c->ev->ev) != 0) + { + log_err("could not basetset tcpout event"); + ldns_buffer_free(c->buffer); + free(c->ev); + free(c); + return NULL; + } + return c; } diff --git a/util/netevent.h b/util/netevent.h index 4e031d7cb..83b5d5297 100644 --- a/util/netevent.h +++ b/util/netevent.h @@ -293,13 +293,14 @@ struct comm_point* comm_point_create_tcp(struct comm_base* base, /** * Create an outgoing TCP commpoint. No file descriptor is opened, left at -1. + * @param base: in which base to alloc the commpoint. * @param bufsize: size of buffer to create for handlers. * @param callback: callback function pointer for the handler. * @param callback_arg: will be passed to your callback function. * @return: the commpoint or NULL on error. */ -struct comm_point* comm_point_create_tcp_out(size_t bufsize, - comm_point_callback_t* callback, void* callback_arg); +struct comm_point* comm_point_create_tcp_out(struct comm_base* base, + size_t bufsize, comm_point_callback_t* callback, void* callback_arg); /** * Create commpoint to listen to a local domain file descriptor. -- 2.47.2