Subject: [PATCH 1/4] ares_cancel(): ensure cancellation of all requests

[PATCH 1/4] ares_cancel(): ensure cancellation of all requests

From: Alexander Klauer <Alexander.Klauer_at_itwm.fraunhofer.de>
Date: Thu, 21 Mar 2013 10:20:12 +0100

An invocation of ares_cancel() walks through the request list, calling
the callbacks of all pending requests on a channel. Previously, if such
a callback added a new request to the channel, the request list might
not end up empty, causing an abort by assertion failure. The present
commit ensures that all such newly added requests are cancelled
immediately and make it never into request list. Thus, the crash is
avoided, and it is made certain that upon return of ares_cancel(), there
are no requests whatsoever on the channel.

---
 ares.h               |    1 +
 ares_cancel.c        |    6 ++++++
 ares_gethostbyaddr.c |    6 ++++++
 ares_gethostbyname.c |    6 ++++++
 ares_getnameinfo.c   |    6 ++++++
 ares_private.h       |    3 +++
 ares_query.c         |    6 ++++++
 ares_search.c        |    6 ++++++
 ares_send.c          |    6 ++++++
 9 files changed, 46 insertions(+)
diff --git a/ares.h b/ares.h
index 9b3f376..8a9b995 100644
--- a/ares.h
+++ b/ares.h
@@ -140,6 +140,7 @@ extern "C" {
 #define ARES_FLAG_NOALIASES     (1 << 6)
 #define ARES_FLAG_NOCHECKRESP   (1 << 7)
 #define ARES_FLAG_EDNS          (1 << 8)
+/* #define ARES_FLAG_CANCELLING    (1 << 9) */ /* defined in ares_private.h */
 
 /* Option mask values */
 #define ARES_OPT_FLAGS          (1 << 0)
diff --git a/ares_cancel.c b/ares_cancel.c
index e5bb050..012fec1 100644
--- a/ares_cancel.c
+++ b/ares_cancel.c
@@ -30,6 +30,11 @@ void ares_cancel(ares_channel channel)
   struct list_node* list_node;
   int i;
 
+  if (channel->flags & ARES_FLAG_CANCELLING)
+  {
+    return; /* Already being cancelled */
+  }
+  channel->flags |= ARES_FLAG_CANCELLING;
   list_head = &(channel->all_queries);
   for (list_node = list_head->next; list_node != list_head; )
   {
@@ -60,4 +65,5 @@ void ares_cancel(ares_channel channel)
         ares__close_sockets(channel, &channel->servers[i]);
     }
   }
+  channel->flags &= ~ARES_FLAG_CANCELLING;
 }
diff --git a/ares_gethostbyaddr.c b/ares_gethostbyaddr.c
index 85862e2..f77dafd 100644
--- a/ares_gethostbyaddr.c
+++ b/ares_gethostbyaddr.c
@@ -66,6 +66,12 @@ void ares_gethostbyaddr(ares_channel channel, const void *addr, int addrlen,
 {
   struct addr_query *aquery;
 
+  if (channel->flags & ARES_FLAG_CANCELLING)
+    {
+      callback(arg, ARES_ECANCELLED, 0, NULL);
+      return;
+    }
+
   if (family != AF_INET && family != AF_INET6)
     {
       callback(arg, ARES_ENOTIMP, 0, NULL);
diff --git a/ares_gethostbyname.c b/ares_gethostbyname.c
index 2b27b2e..12e452b 100644
--- a/ares_gethostbyname.c
+++ b/ares_gethostbyname.c
@@ -95,6 +95,12 @@ void ares_gethostbyname(ares_channel channel, const char *name, int family,
     return;
   }
 
+  if (channel->flags & ARES_FLAG_CANCELLING)
+    {
+      callback(arg, ARES_ECANCELLED, 0, NULL);
+      return;
+    }
+
   if (fake_hostent(name, family, callback, arg))
     return;
 
diff --git a/ares_getnameinfo.c b/ares_getnameinfo.c
index 5b9f638..26521be 100644
--- a/ares_getnameinfo.c
+++ b/ares_getnameinfo.c
@@ -88,6 +88,12 @@ void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
   struct nameinfo_query *niquery;
   unsigned int port = 0;
 
+  if (channel->flags & ARES_FLAG_CANCELLING)
+    {
+      callback(arg, ARES_ECANCELLED, 0, NULL, NULL);
+      return;
+    }
+
   /* Validate socket address family and length */
   if ((sa->sa_family == AF_INET) &&
       (salen == sizeof(struct sockaddr_in)))
diff --git a/ares_private.h b/ares_private.h
index ab5be5a..14b9ac2 100644
--- a/ares_private.h
+++ b/ares_private.h
@@ -113,6 +113,9 @@
 #define EDNSFIXEDSZ    11    /* Size of EDNS header */
 /********* EDNS defines section ******/
 
+#define ARES_FLAG_CANCELLING (1 << 9) /* flag indicating all requests
+                                         being cancelled on a channel */
+
 struct ares_addr {
   int family;
   union {
diff --git a/ares_query.c b/ares_query.c
index 4bc9c25..2e8949a 100644
--- a/ares_query.c
+++ b/ares_query.c
@@ -115,6 +115,12 @@ void ares_query(ares_channel channel, const char *name, int dnsclass,
   unsigned char *qbuf;
   int qlen, rd, status;
 
+  if (channel->flags & ARES_FLAG_CANCELLING)
+    {
+      callback(arg, ARES_ECANCELLED, 0, NULL, 0);
+      return;
+    }
+
   /* Compose the query. */
   rd = !(channel->flags & ARES_FLAG_NORECURSE);
   status = ares_create_query(name, dnsclass, type, channel->next_id, rd, &qbuf,
diff --git a/ares_search.c b/ares_search.c
index ec07640..1d3e518 100644
--- a/ares_search.c
+++ b/ares_search.c
@@ -54,6 +54,12 @@ void ares_search(ares_channel channel, const char *name, int dnsclass,
   const char *p;
   int status, ndots;
 
+  if (channel->flags & ARES_FLAG_CANCELLING)
+    {
+      callback(arg, ARES_ECANCELLED, 0, NULL, 0);
+      return;
+    }
+
   /* If name only yields one domain to search, then we don't have
    * to keep extra state, so just do an ares_query().
    */
diff --git a/ares_send.c b/ares_send.c
index 1a450b1..81a4049 100644
--- a/ares_send.c
+++ b/ares_send.c
@@ -39,6 +39,12 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
   int i, packetsz;
   struct timeval now;
 
+  if (channel->flags & ARES_FLAG_CANCELLING)
+    {
+      callback(arg, ARES_ECANCELLED, 0, NULL, 0);
+      return;
+    }
+
   /* Verify that the query is at least long enough to hold the header. */
   if (qlen < HFIXEDSZ || qlen >= (1 << 16))
     {
-- 
1.7.9.5
Received on 2013-03-21