resolved: most DNS servers can't handle more than one question per packet, hence let's not generate that

This commit is contained in:
Lennart Poettering 2014-07-23 01:59:36 +02:00
parent 7e8e0422ae
commit 934e9b10b4
4 changed files with 134 additions and 30 deletions

View File

@ -138,3 +138,40 @@ int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **r
return 0;
}
DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b) {
_cleanup_(dns_answer_unrefp) DnsAnswer *ret = NULL;
DnsAnswer *k;
unsigned i;
int r;
if (a && (!b || b->n_rrs <= 0))
return dns_answer_ref(a);
if ((!a || a->n_rrs <= 0) && b)
return dns_answer_ref(b);
ret = dns_answer_new((a ? a->n_rrs : 0) + (b ? b->n_rrs : 0));
if (!ret)
return NULL;
if (a) {
for (i = 0; i < a->n_rrs; i++) {
r = dns_answer_add(ret, a->rrs[i]);
if (r < 0)
return NULL;
}
}
if (b) {
for (i = 0; i < b->n_rrs; i++) {
r = dns_answer_add(ret, b->rrs[i]);
if (r < 0)
return NULL;
}
}
k = ret;
ret = NULL;
return k;
}

View File

@ -41,4 +41,6 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr);
int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key);
int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret);
DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);

View File

@ -335,8 +335,9 @@ void dns_query_transaction_process_reply(DnsQueryTransaction *t, DnsPacket *p) {
if (r < 0) {
dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
return;
} else
dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, 0);
}
dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, 0);
if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
@ -575,7 +576,8 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
return 0;
}
static int dns_query_add_transaction(DnsQuery *q, DnsScope *s) {
static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
DnsQueryTransaction *t;
int r;
@ -585,12 +587,23 @@ static int dns_query_add_transaction(DnsQuery *q, DnsScope *s) {
if (r < 0)
return r;
if (key) {
question = dns_question_new(1);
if (!question)
return -ENOMEM;
r = dns_question_add(question, key);
if (r < 0)
return r;
} else
question = dns_question_ref(q->question);
LIST_FOREACH(transactions_by_scope, t, s->transactions)
if (dns_question_is_superset(t->question, q->question))
if (dns_question_is_superset(t->question, question))
break;
if (!t) {
r = dns_query_transaction_new(&t, s, q->question);
r = dns_query_transaction_new(&t, s, question);
if (r < 0)
return r;
}
@ -616,6 +629,33 @@ fail:
return r;
}
static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
int r;
assert(q);
assert(s);
if (s->protocol == DNS_PROTOCOL_MDNS) {
r = dns_query_add_transaction(q, s, NULL);
if (r < 0)
return r;
} else {
unsigned i;
/* On DNS and LLMNR we can only send a single
* question per datagram, hence issue multiple
* transactions. */
for (i = 0; i < q->question->n_keys; i++) {
r = dns_query_add_transaction(q, s, q->question->keys[i]);
if (r < 0)
return r;
}
}
return 0;
}
int dns_query_go(DnsQuery *q) {
DnsScopeMatch found = DNS_SCOPE_NO;
DnsScope *s, *first = NULL;
@ -660,7 +700,7 @@ int dns_query_go(DnsQuery *q) {
if (found == DNS_SCOPE_NO)
return -ESRCH;
r = dns_query_add_transaction(q, first);
r = dns_query_add_transaction_split(q, first);
if (r < 0)
return r;
@ -674,7 +714,7 @@ int dns_query_go(DnsQuery *q) {
if (match != found)
continue;
r = dns_query_add_transaction(q, s);
r = dns_query_add_transaction_split(q, s);
if (r < 0)
return r;
}
@ -711,8 +751,9 @@ fail:
void dns_query_ready(DnsQuery *q) {
DnsQueryTransaction *t;
DnsQueryState state = DNS_QUERY_NO_SERVERS;
DnsAnswer *failure_answer = NULL;
int failure_rcode = 0, failure_ifindex = 0;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
int rcode = 0;
DnsScope *scope = NULL;
Iterator i;
assert(q);
@ -728,6 +769,10 @@ void dns_query_ready(DnsQuery *q) {
SET_FOREACH(t, q->transactions, i) {
/* If we found a successful answer, ignore all answers from other scopes */
if (state == DNS_QUERY_SUCCESS && t->scope != scope)
continue;
/* One of the transactions is still going on, let's wait for it */
if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
return;
@ -735,34 +780,55 @@ void dns_query_ready(DnsQuery *q) {
/* One of the transactions is successful, let's use
* it, and copy its data out */
if (t->state == DNS_QUERY_SUCCESS) {
DnsAnswer *a;
if (t->received) {
q->answer = dns_answer_ref(t->received->answer);
q->answer_ifindex = t->received->ifindex;
q->answer_rcode = DNS_PACKET_RCODE(t->received);
rcode = DNS_PACKET_RCODE(t->received);
a = t->received->answer;
} else {
q->answer = dns_answer_ref(t->cached);
q->answer_ifindex = t->scope->link ? t->scope->link->ifindex : 0;
q->answer_rcode = t->cached_rcode;
rcode = t->cached_rcode;
a = t->cached;
}
dns_query_complete(q, DNS_QUERY_SUCCESS);
return;
if (state == DNS_QUERY_SUCCESS) {
DnsAnswer *merged;
merged = dns_answer_merge(answer, a);
if (!merged) {
dns_query_complete(q, DNS_QUERY_RESOURCES);
return;
}
dns_answer_unref(answer);
answer = merged;
} else {
dns_answer_unref(answer);
answer = dns_answer_ref(a);
}
scope = t->scope;
state = DNS_QUERY_SUCCESS;
continue;
}
/* One of the transactions has failed, let's see
* whether we find anything better, but if not, return
* its response packet */
if (t->state == DNS_QUERY_FAILURE) {
* its response data */
if (state != DNS_QUERY_SUCCESS && t->state == DNS_QUERY_FAILURE) {
DnsAnswer *a;
if (t->received) {
failure_answer = t->received->answer;
failure_ifindex = t->received->ifindex;
failure_rcode = DNS_PACKET_RCODE(t->received);
rcode = DNS_PACKET_RCODE(t->received);
a = t->received->answer;
} else {
failure_answer = t->cached;
failure_ifindex = t->scope->link ? t->scope->link->ifindex : 0;
failure_rcode = t->cached_rcode;
rcode = t->cached_rcode;
a = t->cached;
}
dns_answer_unref(answer);
answer = dns_answer_ref(a);
scope = t->scope;
state = DNS_QUERY_FAILURE;
continue;
}
@ -771,10 +837,10 @@ void dns_query_ready(DnsQuery *q) {
state = t->state;
}
if (state == DNS_QUERY_FAILURE) {
q->answer = dns_answer_ref(failure_answer);
q->answer_ifindex = failure_ifindex;
q->answer_rcode = failure_rcode;
if (IN_SET(state, DNS_QUERY_SUCCESS, DNS_QUERY_FAILURE)) {
q->answer = dns_answer_ref(answer);
q->answer_rcode = rcode;
q->answer_ifindex = (scope && scope->link) ? scope->link->ifindex : 0;
}
dns_query_complete(q, state);

View File

@ -90,7 +90,6 @@ struct DnsQuery {
sd_event_source *timeout_event_source;
/* Discovered data */
DnsPacket *received;
DnsAnswer *answer;
int answer_ifindex;
int answer_rcode;