From f7f83b4a88b5168130a7bb9cb3d3811eb8c1c260 Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 16 Oct 1999 19:11:01 +0000 Subject: [PATCH] + * TCP handling revamped (avoids recursive-invocation problems). @@ -3,6 +3,7 @@ Bugfixes: + * TCP handling revamped (avoids recursive-invocation problems). --- changelog | 1 + src/check.c | 64 ++++++++++++---------- src/event.c | 141 +++++++++++++++++++++++++++++++++++-------------- src/internal.h | 127 ++++++++++++++++++++++++-------------------- src/query.c | 13 +++-- src/reply.c | 14 ++--- src/setup.c | 24 +++++---- src/transmit.c | 29 +++++----- 8 files changed, 256 insertions(+), 157 deletions(-) diff --git a/changelog b/changelog index b2dc7db..c86a8d9 100644 --- a/changelog +++ b/changelog @@ -3,6 +3,7 @@ adns (0.6) unstable; urgency=high Bugfixes: * Do not fail assertion if _qf_owner, _qf_search, domain ends in `.'. * Avoid infinite timeouts, causing lockup, when they should be zero ! + * TCP handling revamped (avoids recursive-invocation problems). * Dynamic library building works properly. * adnshost prints somewhat better messages about some wrong usages. * Include stdlib.h in adnshost.h. diff --git a/src/check.c b/src/check.c index 4d5e031..f0f1eec 100644 --- a/src/check.c +++ b/src/check.c @@ -63,12 +63,16 @@ static void checkc_query(adns_state ads, adns_query qu) { assert(qu->udpnextserver < ads->nservers); assert(!(qu->udpsent & (~0UL << ads->nservers))); - assert(!(qu->tcpfailed & (~0UL << ads->nservers))); - assert(qu->udpretries <= UDPMAXRETRIES); assert(qu->search_pos <= ads->nsearchlist); if (qu->parent) DLIST_ASSERTON(qu, child, qu->parent->children, siblings.); } +static void checkc_notcpbuf(adns_state ads) { + assert(!ads->tcpsend.used); + assert(!ads->tcprecv.used); + assert(!ads->tcprecv_skip); +} + static void checkc_global(adns_state ads) { int i; @@ -82,10 +86,12 @@ static void checkc_global(adns_state ads) { switch (ads->tcpstate) { case server_connecting: assert(ads->tcpsocket >= 0); - case server_disconnected: /* fall through */ - assert(!ads->tcpsend.used); - assert(!ads->tcprecv.used); - assert(!ads->tcprecv_skip); + checkc_notcpbuf(ads); + break; + case server_disconnected: + case server_broken: + assert(ads->tcpsocket == -1); + checkc_notcpbuf(ads); break; case server_ok: assert(ads->tcpsocket >= 0); @@ -98,34 +104,36 @@ static void checkc_global(adns_state ads) { assert(ads->searchlist || !ads->nsearchlist); } -static void checkc_queue_timew(adns_state ads) { +static void checkc_queue_udpw(adns_state ads) { adns_query qu; - DLIST_CHECK(ads->timew, qu, , { - switch (qu->state) { - case query_tosend: - assert(qu->udpsent); - assert(!qu->tcpfailed); - break; - case query_tcpwait: - assert(ads->tcpstate != server_ok); - break; - case query_tcpsent: - break; - default: - assert(!"timew state"); - } + DLIST_CHECK(ads->udpw, qu, , { + assert(qu->state==query_tosend); + assert(qu->retries <= UDPMAXRETRIES); + assert(qu->udpsent); assert(!qu->children.head && !qu->children.tail); checkc_query(ads,qu); checkc_query_alloc(ads,qu); }); } +static void checkc_queue_tcpw(adns_state ads) { + adns_query qu; + + DLIST_CHECK(ads->tcpw, qu, , { + assert(qu->state==query_tcpw); + assert(!qu->children.head && !qu->children.tail); + assert(qu->retries <= ads->nservers+1); + checkc_query(ads,qu); + checkc_query_alloc(ads,qu); + }); +} + static void checkc_queue_childw(adns_state ads) { adns_query parent, child; DLIST_CHECK(ads->childw, parent, , { - assert(parent->state == query_child); + assert(parent->state == query_childw); assert(parent->children.head); DLIST_CHECK(parent->children, child, siblings., { assert(child->parent == parent); @@ -165,18 +173,20 @@ void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc) { } checkc_global(ads); - checkc_queue_timew(ads); + checkc_queue_udpw(ads); + checkc_queue_tcpw(ads); checkc_queue_childw(ads); checkc_queue_output(ads); if (qu) { switch (qu->state) { case query_tosend: - case query_tcpwait: - case query_tcpsent: - DLIST_ASSERTON(qu, search, ads->timew, ); + DLIST_ASSERTON(qu, search, ads->udpw, ); + break; + case query_tcpw: + DLIST_ASSERTON(qu, search, ads->tcpw, ); break; - case query_child: + case query_childw: DLIST_ASSERTON(qu, search, ads->childw, ); break; case query_done: diff --git a/src/event.c b/src/event.c index e3f72a4..48970c8 100644 --- a/src/event.c +++ b/src/event.c @@ -42,37 +42,25 @@ /* TCP connection management. */ -void adns__tcp_closenext(adns_state ads) { +static void tcp_close(adns_state ads) { int serv; serv= ads->tcpserver; close(ads->tcpsocket); ads->tcpsocket= -1; - ads->tcpstate= server_disconnected; ads->tcprecv.used= ads->tcprecv_skip= ads->tcpsend.used= 0; - ads->tcpserver= (serv+1)%ads->nservers; } void adns__tcp_broken(adns_state ads, const char *what, const char *why) { int serv; - adns_query qu, nqu; assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok); serv= ads->tcpserver; - adns__warn(ads,serv,0,"TCP connection lost: %s: %s",what,why); - adns__tcp_closenext(ads); - - for (qu= ads->timew.head; qu; qu= nqu) { - nqu= qu->next; - if (qu->state == query_tosend) continue; - assert(qu->state == query_tcpwait || qu->state == query_tcpsent); - qu->state= query_tcpwait; - qu->tcpfailed |= (1<tcpfailed == (1<nservers)-1) { - LIST_UNLINK(ads->timew,qu); - adns__query_fail(qu,adns_s_allservfail); - } - } + if (what) adns__warn(ads,serv,0,"TCP connection failed: %s: %s",what,why); + + tcp_close(ads); + ads->tcpstate= server_broken; + ads->tcpserver= (serv+1)%ads->nservers; } static void tcp_connected(adns_state ads, struct timeval now) { @@ -80,11 +68,10 @@ static void tcp_connected(adns_state ads, struct timeval now) { adns__debug(ads,ads->tcpserver,0,"TCP connected"); ads->tcpstate= server_ok; - for (qu= ads->timew.head; qu; qu= nqu) { + for (qu= ads->tcpw.head; qu && ads->tcpstate == server_ok; qu= nqu) { nqu= qu->next; - if (qu->state == query_tosend) continue; - assert (qu->state == query_tcpwait); - adns__query_tcp(qu,now); + assert(qu->state == query_tcpw); + adns__querysend_tcp(qu,now); } } @@ -94,8 +81,17 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) { struct protoent *proto; for (tries=0; triesnservers; tries++) { - if (ads->tcpstate == server_connecting || ads->tcpstate == server_ok) return; - assert(ads->tcpstate == server_disconnected); + switch (ads->tcpstate) { + case server_connecting: + case server_ok: + case server_broken: + return; + case server_disconnected: + break; + default: + abort(); + } + assert(!ads->tcpsend.used); assert(!ads->tcprecv.used); assert(!ads->tcprecv_skip); @@ -120,9 +116,14 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) { r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr)); ads->tcpsocket= fd; ads->tcpstate= server_connecting; - if (r==0) { tcp_connected(ads,now); continue; } - if (errno == EWOULDBLOCK || errno == EINPROGRESS) return; + if (r==0) { tcp_connected(ads,now); return; } + if (errno == EWOULDBLOCK || errno == EINPROGRESS) { + ads->tcptimeout= now; + timevaladd(&ads->tcptimeout,TCPCONNMS); + return; + } adns__tcp_broken(ads,"connect",strerror(errno)); + ads->tcpstate= server_disconnected; } } @@ -158,6 +159,7 @@ static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf, static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf, struct timeval now, struct timeval maxtime) { + /* tv_io may be 0 */ ldiv_t dr; /*fprintf(stderr,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n", @@ -172,15 +174,14 @@ static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf, inter_maxto(tv_io,tvbuf,maxtime); } -void adns__timeouts(adns_state ads, int act, - struct timeval **tv_io, struct timeval *tvbuf, - struct timeval now) { +static void timeouts_queue(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now, struct query_queue *queue) { adns_query qu, nqu; - - for (qu= ads->timew.head; qu; qu= nqu) { + + for (qu= queue->head; qu; qu= nqu) { nqu= qu->next; if (!timercmp(&now,&qu->timeout,>)) { - if (!tv_io) continue; inter_maxtoabs(tv_io,tvbuf,now,qu->timeout); } else { if (!act) { @@ -189,16 +190,77 @@ void adns__timeouts(adns_state ads, int act, *tv_io= tvbuf; return; } - LIST_UNLINK(ads->timew,qu); + LIST_UNLINK(*queue,qu); if (qu->state != query_tosend) { adns__query_fail(qu,adns_s_timeout); } else { adns__query_send(qu,now); } - nqu= ads->timew.head; + nqu= queue->head; } } -} +} + +static void tcp_events(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now) { + adns_query qu, nqu; + + for (;;) { + switch (ads->tcpstate) { + case server_broken: + for (qu= ads->tcpw.head; qu; qu= nqu) { + nqu= qu->next; + assert(qu->state == query_tcpw); + if (qu->retries > ads->nservers) { + LIST_UNLINK(ads->tcpw,qu); + adns__query_fail(qu,adns_s_allservfail); + } + } + ads->tcpstate= server_disconnected; + case server_disconnected: /* fall through */ + if (!ads->tcpw.head) return; + adns__tcp_tryconnect(ads,now); + break; + case server_ok: + if (ads->tcpw.head) return; + if (!ads->tcptimeout.tv_sec) { + assert(!ads->tcptimeout.tv_usec); + ads->tcptimeout= now; + timevaladd(&ads->tcptimeout,TCPIDLEMS); + } + case server_connecting: /* fall through */ + if (!timercmp(&now,&ads->tcptimeout,>)) { + inter_maxtoabs(tv_io,tvbuf,now,ads->tcptimeout); + return; + } { + /* TCP timeout has happened */ + switch (ads->tcpstate) { + case server_connecting: /* failed to connect */ + adns__tcp_broken(ads,"unable to make connection","timed out"); + break; + case server_ok: /* idle timeout */ + tcp_close(ads); + ads->tcpstate= server_disconnected; + return; + default: + abort(); + } + } + break; + default: + abort(); + } + } +} + +void adns__timeouts(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now) { + timeouts_queue(ads,act,tv_io,tvbuf,now, &ads->udpw); + timeouts_queue(ads,act,tv_io,tvbuf,now, &ads->tcpw); + tcp_events(ads,act,tv_io,tvbuf,now); +} void adns_firsttimeout(adns_state ads, struct timeval **tv_io, struct timeval *tvbuf, @@ -514,14 +576,13 @@ xit: void adns_globalsystemfailure(adns_state ads) { adns__consistency(ads,0,cc_entex); - while (ads->timew.head) { - adns__query_fail(ads->timew.head, adns_s_systemfail); - } + while (ads->udpw.head) adns__query_fail(ads->udpw.head, adns_s_systemfail); + while (ads->tcpw.head) adns__query_fail(ads->tcpw.head, adns_s_systemfail); switch (ads->tcpstate) { case server_connecting: case server_ok: - adns__tcp_closenext(ads); + adns__tcp_broken(ads,0,0); break; case server_disconnected: break; @@ -572,7 +633,7 @@ int adns__internal_check(adns_state ads, if (!qu) { if (ads->output.head) { qu= ads->output.head; - } else if (ads->timew.head) { + } else if (ads->udpw.head || ads->tcpw.head) { return EAGAIN; } else { return ESRCH; diff --git a/src/internal.h b/src/internal.h index 2b604b7..02951a6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -11,20 +11,20 @@ * It is part of adns, which is * Copyright (C) 1997-1999 Ian Jackson * Copyright (C) 1999 Tony Finch - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ADNS_INTERNAL_H_INCLUDED @@ -51,7 +51,9 @@ typedef unsigned char byte; #define MAXSORTLIST 15 #define UDPMAXRETRIES 15 #define UDPRETRYMS 2000 -#define TCPMS 30000 +#define TCPWAITMS 30000 +#define TCPCONNMS 14000 +#define TCPIDLEMS 30000 #define MAXTTLBELIEVE (7*86400) /* any TTL > 7 days is capped */ #define DNS_PORT 53 @@ -164,7 +166,7 @@ typedef struct { struct adns__query { adns_state ads; - enum { query_tosend, query_tcpwait, query_tcpsent, query_child, query_done } state; + enum { query_tosend, query_tcpw, query_childw, query_done } state; adns_query back, next, parent; struct { adns_query head, tail; } children; struct { adns_query back, next; } siblings; @@ -175,7 +177,7 @@ struct adns__query { const typeinfo *typei; byte *query_dgram; int query_dglen; - + vbuf vb; /* General-purpose messing-about buffer. * Wherever a `big' interface is crossed, this may be corrupted/changed @@ -192,7 +194,7 @@ struct adns__query { * owner is set during querying unless we're doing searchlist, * in which case it is set only when we find an answer. */ - + byte *cname_dgram; int cname_dglen, cname_begin; /* If non-0, has been allocated using . */ @@ -207,10 +209,10 @@ struct adns__query { * but not done yet). If flags doesn't have adns_qf_search then * the vbuf is initialised but empty and everything else is zero. */ - - int id, flags, udpretries; + + int id, flags, retries; int udpnextserver; - unsigned long udpsent, tcpfailed; /* bitmap indexed by server */ + unsigned long udpsent; /* bitmap indexed by server */ struct timeval timeout; time_t expires; /* Earliest expiry time of any record we used. */ @@ -219,43 +221,41 @@ struct adns__query { /* Possible states: * * state Queue child id nextudpserver udpsent tcpfailed - * + * * tosend NONE null >=0 0 zero zero - * tosend timew null >=0 any nonzero zero + * tosend udpw null >=0 any nonzero zero * tosend NONE null >=0 any nonzero zero - * - * tcpwait timew null >=0 irrelevant any any - * tcpsent timew null >=0 irrelevant any any - * + * + * tcpw tcpw null >=0 irrelevant any any + * * child childw set >=0 irrelevant irrelevant irrelevant * child NONE null >=0 irrelevant irrelevant irrelevant * done output null -1 irrelevant irrelevant irrelevant * * Queries are only not on a queue when they are actually being processed. + * Queries in state tcpw/tcpw have been sent (or are in the to-send buffer) + * iff the tcp connection is in state server_ok. * * +------------------------+ - * START -----> | udp/NONE | + * START -----> | tosend/NONE | * +------------------------+ * / |\ \ * too big for UDP / UDP timeout \ \ send via UDP - * do this ASAP! / more retries \ \ do this ASAP! - * |_ desired \ _| - * +---------------+ +-----------+ - * | tcpwait/timew | ____ | udp/timew | - * +---------------+ \ +-----------+ - * | ^ | | | - * TCP conn'd; | | TCP died | | | - * send via TCP | | more | UDP timeout | | - * do this ASAP! | | servers | no more | | - * v | to try | retries | | - * +---------------+ | desired | | - * | tcpsent/timew | ____ | | | - * +---------------+ \| | | - * \ \ TCP died | TCP | | - * \ \ no more | timeout / | - * \ \ servers | / | - * \ \ to try | / | - * got \ \ v |_ / got + * send via TCP / more retries \ \ + * when conn'd / desired \ \ + * | | | + * v | v + * +-----------+ +-------------+ + * | tcpw/tcpw | ________ | tosend/udpw | + * +-----------+ \ +-------------+ + * | | | UDP timeout | | + * | | | no more | | + * | | | retries | | + * \ | TCP died | desired | | + * \ \ no more | | | + * \ \ servers | TCP / | + * \ \ to try | timeout / | + * got \ \ v |_ | got * reply \ _| +------------------+ / reply * \ | done/output FAIL | / * \ +------------------+ / @@ -266,23 +266,33 @@ struct adns__query { * need child query/ies / \ no child query * / \ * |_ _| - * +--------------+ +----------------+ - * | child/childw | ----------------> | done/output OK | - * +--------------+ children done +----------------+ + * +---------------+ +----------------+ + * | childw/childw | ----------------> | done/output OK | + * +---------------+ children done +----------------+ */ }; +struct query_queue { adns_query head, tail; }; + struct adns__state { adns_initflags iflags; FILE *diagfile; int configerrno; - struct { adns_query head, tail; } timew, childw, output; + struct query_queue udpw, tcpw, childw, output; adns_query forallnext; int nextid, udpsocket, tcpsocket; vbuf tcpsend, tcprecv; int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip; - enum adns__tcpstate { server_disconnected, server_connecting, server_ok } tcpstate; + enum adns__tcpstate { + server_disconnected, server_connecting, + server_ok, server_broken + } tcpstate; struct timeval tcptimeout; + /* This will have tv_sec==0 if it is not valid. + * It will always be valid if tcpstate _connecting. + * When _ok, it will be nonzero if we are idle + * (ie, tcpw queue is empty) and counting down. + */ struct sigaction stdsigpipe; sigset_t stdsigmask; struct pollfd pollfds_buf[MAX_POLLFDS]; @@ -331,7 +341,7 @@ const char *adns__diag_domain(adns_state ads, int serv, adns_query qu, * Returns either vb->buf, or a pointer to a string literal. Do not modify * vb before using the return value. */ - + void adns__isort(void *array, int nobjs, int sz, void *tempbuf, int (*needswap)(void *context, const void *a, const void *b), void *context); @@ -365,16 +375,10 @@ adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, * That domain must be correct and untruncated. */ -void adns__query_tcp(adns_query qu, struct timeval now); -/* Query must be in state tcpwait/timew; it will be moved to a new state - * if possible and no further processing can be done on it for now. - * (Resulting state is one of tcpwait/timew (if server not connected), - * tcpsent/timew, child/childw or done/output.) - * - * adns__tcp_tryconnect should already have been called - _tcp - * will only use an existing connection (if there is one), which it - * may break. If the conn is lost then the caller is responsible for any - * reestablishment and retry. +void adns__querysend_tcp(adns_query qu, struct timeval now); +/* Query must be in state tcpw/tcpw; it will be sent if possible and + * no further processing can be done on it for now. The connection + * might be broken, but no reconnect will be attempted. */ void adns__query_send(adns_query qu, struct timeval now); @@ -488,7 +492,7 @@ void adns__reset_preserved(adns_query qu); void adns__query_done(adns_query qu); void adns__query_fail(adns_query qu, adns_status stat); - + /* From reply.c: */ void adns__procdgram(adns_state ads, const byte *dgram, int len, @@ -497,6 +501,9 @@ void adns__procdgram(adns_state ads, const byte *dgram, int len, * and sent, or even new queries to be started. However, * query-sending functions are not allowed to call any general event * loop functions in case they accidentally call this. + * + * Ie, receiving functions may call sending functions. + * Sending functions may NOT call receiving functions. */ /* From types.c: */ @@ -633,7 +640,8 @@ int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len); /* From event.c: */ void adns__tcp_broken(adns_state ads, const char *what, const char *why); -void adns__tcp_closenext(adns_state ads); +/* what and why may be both 0, or both non-0. */ + void adns__tcp_tryconnect(adns_state ads, struct timeval now); void adns__autosys(adns_state ads, struct timeval now); @@ -644,9 +652,7 @@ void adns__autosys(adns_state ads, struct timeval now); void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io, struct timeval *tv_buf); -void adns__timeouts(adns_state ads, int act, - struct timeval **tv_io, struct timeval *tvbuf, - struct timeval now); + int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]); void adns__fdevents(adns_state ads, const struct pollfd *pollfds, int npollfds, @@ -658,6 +664,13 @@ int adns__internal_check(adns_state ads, adns_answer **answer, void **context_r); +void adns__timeouts(adns_state ads, int act, + struct timeval **tv_io, struct timeval *tvbuf, + struct timeval now); +/* If act is !0, then this will also deal with the TCP connection + * if previous events broke it or require it to be connected. + */ + /* From check.c: */ void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc); diff --git a/src/query.c b/src/query.c index 7eb322d..f805c92 100644 --- a/src/query.c +++ b/src/query.c @@ -68,9 +68,9 @@ static adns_query query_alloc(adns_state ads, const typeinfo *typei, qu->id= 0; qu->flags= flags; - qu->udpretries= 0; + qu->retries= 0; qu->udpnextserver= 0; - qu->udpsent= qu->tcpfailed= 0; + qu->udpsent= 0; timerclear(&qu->timeout); qu->expires= now.tv_sec + MAXTTLBELIEVE; @@ -399,10 +399,13 @@ void adns_cancel(adns_query qu) { adns__consistency(ads,qu,cc_entex); if (qu->parent) LIST_UNLINK_PART(qu->parent->children,qu,siblings.); switch (qu->state) { - case query_tosend: case query_tcpwait: case query_tcpsent: - LIST_UNLINK(ads->timew,qu); + case query_tosend: + LIST_UNLINK(ads->udpw,qu); break; - case query_child: + case query_tcpw: + LIST_UNLINK(ads->tcpw,qu); + break; + case query_childw: LIST_UNLINK(ads->childw,qu); break; case query_done: diff --git a/src/reply.c b/src/reply.c index 4b3d74f..246d52d 100644 --- a/src/reply.c +++ b/src/reply.c @@ -85,7 +85,7 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen, qdcount); return; } - for (qu= ads->timew.head; qu; qu= nqu) { + for (qu= viatcp ? ads->tcpw.head : ads->udpw.head; qu; qu= nqu) { nqu= qu->next; if (qu->id != id) continue; if (dglen < qu->query_dglen) continue; @@ -94,9 +94,9 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen, qu->query_dglen-DNS_HDRSIZE)) continue; if (viatcp) { - if (qu->state != query_tcpsent) continue; + assert(qu->state == query_tcpw); } else { - if (qu->state != query_tosend) continue; + assert(qu->state == query_tosend); if (!(qu->udpsent & (1<query_dglen; arstart= -1; - LIST_UNLINK(ads->timew,qu); + if (viatcp) LIST_UNLINK(ads->tcpw,qu); + else LIST_UNLINK(ads->udpw,qu); /* We're definitely going to do something with this query now */ switch (rcode) { @@ -318,7 +319,7 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen, /* This may have generated some child queries ... */ if (qu->children.head) { - qu->state= query_child; + qu->state= query_childw; LIST_LINK_TAIL(ads->childw,qu); return; } @@ -349,7 +350,8 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen, memcpy(newquery,qu->vb.buf,qu->vb.used); } - if (qu->state == query_tcpsent) qu->state= query_tosend; + if (qu->state == query_tcpw) qu->state= query_tosend; + qu->retries= 0; adns__reset_preserved(qu); adns__query_send(qu,now); } diff --git a/src/setup.c b/src/setup.c index e051904..ba80f14 100644 --- a/src/setup.c +++ b/src/setup.c @@ -461,7 +461,8 @@ static int init_begin(adns_state *ads_r, adns_initflags flags, FILE *diagfile) { ads->iflags= flags; ads->diagfile= diagfile; ads->configerrno= 0; - LIST_INIT(ads->timew); + LIST_INIT(ads->udpw); + LIST_INIT(ads->tcpw); LIST_INIT(ads->childw); LIST_INIT(ads->output); ads->forallnext= 0; @@ -580,7 +581,8 @@ int adns_init_strcfg(adns_state *ads_r, int flags, void adns_finish(adns_state ads) { adns__consistency(ads,0,cc_entex); for (;;) { - if (ads->timew.head) adns_cancel(ads->timew.head); + if (ads->udpw.head) adns_cancel(ads->udpw.head); + else if (ads->tcpw.head) adns_cancel(ads->tcpw.head); else if (ads->childw.head) adns_cancel(ads->childw.head); else if (ads->output.head) adns_cancel(ads->output.head); else break; @@ -595,7 +597,8 @@ void adns_finish(adns_state ads) { void adns_forallqueries_begin(adns_state ads) { adns__consistency(ads,0,cc_entex); ads->forallnext= - ads->timew.head ? ads->timew.head : + ads->udpw.head ? ads->udpw.head : + ads->tcpw.head ? ads->tcpw.head : ads->childw.head ? ads->childw.head : ads->output.head; } @@ -610,12 +613,15 @@ adns_query adns_forallqueries_next(adns_state ads, void **context_r) { if (!qu) return 0; if (qu->next) { nqu= qu->next; - } else if (qu == ads->timew.tail) { - if (ads->childw.head) { - nqu= ads->childw.head; - } else { - nqu= ads->output.head; - } + } else if (qu == ads->udpw.tail) { + nqu= + ads->tcpw.head ? ads->tcpw.head : + ads->childw.head ? ads->childw.head : + ads->output.head; + } else if (qu == ads->tcpw.tail) { + nqu= + ads->childw.head ? ads->childw.head : + ads->output.head; } else if (qu == ads->childw.tail) { nqu= ads->output.head; } else { diff --git a/src/transmit.c b/src/transmit.c index 4590a16..edb79c3 100644 --- a/src/transmit.c +++ b/src/transmit.c @@ -157,7 +157,7 @@ adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r, return adns_s_ok; } -void adns__query_tcp(adns_query qu, struct timeval now) { +void adns__querysend_tcp(adns_query qu, struct timeval now) { byte length[2]; struct iovec iov[2]; int wr, r; @@ -165,15 +165,18 @@ void adns__query_tcp(adns_query qu, struct timeval now) { if (qu->ads->tcpstate != server_ok) return; + assert(qu->state == query_tcpw); + length[0]= (qu->query_dglen&0x0ff00U) >>8; length[1]= (qu->query_dglen&0x0ff); ads= qu->ads; if (!adns__vbuf_ensure(&ads->tcpsend,ads->tcpsend.used+qu->query_dglen+2)) return; - timevaladd(&now,TCPMS); - qu->timeout= now; - qu->state= query_tcpsent; + qu->retries++; + + /* Reset idle timeout. */ + ads->tcptimeout.tv_sec= ads->tcptimeout.tv_usec= 0; if (ads->tcpsend.used) { wr= 0; @@ -207,11 +210,11 @@ void adns__query_tcp(adns_query qu, struct timeval now) { } static void query_usetcp(adns_query qu, struct timeval now) { - timevaladd(&now,TCPMS); + qu->state= query_tcpw; qu->timeout= now; - qu->state= query_tcpwait; - LIST_LINK_TAIL(qu->ads->timew,qu); - adns__query_tcp(qu,now); + timevaladd(&qu->timeout,TCPWAITMS); + LIST_LINK_TAIL(qu->ads->tcpw,qu); + adns__querysend_tcp(qu,now); adns__tcp_tryconnect(qu->ads,now); } @@ -226,7 +229,7 @@ void adns__query_send(adns_query qu, struct timeval now) { return; } - if (qu->udpretries >= UDPMAXRETRIES) { + if (qu->retries >= UDPMAXRETRIES) { adns__query_fail(qu,adns_s_timeout); return; } @@ -241,13 +244,13 @@ void adns__query_send(adns_query qu, struct timeval now) { r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0, (const struct sockaddr*)&servaddr,sizeof(servaddr)); - if (r<0 && errno == EMSGSIZE) { query_usetcp(qu,now); return; } + if (r<0 && errno == EMSGSIZE) { qu->retries= 0; query_usetcp(qu,now); return; } if (r<0) adns__warn(ads,serv,0,"sendto failed: %s",strerror(errno)); - timevaladd(&now,UDPRETRYMS); qu->timeout= now; + timevaladd(&qu->timeout,UDPRETRYMS); qu->udpsent |= (1<udpnextserver= (serv+1)%ads->nservers; - qu->udpretries++; - LIST_LINK_TAIL(ads->timew,qu); + qu->retries++; + LIST_LINK_TAIL(ads->udpw,qu); } -- 2.30.2