Send DNS A and AAAA queries in parallel
        
This implements sending DNS A & AAAA queries in parallel by
creating "slave" idns_query requests. Current implementation
uses the A lookup as "master" and AAAA as "slave" query.
        
Long term this should probably be restructured to separate "lookup
state" and "query", or even better yet to defer the DNS lookups
until connect time and perform A respective AAAA as needed only and
not look up both before attemting to connect.
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [email protected]\
#   31rsk57yl1j7rniv
# target_branch: http://www.squid-cache.org/bzr/squid3/trunk/
# testament_sha1: 971ef9bbd003970f3447521922c7f7d30b9ebbe7
# timestamp: 2012-01-06 21:35:43 +0100
# base_revision_id: [email protected]\
#   wm58j1qdf5zuz6zz
# 
# Begin patch
=== modified file 'src/dns_internal.cc'
--- src/dns_internal.cc	2012-01-03 02:19:30 +0000
+++ src/dns_internal.cc	2012-01-06 20:30:35 +0000
@@ -127,11 +127,12 @@
     char name[NS_MAXDNAME + 1];
     char orig[NS_MAXDNAME + 1];
     ssize_t sz;
-    unsigned short msg_id; /// random query ID sent to server; changes with every query sent
+    unsigned short query_id; /// random query ID sent to server; changes with every query sent
     InstanceId<idns_query> xact_id; /// identifies our "transaction", stays constant when query is retried
 
     int nsends;
     int need_vc;
+    int pending;
 
     struct timeval start_t;
     struct timeval sent_t;
@@ -142,13 +143,12 @@
     int attempt;
     int rcode;
     idns_query *queue;
+    idns_query *slave;  // AAAA
+    idns_query *master; // A
     unsigned short domain;
     unsigned short do_searchpath;
-    bool need_A;
-    struct {
-        int count;
-        rfc1035_rr *answers;
-    } initial_AAAA;
+    rfc1035_message *message;
+    int ancount;
 };
 InstanceIdDefinitions(idns_query,  "dns");
 
@@ -249,6 +249,7 @@
 static void idnsRcodeCount(int, int);
 static CLCB idnsVCClosed;
 static unsigned short idnsQueryID(void);
+static void idnsSendSlaveAAAAQuery(idns_query *q);
 
 static void
 idnsAddNameserver(const char *buf)
@@ -670,7 +671,7 @@
     for (n = lru_list.head; n; n = n->next) {
         q = (idns_query *)n->data;
         storeAppendPrintf(sentry, "%#06x %4d %5d %10.3f %9.3f\n",
-                          (int) q->msg_id, (int) q->sz, q->nsends,
+                          (int) q->query_id, (int) q->sz, q->nsends,
                           tvSubDsec(q->start_t, current_time),
                           tvSubDsec(q->sent_t, current_time));
     }
@@ -906,8 +907,6 @@
     int ns;
 
     q->start_t = current_time;
-    q->msg_id = idnsQueryID();
-    rfc1035SetQueryID(q->buf, q->msg_id);
 
     do {
         ns = q->nsends % nns;
@@ -918,7 +917,7 @@
         } else {
             if (DnsSocketB >= 0 && nameservers[ns].S.IsIPv6())
                 y = comm_udp_sendto(DnsSocketB, nameservers[ns].S, q->buf, q->sz);
-            else if (DnsSocketA)
+            else if (DnsSocketA >= 0)
                 x = comm_udp_sendto(DnsSocketA, nameservers[ns].S, q->buf, q->sz);
         }
 
@@ -943,6 +942,7 @@
     nameservers[ns].nqueries++;
     q->queue_t = current_time;
     dlinkAdd(q, &q->lru, &lru_list);
+    q->pending = 1;
     idnsTickleQueue();
 }
 
@@ -973,7 +973,7 @@
     for (n = lru_list.tail; n; n = n->prev) {
         q = (idns_query*)n->data;
 
-        if (q->msg_id == id)
+        if (q->query_id == id)
             return q;
     }
 
@@ -1028,16 +1028,6 @@
     }
 }
 
-void
-idnsDropMessage(rfc1035_message *message, idns_query *q)
-{
-    rfc1035MessageDestroy(&message);
-    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, int from_ns)
 {
@@ -1092,9 +1082,11 @@
     }
 #endif
 
+    dlinkDelete(&q->lru, &lru_list);
+    q->pending = 0;
+
     if (message->tc) {
         debugs(78, 3, HERE << "Resolver requested TC (" << q->query.name << ")");
-        dlinkDelete(&q->lru, &lru_list);
         rfc1035MessageDestroy(&message);
 
         if (!q->need_vc) {
@@ -1112,7 +1104,6 @@
         return;
     }
 
-    dlinkDelete(&q->lru, &lru_list);
     idnsRcodeCount(n, q->attempt);
 
     if (n < 0) {
@@ -1131,7 +1122,7 @@
             return;
         }
 
-        if (q->rcode == 3 && q->do_searchpath && q->attempt < MAX_ATTEMPT) {
+        if (q->rcode == 3 && !q->master && q->do_searchpath && q->attempt < MAX_ATTEMPT) {
             assert(NULL == message->answer);
             strcpy(q->name, q->orig);
 
@@ -1146,17 +1137,21 @@
                 q->attempt++;
             }
 
-            idnsDropMessage(message, q);
+            rfc1035MessageDestroy(&message);
 
-            if (Ip::EnableIpv6 && q->query.qtype == RFC1035_TYPE_AAAA) {
-                debugs(78, 3, "idnsGrokReply: Trying AAAA Query for " << q->name);
-                q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, Config.dns.packet_max);
-            } else {
-                debugs(78, 3, "idnsGrokReply: Trying A Query for " << q->name);
-                // see EDNS notes at top of file why this sends 0
-                q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, 0);
+            // cleanup stale AAAA query
+            while (idns_query *slave = q->slave) {
+                dlinkDelete(&slave->lru, &lru_list);
+                q->slave = slave->slave;
+                rfc1035MessageDestroy(&slave->message);
+                cbdataFree(slave);
             }
-
+            
+            // Build new query
+            q->query_id = idnsQueryID();
+            debugs(78, 3, "idnsGrokReply: Trying A Query for " << q->name);
+            // see EDNS notes at top of file why this sends 0
+            q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->query_id, &q->query, 0);
             if (q->sz < 0) {
                 /* problem with query data -- query not sent */
                 idnsCallback(static_cast<idns_query *>(q->callback_data), NULL, 0, "Internal error");
@@ -1164,94 +1159,62 @@
                 return;
             }
 
-            idnsCacheQuery(q);
             idnsSendQuery(q);
-            return;
-        }
-    }
-
-    if (q->need_A && (Config.onoff.dns_require_A == 1 || n <= 0 ) ) {
-        /* ERROR or NO AAAA exist. Failover to A records. */
-        /*      Apparently its also a good idea to lookup and store the A records
-         *      just in case the AAAA are not available when we need them.
-         *      This could occur due to number of network failings beyond our control
-         *      thus the || above allowing the user to request always both.
-         */
-
-        if (n == 0)
-            debugs(78, 3, "idnsGrokReply: " << q->name << " has no AAAA records. Looking up A record instead.");
-        else if (q->need_A && n <= 0)
-            debugs(78, 3, "idnsGrokReply: " << q->name << " AAAA query failed. Trying A now instead.");
-        else // admin requested this.
-            debugs(78, 3, "idnsGrokReply: " << q->name << " AAAA query done. Configured to retrieve A now also.");
-
-        // move the initial message results into the failover query for merging later.
-        if (n > 0) {
-            q->initial_AAAA.count = message->ancount;
-            q->initial_AAAA.answers = message->answer;
-            message->answer = NULL;
-        }
-
-        // remove the hashed query info
-        idnsDropMessage(message, q);
-
-        // reset the query as an A query
-        q->nsends = 0;
-        // see EDNS notes at top of file why this sends 0
-        q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, 0);
-        q->need_A = false;
-
-        if (q->sz < 0) {
-            /* problem with query data -- query not sent */
-            idnsCallback(static_cast<idns_query *>(q->callback_data), NULL, 0, "Internal error");
-            cbdataFree(q);
-            return;
-        }
-
-        idnsCacheQuery(q);
-        idnsSendQuery(q);
-        return;
-    }
-
-    /** If there are two result sets from preceeding AAAA and A lookups merge them with a preference for AAAA */
-    if (q->initial_AAAA.count > 0 && n > 0) {
-        /* two sets of RR need merging */
-        rfc1035_rr *result = (rfc1035_rr*) xmalloc( sizeof(rfc1035_rr)*(n + q->initial_AAAA.count) );
-        rfc1035_rr *tmp = result;
-
-        debugs(78, 6, HERE << "Merging DNS results " << q->name << " AAAA has " << q->initial_AAAA.count << " RR, A has " << n << " RR");
-
-        if (Config.dns.v4_first) {
-            memcpy( tmp, message->answer, (sizeof(rfc1035_rr)*n) );
-            tmp += n;
-            /* free the RR object without freeing its child strings (they are now taken by the copy above) */
-            safe_free(message->answer);
-        }
-
-        memcpy(tmp, q->initial_AAAA.answers, (sizeof(rfc1035_rr)*(q->initial_AAAA.count)) );
-        tmp += q->initial_AAAA.count;
-        /* free the RR object without freeing its child strings (they are now taken by the copy above) */
-        safe_free(q->initial_AAAA.answers);
-
-        if (!Config.dns.v4_first) {
-            memcpy( tmp, message->answer, (sizeof(rfc1035_rr)*n) );
-            /* free the RR object without freeing its child strings (they are now taken by the copy above) */
-            safe_free(message->answer);
-        }
-
-        n += q->initial_AAAA.count;
-        q->initial_AAAA.count = 0;
-        message->answer = result;
-        message->ancount = n;
-    } else if (q->initial_AAAA.count > 0 && n <= 0) {
-        /* initial of dual queries was the only result set. */
-        debugs(78, 6, HERE << "Merging DNS results " << q->name << " AAAA has " << q->initial_AAAA.count << " RR, A has " << n << " RR");
-        rfc1035RRDestroy(&(message->answer), n);
-        message->answer = q->initial_AAAA.answers;
-        n = q->initial_AAAA.count;
-        message->ancount = n;
-    }
-    /* else initial results were empty. just use the final set as authoritative */
+            if (Ip::EnableIpv6)
+                idnsSendSlaveAAAAQuery(q);
+            return;
+        }
+    }
+
+    q->message = message;
+    q->ancount = n;
+
+    if (q->master)
+        q = q->master;
+
+    idns_query *q2;
+    // If any of our subqueries are still pending then wait for them to complete before continuing
+    for ( q2 = q; q2; q2 = q2->slave) {
+        if (q2->pending) {
+            return;
+        }
+    }
+
+    /* Merge results */
+    message = q->message;
+    n = q->ancount;
+    
+    while ( (q2 = q->slave) ) {
+        debugs(78, 6, HERE << "Merging DNS results " << q->name << " A has " << n << " RR, AAAA has " << q2->ancount << " RR");
+        q->slave = q2->slave;
+        if ( q2->ancount >= 0 ) {
+            if (n > 0 ) {
+                // two sets of RR need merging
+                rfc1035_rr *result = (rfc1035_rr*) xmalloc( sizeof(rfc1035_rr)*(n + q2->ancount) );
+                if (Config.dns.v4_first) {
+                    memcpy(result, message->answer, (sizeof(rfc1035_rr)*n) );
+                    memcpy(result+n, q2->message->answer, (sizeof(rfc1035_rr)*q2->ancount) );
+                } else {
+                    memcpy(result, q2->message->answer, (sizeof(rfc1035_rr)*q2->ancount) );
+                    memcpy(result+q2->ancount, message->answer, (sizeof(rfc1035_rr)*n) );
+                }
+                n += q2->ancount;
+                safe_free(message->answer);
+                message->answer = result;
+                message->ancount += q2->message->ancount;
+                safe_free(q2->message->answer);
+                q2->message->answer = NULL;
+            } else if (n < 0 || q2->ancount > 0) {
+                // first set empty / failed
+                rfc1035MessageDestroy(&message);
+                message = q->message = q2->message;
+                q2->message = NULL;
+                n = q2->ancount;
+            }
+        }
+        rfc1035MessageDestroy(&q2->message);
+        cbdataFree(q2);
+    }
 
     debugs(78, 6, HERE << "Sending " << n << " DNS results to caller.");
     idnsCallback(q, message->answer, n, rfc1035ErrorMessage(n));
@@ -1375,7 +1338,7 @@
 
         debugs(78, 3, "idnsCheckQueue: ID " << q->xact_id <<
                " QID 0x"  << std::hex << std::setfill('0')  <<
-               std::setw(4) << q->msg_id << ": timeout" );
+               std::setw(4) << q->query_id << ": timeout" );
 
         dlinkDelete(&q->lru, &lru_list);
 
@@ -1383,7 +1346,7 @@
             idnsSendQuery(q);
         } else {
             debugs(78, 2, "idnsCheckQueue: ID " << q->xact_id <<
-                   " QID 0x" << std::hex << q->msg_id <<
+                   " QID 0x" << std::hex << q->query_id <<
                    " : giving up after " << std::dec << q->nsends << " tries and " <<
                    std::setw(5)<< std::setprecision(2) << tvSubDsec(q->start_t, current_time) << " seconds");
 
@@ -1500,30 +1463,30 @@
     CBDATA_INIT_TYPE(idns_query);
 
     if (DnsSocketA < 0 && DnsSocketB < 0) {
-        Ip::Address addrA; // since we don't want to alter Config.Addrs.udp_* and dont have one of our own.
+        Ip::Address addrV6; // since we don't want to alter Config.Addrs.udp_* and dont have one of our own.
 
         if (!Config.Addrs.udp_outgoing.IsNoAddr())
-            addrA = Config.Addrs.udp_outgoing;
+            addrV6 = Config.Addrs.udp_outgoing;
         else
-            addrA = Config.Addrs.udp_incoming;
-
-        Ip::Address addrB = addrA;
-        addrA.SetIPv4();
-
-        if (Ip::EnableIpv6 && addrB.IsIPv6()) {
-            debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrB);
+            addrV6 = Config.Addrs.udp_incoming;
+
+        Ip::Address addrV4 = addrV6;
+        addrV4.SetIPv4();
+
+        if (Ip::EnableIpv6 && addrV6.IsIPv6()) {
+            debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrV6);
             DnsSocketB = comm_open_listener(SOCK_DGRAM,
                                             IPPROTO_UDP,
-                                            addrB,
+                                            addrV6,
                                             COMM_NONBLOCKING,
                                             "DNS Socket IPv6");
         }
 
-        if (addrA.IsIPv4()) {
-            debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrA);
+        if (addrV4.IsIPv4()) {
+            debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrV4);
             DnsSocketA = comm_open_listener(SOCK_DGRAM,
                                             IPPROTO_UDP,
-                                            addrA,
+                                            addrV4,
                                             COMM_NONBLOCKING,
                                             "DNS Socket IPv4");
         }
@@ -1536,12 +1499,12 @@
          */
         if (DnsSocketB >= 0) {
             comm_local_port(DnsSocketB);
-            debugs(78, 1, "DNS Socket created at " << addrB << ", FD " << DnsSocketB);
+            debugs(78, 1, "DNS Socket created at " << addrV6 << ", FD " << DnsSocketB);
             Comm::SetSelect(DnsSocketB, COMM_SELECT_READ, idnsRead, NULL, 0);
         }
         if (DnsSocketA >= 0) {
             comm_local_port(DnsSocketA);
-            debugs(78, 1, "DNS Socket created at " << addrA << ", FD " << DnsSocketA);
+            debugs(78, 1, "DNS Socket created at " << addrV4 << ", FD " << DnsSocketA);
             Comm::SetSelect(DnsSocketA, COMM_SELECT_READ, idnsRead, NULL, 0);
         }
     }
@@ -1629,6 +1592,7 @@
     q = cbdataAlloc(idns_query);
     // idns_query is POD so no constructors are called after allocation
     q->xact_id.change();
+    // no query_id on this instance.
 
     q->callback = callback;
 
@@ -1644,10 +1608,31 @@
 static void
 idnsCacheQuery(idns_query *q)
 {
-    q->hash.key = q->query.name;
+    q->hash.key = q->orig;
     hash_join(idns_lookup_hash, &q->hash);
 }
 
+static void
+idnsSendSlaveAAAAQuery(idns_query *master)
+{
+    idns_query *q = cbdataAlloc(idns_query);
+    memcpy(q->name, master->name, sizeof(q->name));
+    memcpy(q->orig, master->orig, sizeof(q->orig));
+    q->master = master;
+    q->query_id = idnsQueryID();
+    q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), q->query_id, &q->query, Config.dns.packet_max);
+    q->slave = master->slave;
+    
+    debugs(78, 3, "idnsALookup: buf is " << q->sz << " bytes for " << q->name <<
+           ", id = 0x" << std::hex << q->query_id);
+    if (!q->sz) {
+        cbdataFree(q);
+        return;
+    }
+    master->slave = q;
+    idnsSendQuery(q);
+}
+
 void
 idnsALookup(const char *name, IDNSCB * callback, void *data)
 {
@@ -1661,6 +1646,7 @@
     q = cbdataAlloc(idns_query);
     // idns_query is POD so no constructors are called after allocation
     q->xact_id.change();
+    q->query_id = idnsQueryID();
 
     for (i = 0; i < strlen(name); i++)
         if (name[i] == '.')
@@ -1682,14 +1668,8 @@
         debugs(78, 3, "idnsALookup: searchpath used for " << q->name);
     }
 
-    if (Ip::EnableIpv6) {
-        q->sz = rfc3596BuildAAAAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, Config.dns.packet_max);
-        q->need_A = true;
-    } else {
-        // see EDNS notes at top of file why this sends 0
-        q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), 0, &q->query, 0);
-        q->need_A = false;
-    }
+    // see EDNS notes at top of file why this sends 0
+    q->sz = rfc3596BuildAQuery(q->name, q->buf, sizeof(q->buf), q->query_id, &q->query, 0);
 
     if (q->sz < 0) {
         /* problem with query data -- query not sent */
@@ -1699,13 +1679,17 @@
     }
 
     debugs(78, 3, "idnsALookup: buf is " << q->sz << " bytes for " << q->name <<
-           ", id = 0x" << std::hex << q->msg_id);
+           ", id = 0x" << std::hex << q->query_id);
 
     q->callback = callback;
     q->callback_data = cbdataReference(data);
 
     idnsCacheQuery(q);
     idnsSendQuery(q);
+
+    if (Ip::EnableIpv6)
+        idnsSendSlaveAAAAQuery(q);
+
 }
 
 void
@@ -1721,6 +1705,7 @@
 
     // idns_query is POD so no constructors are called after allocation
     q->xact_id.change();
+    q->query_id = idnsQueryID();
 
     if (addr.IsIPv6()) {
         struct in6_addr addr6;
@@ -1733,9 +1718,6 @@
         q->sz = rfc3596BuildPTRQuery4(addr4, q->buf, sizeof(q->buf), 0, &q->query, 0);
     }
 
-    /* PTR does not do inbound A/AAAA */
-    q->need_A = false;
-
     if (q->sz < 0) {
         /* problem with query data -- query not sent */
         callback(data, NULL, 0, "Internal error");
@@ -1749,7 +1731,7 @@
     }
 
     debugs(78, 3, "idnsPTRLookup: buf is " << q->sz << " bytes for " << ip <<
-           ", id = 0x" << std::hex << q->msg_id);
+           ", id = 0x" << std::hex << q->query_id);
 
     q->callback = callback;
     q->callback_data = cbdataReference(data);

# Begin bundle
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

Reply via email to