]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Bug #852: A large number of queued DNS lookups for the same domain
authorhno <>
Sun, 10 Oct 2004 08:53:26 +0000 (08:53 +0000)
committerhno <>
Sun, 10 Oct 2004 08:53:26 +0000 (08:53 +0000)
src/dns_internal.cc

index a73efbac5a42efe56a23ad6e02327faaecc14c75..e666674366315c792bad13f955539c5ea33b5b80 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: dns_internal.cc,v 1.63 2004/04/10 13:10:17 hno Exp $
+ * $Id: dns_internal.cc,v 1.64 2004/10/10 02:53:26 hno Exp $
  *
  * DEBUG: section 78    DNS lookups; interacts with lib/rfc1035.c
  * AUTHOR: Duane Wessels
@@ -63,6 +63,8 @@ typedef struct _ns ns;
 
 struct _idns_query
 {
+    hash_link hash;
+    char query[RFC1035_MAXHOSTNAMESZ+1];
     char buf[512];
     size_t sz;
     unsigned short id;
@@ -77,6 +79,7 @@ struct _idns_query
     int attempt;
     const char *error;
     int rcode;
+    idns_query *queue;
 };
 
 struct _ns
@@ -93,6 +96,7 @@ static int nns = 0;
 static int nns_alloc = 0;
 static dlink_list lru_list;
 static int event_queued = 0;
+static hash_table *idns_lookup_hash = NULL;
 
 static OBJH idnsStats;
 static void idnsAddNameserver(const char *buf);
@@ -544,6 +548,36 @@ idnsFindQuery(unsigned short id)
     return NULL;
 }
 
+static void
+idnsCallback(idns_query *q, rfc1035_rr *answers, int n, const char *error)
+{
+    IDNSCB *callback;
+    void *cbdata;
+
+    callback = q->callback;
+    q->callback = NULL;
+
+    if (cbdataReferenceValidDone(q->callback_data, &cbdata))
+        callback(cbdata, answers, n, error);
+
+    while(q->queue) {
+        idns_query *q2 = q->queue;
+        q->queue = q2->queue;
+        callback = q2->callback;
+        q2->callback = NULL;
+
+        if (cbdataReferenceValidDone(q2->callback_data, &cbdata))
+            callback(cbdata, answers, n, error);
+
+        memFree(q2, MEM_IDNS_QUERY);
+    }
+
+    if (q->hash.key) {
+        hash_remove_link(idns_lookup_hash, &q->hash);
+        q->hash.key = NULL;
+    }
+}
+
 static void
 idnsGrokReply(const char *buf, size_t sz)
 {
@@ -551,8 +585,7 @@ idnsGrokReply(const char *buf, size_t sz)
     rfc1035_rr *answers = NULL;
     unsigned short rid = 0xFFFF;
     idns_query *q;
-    IDNSCB *callback;
-    void *cbdata;
+
     n = rfc1035AnswersUnpack(buf,
                              sz,
                              &answers,
@@ -597,12 +630,7 @@ idnsGrokReply(const char *buf, size_t sz)
         }
     }
 
-    callback = q->callback;
-    q->callback = NULL;
-
-    if (cbdataReferenceValidDone(q->callback_data, &cbdata))
-        callback(cbdata, answers, n, q->error);
-
+    idnsCallback(q, answers, n, q->error);
     rfc1035RRDestroy(answers, n);
 
     memFree(q, MEM_IDNS_QUERY);
@@ -725,20 +753,14 @@ idnsCheckQueue(void *unused)
         if (tvSubDsec(q->start_t, current_time) < Config.Timeout.idns_query) {
             idnsSendQuery(q);
         } else {
-            IDNSCB *callback;
-            void *cbdata;
             debug(78, 2) ("idnsCheckQueue: ID %x: giving up after %d tries and %5.1f seconds\n",
                           (int) q->id, q->nsends,
                           tvSubDsec(q->start_t, current_time));
-            callback = q->callback;
-            q->callback = NULL;
-
-            if (cbdataReferenceValidDone(q->callback_data, &cbdata)) {
-                if (q->rcode != 0)
-                    callback(cbdata, NULL, -q->rcode, q->error);
-                else
-                    callback(cbdata, NULL, -16, "Timeout");
-            }
+
+            if (q->rcode != 0)
+                idnsCallback(q, NULL, -q->rcode, q->error);
+            else
+                idnsCallback(q, NULL, -16, "Timeout");
 
             memFree(q, MEM_IDNS_QUERY);
         }
@@ -830,6 +852,7 @@ idnsInit(void)
                          "Internal DNS Statistics",
                          idnsStats, 0, 1);
         memset(RcodeMatrix, '\0', sizeof(RcodeMatrix));
+        idns_lookup_hash = hash_create((HASHCMP *) strcmp, 103, hash_string);
         init++;
     }
 }
@@ -847,11 +870,49 @@ idnsShutdown(void)
     idnsFreeNameservers();
 }
 
+static int
+idnsCachedLookup(const char *key, IDNSCB * callback, void *data)
+{
+    idns_query *q;
+
+    idns_query *old = (idns_query *) hash_lookup(idns_lookup_hash, key);
+
+    if (!old)
+        return 0;
+
+    q = (idns_query *)memAllocate(MEM_IDNS_QUERY);
+
+    q->callback = callback;
+
+    q->callback_data = cbdataReference(data);
+
+    q->queue = old->queue;
+
+    old->queue = q;
+
+    return 1;
+}
+
+static void
+idnsCacheQuery(idns_query *q, const char *key)
+{
+    xstrncpy(q->query, key, sizeof(q->query));
+    q->hash.key = q->query;
+    hash_join(idns_lookup_hash, &q->hash);
+}
+
 void
 idnsALookup(const char *name, IDNSCB * callback, void *data)
 {
-    idns_query *q = (idns_query *)memAllocate(MEM_IDNS_QUERY);
+    idns_query *q;
+
+    if (idnsCachedLookup(name, callback, data))
+        return;
+
+    q = (idns_query *)memAllocate(MEM_IDNS_QUERY);
+
     q->sz = sizeof(q->buf);
+
     q->id = rfc1035BuildAQuery(name, q->buf, &q->sz);
 
     if (0 == q->id) {
@@ -866,6 +927,7 @@ idnsALookup(const char *name, IDNSCB * callback, void *data)
     q->callback = callback;
     q->callback_data = cbdataReference(data);
     q->start_t = current_time;
+    idnsCacheQuery(q, name);
     idnsSendQuery(q);
 }
 
@@ -873,14 +935,30 @@ void
 
 idnsPTRLookup(const struct in_addr addr, IDNSCB * callback, void *data)
 {
-    idns_query *q = (idns_query *)memAllocate(MEM_IDNS_QUERY);
+    idns_query *q;
+
+    const char *ip = inet_ntoa(addr);
+
+    if (idnsCachedLookup(ip, callback, data))
+        return;
+
+    q = (idns_query *)memAllocate(MEM_IDNS_QUERY);
+
     q->sz = sizeof(q->buf);
+
     q->id = rfc1035BuildPTRQuery(addr, q->buf, &q->sz);
+
     debug(78, 3) ("idnsPTRLookup: buf is %d bytes for %s, id = %#hx\n",
-                  (int) q->sz, inet_ntoa(addr), q->id);
+                  (int) q->sz, ip, q->id);
+
     q->callback = callback;
+
     q->callback_data = cbdataReference(data);
+
     q->start_t = current_time;
+
+    idnsCacheQuery(q, ip);
+
     idnsSendQuery(q);
 }