# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: henrik@henriknordstrom.net-20120106204121-\ # zvhpl72emejmxec3 # target_branch: http://www.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: 632f737bae5da0d5043420fa85eaa9e795102325 # timestamp: 2012-01-11 02:19:56 +0100 # base_revision_id: squid3@treenet.co.nz-20120105141204-\ # wm58j1qdf5zuz6zz # # Begin patch === modified file 'src/cf.data.pre' --- src/cf.data.pre 2012-01-03 02:19:30 +0000 +++ src/cf.data.pre 2012-01-06 20:41:21 +0000 @@ -7238,27 +7238,6 @@ nameservers by setting this option to 'off'. DOC_END -NAME: dns_v4_fallback -TYPE: onoff -DEFAULT: on -LOC: Config.onoff.dns_require_A -IFDEF: !USE_DNSHELPER -DOC_START - Standard practice with DNS is to lookup either A or AAAA records - and use the results if it succeeds. Only looking up the other if - the first attempt fails or otherwise produces no results. - - That policy however will cause squid to produce error pages for some - servers that advertise AAAA but are unreachable over IPv6. - - If this is ON squid will always lookup both AAAA and A, using both. - If this is OFF squid will lookup AAAA and only try A if none found. - - WARNING: There are some possibly unwanted side-effects with this on: - *) Doubles the load placed by squid on the DNS network. - *) May negatively impact connection delay times. -DOC_END - NAME: dns_v4_first TYPE: onoff DEFAULT: off === 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 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(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(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); === modified file 'src/structs.h' --- src/structs.h 2012-01-03 02:19:30 +0000 +++ src/structs.h 2012-01-06 20:41:21 +0000 @@ -445,7 +445,6 @@ int emailErrData; int httpd_suppress_version_string; int global_internal_static; - int dns_require_A; #if FOLLOW_X_FORWARDED_FOR int acl_uses_indirect_client; # Begin bundle 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