# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: henrik@henriknordstrom.net-20120106203035-\ # 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: squid3@treenet.co.nz-20120105141204-\ # 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 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); # Begin bundle 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