X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-query.c;h=22770023047fdd3cd18b84cd42c9bccb2b15459f;hb=e4501ed4e66e31f5bc6e40046ac8f27ce6e13a5c;hp=ecffe069592ac1a7c60cd88bc72bd5b49101c6d5;hpb=ea917db9e662ae6e6d0ae07e0118b323688c8616;p=elogind.git diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index ecffe0695..227700230 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -19,15 +19,32 @@ along with systemd; If not, see . ***/ +#include "af-list.h" + #include "resolved-dns-query.h" #include "resolved-dns-domain.h" -#define TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC) +/* After how much time to repeat classic DNS requests */ +#define DNS_TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC) + +/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */ +#define LLMNR_TRANSACTION_TIMEOUT_USEC (1 * USEC_PER_SEC) + +/* How long to wait for the query in total */ #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC) -#define ATTEMPTS_MAX 8 + +/* Maximum attempts to send DNS requests, across all DNS servers */ +#define DNS_TRANSACTION_ATTEMPTS_MAX 8 + +/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */ +#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3 + #define CNAME_MAX 8 #define QUERIES_MAX 2048 +#define TRANSACTION_TIMEOUT_USEC(p) ((t)->scope->protocol == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_TIMEOUT_USEC : DNS_TRANSACTION_TIMEOUT_USEC) +#define TRANSACTION_ATTEMPTS_MAX(p) ((t)->scope->protocol == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX) + static int dns_query_transaction_go(DnsQueryTransaction *t); DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) { @@ -132,6 +149,12 @@ void dns_query_transaction_complete(DnsQueryTransaction *t, DnsQueryState state) * should hence not attempt to access the query or transaction * after calling this function. */ + log_debug("Transaction on scope %s on %s/%s now complete with %s", + dns_protocol_to_string(t->scope->protocol), + t->scope->link ? t->scope->link->name : "*", + t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family), + dns_query_state_to_string(state)); + t->state = state; dns_query_transaction_stop(t); @@ -261,17 +284,6 @@ void dns_query_transaction_process_reply(DnsQueryTransaction *t, DnsPacket *p) { if (p->family != t->scope->family) return; - /* Don't accept UDP packets directed to anything but - * the LLMNR multicast addresses. */ - - if (p->ipproto == IPPROTO_UDP) { - if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS)) - return; - - if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS)) - return; - } - /* Tentative replies shall be discarded, see RFC 4795, * 2.1.1 */ @@ -347,7 +359,8 @@ void dns_query_transaction_process_reply(DnsQueryTransaction *t, DnsPacket *p) { return; } - dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, 0); + /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ + dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0); if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) dns_query_transaction_complete(t, DNS_QUERY_SUCCESS); @@ -413,13 +426,28 @@ static int dns_query_make_packet(DnsQueryTransaction *t) { } static int dns_query_transaction_go(DnsQueryTransaction *t) { + bool had_stream; int r; assert(t); + had_stream = !!t->stream; + dns_query_transaction_stop(t); - if (t->n_attempts >= ATTEMPTS_MAX) { + log_debug("Beginning transaction on scope %s on %s/%s", + dns_protocol_to_string(t->scope->protocol), + t->scope->link ? t->scope->link->name : "*", + t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family)); + + if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t)) { + dns_query_transaction_complete(t, DNS_QUERY_ATTEMPTS_MAX); + return 0; + } + + if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) { + /* If we already tried via a stream, then we don't + * retry on LLMNR. See RFC 4795, Section 2.7. */ dns_query_transaction_complete(t, DNS_QUERY_ATTEMPTS_MAX); return 0; } @@ -479,7 +507,7 @@ static int dns_query_transaction_go(DnsQueryTransaction *t) { return dns_query_transaction_go(t); } - r = sd_event_add_time(t->scope->manager->event, &t->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + TRANSACTION_TIMEOUT_USEC, 0, on_transaction_timeout, t); + r = sd_event_add_time(t->scope->manager->event, &t->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + TRANSACTION_TIMEOUT_USEC(t), 0, on_transaction_timeout, t); if (r < 0) return r; @@ -539,10 +567,13 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question) { q->question = dns_question_ref(question); for (i = 0; i < question->n_keys; i++) { - log_debug("Looking up RR for %s %s %s", - strna(dns_class_to_string(question->keys[i]->class)), - strna(dns_type_to_string(question->keys[i]->type)), - DNS_RESOURCE_KEY_NAME(question->keys[i])); + _cleanup_free_ char *p; + + r = dns_resource_key_to_string(question->keys[i], &p); + if (r < 0) + return r; + + log_debug("Looking up RR for %s", p); } LIST_PREPEND(queries, m->dns_queries, q); @@ -773,6 +804,7 @@ void dns_query_ready(DnsQuery *q) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; int rcode = 0; DnsScope *scope = NULL; + bool pending = false; Iterator i; assert(q); @@ -792,9 +824,11 @@ void dns_query_ready(DnsQuery *q) { 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; + /* One of the transactions is still going on, let's maybe wait for it */ + if (IN_SET(t->state, DNS_QUERY_PENDING, DNS_QUERY_NULL)) { + pending = true; + continue; + } /* One of the transactions is successful, let's use * it, and copy its data out */ @@ -856,6 +890,21 @@ void dns_query_ready(DnsQuery *q) { state = t->state; } + if (pending) { + + /* If so far we weren't successful, and there's + * something still pending, then wait for it */ + if (state != DNS_QUERY_SUCCESS) + return; + + /* If we already were successful, then only wait for + * other transactions on the same scope to finish. */ + SET_FOREACH(t, q->transactions, i) { + if (t->scope == scope && IN_SET(t->state, DNS_QUERY_PENDING, DNS_QUERY_NULL)) + return; + } + } + if (IN_SET(state, DNS_QUERY_SUCCESS, DNS_QUERY_FAILURE)) { q->answer = dns_answer_ref(answer); q->answer_rcode = rcode; @@ -889,3 +938,17 @@ int dns_query_cname_redirect(DnsQuery *q, const char *name) { return 0; } + +static const char* const dns_query_state_table[_DNS_QUERY_STATE_MAX] = { + [DNS_QUERY_NULL] = "null", + [DNS_QUERY_PENDING] = "pending", + [DNS_QUERY_FAILURE] = "failure", + [DNS_QUERY_SUCCESS] = "success", + [DNS_QUERY_NO_SERVERS] = "no-servers", + [DNS_QUERY_TIMEOUT] = "timeout", + [DNS_QUERY_ATTEMPTS_MAX] = "attempts-max", + [DNS_QUERY_INVALID_REPLY] = "invalid-reply", + [DNS_QUERY_RESOURCES] = "resources", + [DNS_QUERY_ABORTED] = "aborted", +}; +DEFINE_STRING_TABLE_LOOKUP(dns_query_state, DnsQueryState);