chiark / gitweb /
+ * TCP handling revamped (avoids recursive-invocation problems).
authorian <ian>
Sat, 16 Oct 1999 19:11:01 +0000 (19:11 +0000)
committerian <ian>
Sat, 16 Oct 1999 19:11:01 +0000 (19:11 +0000)
@@ -3,6 +3,7 @@
   Bugfixes:
+  * TCP handling revamped (avoids recursive-invocation problems).

changelog
src/check.c
src/event.c
src/internal.h
src/query.c
src/reply.c
src/setup.c
src/transmit.c

index b2dc7db06a74ef5d696cc006eca5b0ac7289291d..c86a8d997af1d1c0c96fade90e4a25563f89e441 100644 (file)
--- 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.
index 4d5e031f702acbdfafee00fcfaea8cd30caf1f28..f0f1eeca35499059970c4d786770708c51319562 100644 (file)
@@ -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:
index e3f72a409c731e37b192007f60afe2cc868a29b4..48970c8b89fae9aa44bb62b6c0128c2a5c47fd54 100644 (file)
 
 /* 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) {
@@ -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; 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);
@@ -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;
index 2b604b7a5801ab8f7c363bfe4d468ccf58b5c11d..02951a625ea92bdffe380b61d2616dde893562e2 100644 (file)
  *  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
@@ -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);
index 7eb322d996ba87e85e61815175a087672f0128c3..f805c9296565fe8b10920c15d144e31cd34457a9 100644 (file)
@@ -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:
index 4b3d74fa6b5ad6427f3f064da00c0ee0ceb5220f..246d52d44fe0a796018b0b15666be9710909fbe6 100644 (file)
@@ -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<<serv))) continue;
     }
     break;
@@ -113,7 +113,8 @@ void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
   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) {
@@ -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);
 }
index e051904f89ec52ade05af32f851609b6b245b5ab..ba80f14a66a1dceda08929efc80f795eeb0e1982 100644 (file)
@@ -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 {
index 4590a16c7f74c95eb3b0dce8b84aeb827c628eaf..edb79c305845ce5a63fe83d4a9c6ab0433dbe2ac 100644 (file)
@@ -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<<serv);
   qu->udpnextserver= (serv+1)%ads->nservers;
-  qu->udpretries++;
-  LIST_LINK_TAIL(ads->timew,qu);
+  qu->retries++;
+  LIST_LINK_TAIL(ads->udpw,qu);
 }