Index: ares.h
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares.h,v
retrieving revision 1.24
diff -u -r1.24 ares.h
--- ares.h	5 Jul 2006 23:10:38 -0000	1.24
+++ ares.h	1 Oct 2006 06:21:06 -0000
@@ -65,13 +65,14 @@
 #define ARES_ENOMEM             15
 #define ARES_EDESTRUCTION       16
 #define ARES_EBADSTR            17
+#define ARES_ECANCELLED         18
 
 /* ares_getnameinfo error codes */
-#define ARES_EBADFLAGS          18
+#define ARES_EBADFLAGS          19
 
 /* ares_getaddrinfo error codes */
-#define ARES_ENONAME            19
-#define ARES_EBADHINTS          20
+#define ARES_ENONAME            20
+#define ARES_EBADHINTS          21
 
 /* Flag values */
 #define ARES_FLAG_USEVC         (1 << 0)
Index: ares__close_sockets.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares__close_sockets.c,v
retrieving revision 1.7
diff -u -r1.7 ares__close_sockets.c
--- ares__close_sockets.c	22 Jul 2006 15:37:10 -0000	1.7
+++ ares__close_sockets.c	1 Oct 2006 06:21:06 -0000
@@ -27,6 +27,7 @@
 
 void ares__close_sockets(ares_channel channel, struct server_state *server)
 {
+  struct ares_frame call;
   struct send_request *sendreq;
 
   /* Free all pending output buffers. */
@@ -45,17 +46,24 @@
   server->tcp_buffer = NULL;
   server->tcp_lenbuf_pos = 0;
 
+  FRAME_PUSH(channel, &call);
+
   /* Close the TCP and UDP sockets. */
   if (server->tcp_socket != ARES_SOCKET_BAD)
     {
-      SOCK_STATE_CALLBACK(channel, server->tcp_socket, 0, 0);
       closesocket(server->tcp_socket);
       server->tcp_socket = ARES_SOCKET_BAD;
+      SOCK_STATE_CALLBACK(channel, server->tcp_socket, 0, 0);
+      if (!FRAME_OKAY(&call))
+        return;
     }
   if (server->udp_socket != ARES_SOCKET_BAD)
     {
-      SOCK_STATE_CALLBACK(channel, server->udp_socket, 0, 0);
       closesocket(server->udp_socket);
       server->udp_socket = ARES_SOCKET_BAD;
+      SOCK_STATE_CALLBACK(channel, server->udp_socket, 0, 0);
+      if (!FRAME_OKAY(&call))
+        return;
     }
+  FRAME_POP(channel, &call);
 }
Index: ares_cancel.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_cancel.c,v
retrieving revision 1.5
diff -u -r1.5 ares_cancel.c
--- ares_cancel.c	22 Jul 2006 15:37:10 -0000	1.5
+++ ares_cancel.c	1 Oct 2006 06:21:06 -0000
@@ -28,18 +28,20 @@
   struct query *query, *next;
   int i;
 
-  for (query = channel->queries; query; query = next)
+  FRAME_KILL_ALL(channel);
+
+  if (!(channel->flags & ARES_FLAG_STAYOPEN))
+  {
+    for (i = 0; i < channel->nservers; i++)
+      ares__close_sockets(channel, &channel->servers[i]);
+  }
+
+  for (query = channel->queries, channel->queries = NULL; query; query = next)
   {
     next = query->next;
-    query->callback(query->arg, ARES_ETIMEOUT, NULL, 0);
+    query->callback(query->arg, ARES_ECANCELLED, NULL, 0);
     free(query->tcpbuf);
     free(query->skip_server);
     free(query);
   }
-  channel->queries = NULL;
-  if (!(channel->flags & ARES_FLAG_STAYOPEN))
-  {
-    for (i = 0; i < channel->nservers; i++)
-      ares__close_sockets(channel, &channel->servers[i]);
-  }
 }
Index: ares_destroy.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_destroy.c,v
retrieving revision 1.7
diff -u -r1.7 ares_destroy.c
--- ares_destroy.c	22 Jul 2006 15:37:10 -0000	1.7
+++ ares_destroy.c	1 Oct 2006 06:21:06 -0000
@@ -17,11 +17,13 @@
 
 #include "setup.h"
 #include <stdlib.h>
+#include <assert.h>
 #include "ares.h"
 #include "ares_private.h"
 
 void ares_destroy(ares_channel channel)
 {
+  struct ares_frame call;
   int i;
   struct query *query;
 
@@ -34,11 +36,17 @@
   if(channel->sortlist)
     free(channel->sortlist);
   free(channel->lookups);
+
+  FRAME_KILL_ALL(channel);
+
+  FRAME_PUSH(channel, &call);
+
   while (channel->queries)
     {
       query = channel->queries;
       channel->queries = query->next;
       query->callback(query->arg, ARES_EDESTRUCTION, NULL, 0);
+      assert(FRAME_OKAY(&call));
       free(query->tcpbuf);
       free(query->skip_server);
       free(query);
Index: ares_init.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_init.c,v
retrieving revision 1.39
diff -u -r1.39 ares_init.c
--- ares_init.c	11 Sep 2006 20:25:13 -0000	1.39
+++ ares_init.c	1 Oct 2006 06:21:06 -0000
@@ -126,6 +126,7 @@
   channel->domains = NULL;
   channel->sortlist = NULL;
   channel->sock_state_cb = NULL;
+  channel->stack = NULL;
 
   /* Initialize configuration by each of the four sources, from highest
    * precedence to lowest.
Index: ares_private.h
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_private.h,v
retrieving revision 1.22
diff -u -r1.22 ares_private.h
--- ares_private.h	26 Jul 2006 10:54:08 -0000	1.22
+++ ares_private.h	1 Oct 2006 06:21:06 -0000
@@ -156,6 +156,13 @@
   unsigned short type;
 };
 
+/* Data structure representing an ares function call stack frame. */
+struct ares_frame {
+  struct ares_channeldata **cp;  
+
+  struct ares_frame *next;
+};
+
 struct ares_channeldata {
   /* Configuration data */
   int flags;
@@ -182,6 +189,8 @@
 
   ares_sock_state_cb sock_state_cb;
   void *sock_state_cb_data;
+
+  struct ares_frame *stack;
 };
 
 void ares__send_query(ares_channel channel, struct query *query, time_t now);
@@ -189,12 +198,39 @@
 int ares__get_hostent(FILE *fp, int family, struct hostent **host);
 int ares__read_line(FILE *fp, char **buf, int *bufsize);
 
+#include <assert.h> /* For FRAME_POP sanity checking. */
+
 #define SOCK_STATE_CALLBACK(c, s, r, w)                                 \
   do {                                                                  \
     if ((c)->sock_state_cb)                                             \
       (c)->sock_state_cb((c)->sock_state_cb_data, (s), (r), (w));       \
   } while (0)
 
+#define FRAME_PUSH(c, f)                                                \
+  do {                                                                  \
+    (f)->cp    = &(c);                                                  \
+    (f)->next  = (c)->stack;                                            \
+    (c)->stack = (f);                                                   \
+  } while(0)
+
+#define FRAME_POP(c, f)                                                 \
+  do {                                                                  \
+    if (FRAME_OKAY((f))) {                                              \
+      assert((f) == (c)->stack);                                        \
+      (c)->stack = (c)->stack->next;                                    \
+    }                                                                   \
+  } while(0)
+
+#define FRAME_OKAY(f) (*(f)->cp != 0)
+
+#define FRAME_KILL(f) (*(f)->cp = 0)
+
+#define FRAME_KILL_ALL(c)                                               \
+  do {                                                                  \
+    for (; (c)->stack; (c)->stack = (c)->stack->next)                   \
+      FRAME_KILL((c)->stack);                                           \
+  } while (0)
+
 #ifdef CURLDEBUG
 /* This is low-level hard-hacking memory leak tracking and similar. Using the
    libcurl lowlevel code from within library is ugly and only works when
Index: ares_process.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_process.c,v
retrieving revision 1.31
diff -u -r1.31 ares_process.c
--- ares_process.c	29 Aug 2006 15:17:48 -0000	1.31
+++ ares_process.c	1 Oct 2006 06:21:06 -0000
@@ -88,13 +88,31 @@
  */
 void ares_process(ares_channel channel, fd_set *read_fds, fd_set *write_fds)
 {
+  struct ares_frame call;
   time_t now;
 
   time(&now);
+
+  FRAME_PUSH(channel, &call);
+
   write_tcp_data(channel, write_fds, now);
+
+  if (!FRAME_OKAY(&call))
+    return;
+
   read_tcp_data(channel, read_fds, now);
+
+  if (!FRAME_OKAY(&call))
+    return;
+
   read_udp_packets(channel, read_fds, now);
+
+  if (!FRAME_OKAY(&call))
+    return;
+
   process_timeouts(channel, now);
+
+  FRAME_POP(channel, &call);
 }
 
 /* Return 1 if the specified errno describes a readiness error, or 0
@@ -127,6 +145,7 @@
  */
 static void write_tcp_data(ares_channel channel, fd_set *write_fds, time_t now)
 {
+  struct ares_frame call;
   struct server_state *server;
   struct send_request *sendreq;
   struct iovec *vec;
@@ -135,6 +154,8 @@
   ssize_t wcount;
   size_t n;
 
+  FRAME_PUSH(channel, &call);
+
   for (i = 0; i < channel->nservers; i++)
     {
       /* Make sure server has data to send and is selected in write_fds. */
@@ -165,7 +186,11 @@
           if (wcount < 0)
             {
               if (!try_again(GET_ERRNO()))
+                {
                   handle_error(channel, i, now);
+                  if (!FRAME_OKAY(&call))
+                    return;
+                }
               continue;
             }
 
@@ -177,12 +202,14 @@
                 {
                   wcount -= sendreq->len;
                   server->qhead = sendreq->next;
+                  free(sendreq);
                   if (server->qhead == NULL)
                     {
-                      SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0);
                       server->qtail = NULL;
+                      SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0);
+                      if (!FRAME_OKAY(&call))
+                        return;
                     }
-                  free(sendreq);
                 }
               else
                 {
@@ -201,7 +228,11 @@
           if (scount < 0)
             {
               if (!try_again(GET_ERRNO()))
+                {
                   handle_error(channel, i, now);
+                  if (!FRAME_OKAY(&call))
+                    return;
+                }
               continue;
             }
 
@@ -209,12 +240,14 @@
           if ((size_t)scount == sendreq->len)
             {
               server->qhead = sendreq->next;
+              free(sendreq);
               if (server->qhead == NULL)
                 {
-                  SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0);
                   server->qtail = NULL;
+                  SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0);
+                  if (!FRAME_OKAY(&call))
+                    return;
                 }
-              free(sendreq);
             }
           else
             {
@@ -223,6 +256,10 @@
             }
         }
     }
+
+    FRAME_POP(channel, &call);
+
+    return;
 }
 
 /* If any TCP socket selects true for reading, read some data,
@@ -231,9 +268,12 @@
  */
 static void read_tcp_data(ares_channel channel, fd_set *read_fds, time_t now)
 {
+  struct ares_frame call;
   struct server_state *server;
-  int i;
-  ssize_t count;
+  unsigned char *abuf;
+  ssize_t i, count, alen;
+
+  FRAME_PUSH(channel, &call);
 
   for (i = 0; i < channel->nservers; i++)
     {
@@ -254,7 +294,11 @@
           if (count <= 0)
             {
               if (!(count == -1 && try_again(GET_ERRNO())))
+                {
                   handle_error(channel, i, now);
+                  if (!FRAME_OKAY(&call))
+                    return;
+                }
               continue;
             }
 
@@ -267,9 +311,13 @@
               server->tcp_length = server->tcp_lenbuf[0] << 8
                 | server->tcp_lenbuf[1];
               server->tcp_buffer = malloc(server->tcp_length);
-              if (!server->tcp_buffer)
-                handle_error(channel, i, now);
               server->tcp_buffer_pos = 0;
+              if (!server->tcp_buffer)
+                {
+                  handle_error(channel, i, now);
+                  if (!FRAME_OKAY(&call))
+                    return;
+                }
             }
         }
       else
@@ -281,37 +329,52 @@
           if (count <= 0)
             {
               if (!(count == -1 && try_again(GET_ERRNO())))
+                {
                   handle_error(channel, i, now);
+                  if (!FRAME_OKAY(&call))
+                    return;
+                }
               continue;
             }
 
           server->tcp_buffer_pos += (int)count;
           if (server->tcp_buffer_pos == server->tcp_length)
             {
+              abuf = server->tcp_buffer;
+              alen = server->tcp_length;
+              server->tcp_buffer = NULL;
+              server->tcp_lenbuf_pos = 0;
+              server->tcp_buffer_pos = 0;
+
               /* We finished reading this answer; process it and
                * prepare to read another length word.
                */
               process_answer(channel, server->tcp_buffer, server->tcp_length,
                              i, 1, now);
-          if (server->tcp_buffer)
-                        free(server->tcp_buffer);
-              server->tcp_buffer = NULL;
-              server->tcp_lenbuf_pos = 0;
-              server->tcp_buffer_pos = 0;
+              free(abuf);
+              if (!FRAME_OKAY(&call))
+                return;
             }
         }
     }
+
+    FRAME_POP(channel, &call);
+
+    return;
 }
 
 /* If any UDP sockets select true for reading, process them. */
 static void read_udp_packets(ares_channel channel, fd_set *read_fds,
                              time_t now)
 {
+  struct ares_frame call;
   struct server_state *server;
   int i;
   ssize_t count;
   unsigned char buf[PACKETSZ + 1];
 
+  FRAME_PUSH(channel, &call);
+
   for (i = 0; i < channel->nservers; i++)
     {
       /* Make sure the server has a socket and is selected in read_fds. */
@@ -325,17 +388,30 @@
       if (count == -1 && try_again(GET_ERRNO()))
         continue;
       else if (count <= 0)
-        handle_error(channel, i, now);
+        {
+          handle_error(channel, i, now);
+          if (!FRAME_OKAY(&call))
+            return;
+        }
 
       process_answer(channel, buf, (int)count, i, 0, now);
+      if (!FRAME_OKAY(&call))
+        return;
     }
+
+    FRAME_POP(channel, &call);
+
+    return;
 }
 
 /* If any queries have timed out, note the timeout and move them on. */
 static void process_timeouts(ares_channel channel, time_t now)
 {
+  struct ares_frame call;
   struct query *query, *next;
 
+  FRAME_PUSH(channel, &call);
+
   for (query = channel->queries; query; query = next)
     {
       next = query->next;
@@ -343,8 +419,14 @@
         {
           query->error_status = ARES_ETIMEOUT;
           next = next_server(channel, query, now);
+          if (!FRAME_OKAY(&call))
+            return;
         }
     }
+
+  FRAME_POP(channel, &call);
+
+  return;
 }
 
 /* Handle an answer from a server. */
@@ -440,6 +522,10 @@
 
 static struct query *next_server(ares_channel channel, struct query *query, time_t now)
 {
+  struct ares_frame call;
+
+  FRAME_PUSH(channel, &call);
+
   /* Advance to the next server or try. */
   query->server++;
   for (; query->try < channel->tries; query->try++)
@@ -449,6 +535,9 @@
           if (!query->skip_server[query->server])
             {
               ares__send_query(channel, query, now);
+              if (!FRAME_OKAY(&call))
+                return NULL;
+              FRAME_POP(channel, &call);
               return (query->next);
             }
         }
@@ -458,11 +547,15 @@
       if (query->using_tcp)
         break;
     }
+
+  FRAME_POP(channel, &call);
+
   return end_query(channel, query, query->error_status, NULL, 0);
 }
 
 void ares__send_query(ares_channel channel, struct query *query, time_t now)
 {
+  struct ares_frame call;
   struct send_request *sendreq;
   struct server_state *server;
 
@@ -484,21 +577,28 @@
       sendreq = calloc(sizeof(struct send_request), 1);
       if (!sendreq)
         {
-        end_query(channel, query, ARES_ENOMEM, NULL, 0);
+          end_query(channel, query, ARES_ENOMEM, NULL, 0);
           return;
         }
       sendreq->data = query->tcpbuf;
       sendreq->len = query->tcplen;
       sendreq->next = NULL;
+      query->timeout = 0;
       if (server->qtail)
-        server->qtail->next = sendreq;
+        {
+          server->qtail->next = sendreq;
+          server->qtail = sendreq;
+        }
       else
         {
-          SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 1);
           server->qhead = sendreq;
+          server->qtail = sendreq;
+          FRAME_PUSH(channel, &call);
+          SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 1);
+          if (!FRAME_OKAY(&call))
+            return;
+          FRAME_POP(channel, &call);
         }
-      server->qtail = sendreq;
-      query->timeout = 0;
     }
   else
     {
@@ -619,9 +719,11 @@
     }
   }
 
-  SOCK_STATE_CALLBACK(channel, s, 1, 0);
   server->tcp_buffer_pos = 0;
   server->tcp_socket = s;
+
+  SOCK_STATE_CALLBACK(channel, s, 1, 0);
+
   return 0;
 }
 
@@ -649,9 +751,10 @@
       return -1;
     }
 
+  server->udp_socket = s;
+
   SOCK_STATE_CALLBACK(channel, s, 1, 0);
 
-  server->udp_socket = s;
   return 0;
 }
 
@@ -737,24 +840,35 @@
 static struct query *end_query (ares_channel channel, struct query *query, int status,
                       unsigned char *abuf, int alen)
 {
+  struct ares_frame call;
   struct query **q, *next;
   int i;
 
-  query->callback(query->arg, status, abuf, alen);
   for (q = &channel->queries; *q; q = &(*q)->next)
     {
       if (*q == query)
         break;
     }
+
   *q = query->next;
   if (*q)
     next = (*q)->next;
   else
     next = NULL;
+
+  FRAME_PUSH(channel, &call);
+
+  query->callback(query->arg, status, abuf, alen);
+
   free(query->tcpbuf);
   free(query->skip_server);
   free(query);
 
+  if (!FRAME_OKAY(&call))
+    return NULL;
+
+  FRAME_POP(channel, &call);
+
   /* Simple cleanup policy: if no queries are remaining, close all
    * network sockets unless STAYOPEN is set.
    */
