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.
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;
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);
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);
}
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:
/* 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<<serv);
- if (qu->tcpfailed == (1<<ads->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) {
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);
}
}
struct protoent *proto;
for (tries=0; tries<ads->nservers; 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);
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;
}
}
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",
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) {
*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,
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;
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;
* It is part of adns, which is
* Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
* Copyright (C) 1999 Tony Finch <dot@dotat.at>
- *
+ *
* 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
#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
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;
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
* 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 . */
* 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. */
/* 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 | /
* \ +------------------+ /
* 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];
* 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);
* 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);
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,
* 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: */
/* 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);
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,
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);
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;
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:
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;
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<<serv))) continue;
}
break;
anstart= qu->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) {
/* 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;
}
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);
}
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;
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;
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;
}
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 {
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;
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;
}
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);
}
return;
}
- if (qu->udpretries >= UDPMAXRETRIES) {
+ if (qu->retries >= UDPMAXRETRIES) {
adns__query_fail(qu,adns_s_timeout);
return;
}
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<<serv);
qu->udpnextserver= (serv+1)%ads->nservers;
- qu->udpretries++;
- LIST_LINK_TAIL(ads->timew,qu);
+ qu->retries++;
+ LIST_LINK_TAIL(ads->udpw,qu);
}