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;
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");
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)
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));
}
int ns;
q->start_t = current_time;
- q->msg_id = idnsQueryID();
- rfc1035SetQueryID(q->buf, q->msg_id);
do {
ns = q->nsends % nns;
} 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);
}
nameservers[ns].nqueries++;
q->queue_t = current_time;
dlinkAdd(q, &q->lru, &lru_list);
+ q->pending = 1;
idnsTickleQueue();
}
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;
}
}
}
-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)
{
}
#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) {
return;
}
- dlinkDelete(&q->lru, &lru_list);
idnsRcodeCount(n, q->attempt);
if (n < 0) {
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);
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");
return;
}
- idnsCacheQuery(q);
idnsSendQuery(q);
+ if (Ip::EnableIpv6)
+ idnsSendSlaveAAAAQuery(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);
+ q->message = message;
+ q->ancount = n;
- // 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->master)
+ q = q->master;
- 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);
+ 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;
}
-
- 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);
+ /* 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;
+ }
}
-
- 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;
+ rfc1035MessageDestroy(&q2->message);
+ cbdataFree(q2);
}
- /* else initial results were empty. just use the final set as authoritative */
debugs(78, 6, HERE << "Sending " << n << " DNS results to caller.");
idnsCallback(q, message->answer, n, rfc1035ErrorMessage(n));
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);
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");
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;
+ addrV6 = Config.Addrs.udp_incoming;
- Ip::Address addrB = addrA;
- addrA.SetIPv4();
+ Ip::Address addrV4 = addrV6;
+ addrV4.SetIPv4();
- if (Ip::EnableIpv6 && addrB.IsIPv6()) {
- debugs(78, 2, "idnsInit: attempt open DNS socket to: " << addrB);
+ 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");
}
*/
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);
}
}
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;
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)
{
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] == '.')
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 */
}
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
// 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;
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");
}
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);