chiark / gitweb /
resolve: remove unused variable
[elogind.git] / src / resolve / resolved-dns-transaction.c
index 48da432a1200f94fb2b6105801e790aa24eaae8d..74b0634142be5fbd9fd141969402c27ac44617be 100644 (file)
@@ -78,7 +78,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
         assert(s);
         assert(q);
 
-        r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL, NULL);
+        r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
         if (r < 0)
                 return r;
 
@@ -118,8 +118,8 @@ static void dns_transaction_stop(DnsTransaction *t) {
 }
 
 static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
+        _cleanup_free_ char *pretty = NULL;
         DnsZoneItem *z;
-        Iterator i;
 
         assert(t);
         assert(p);
@@ -127,14 +127,33 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
         if (manager_our_packet(t->scope->manager, p) != 0)
                 return;
 
-        log_debug("Transaction on scope %s on %s/%s got tentative packet",
+        in_addr_to_string(p->family, &p->sender, &pretty);
+
+        log_debug("Transaction on scope %s on %s/%s got tentative packet from %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));
+                  t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
+                  pretty);
+
+        /* RFC 4795, Section 4.1 says that the peer with the
+         * lexicographically smaller IP address loses */
+        if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) {
+                log_debug("Peer has lexicographically larger IP address and thus lost in the conflict.");
+                return;
+        }
+
+        log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
 
         t->block_gc++;
-        SET_FOREACH(z, t->zone_items, i)
+        while ((z = set_first(t->zone_items))) {
+                /* First, make sure the zone item drops the reference
+                 * to us */
+                dns_zone_item_probe_stop(z);
+
+                /* Secondly, report this as conflict, so that we might
+                 * look for a different hostname */
                 dns_zone_item_conflict(z);
+        }
         t->block_gc--;
 
         dns_transaction_gc(t);
@@ -147,7 +166,9 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
 
         assert(t);
         assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
-        assert(IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
+
+        if (!IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING))
+                return;
 
         /* Note that this call might invalidate the query. Callers
          * should hence not attempt to access the query or transaction
@@ -194,6 +215,14 @@ static int on_stream_complete(DnsStream *s, int error) {
                 return 0;
         }
 
+        if (dns_packet_validate_reply(p) <= 0) {
+                log_debug("Invalid LLMNR TCP packet.");
+                dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
+                return 0;
+        }
+
+        dns_scope_check_conflicts(t->scope, p);
+
         t->block_gc++;
         dns_transaction_process_reply(t, p);
         t->block_gc--;
@@ -368,7 +397,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
         }
 
         /* 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);
+        dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
 
         if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
                 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
@@ -443,7 +472,7 @@ int dns_transaction_go(DnsTransaction *t) {
 
         dns_transaction_stop(t);
 
-        log_debug("Beginning transaction on scope %s on %s/%s",
+        log_debug("Excercising 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));
@@ -465,23 +494,56 @@ int dns_transaction_go(DnsTransaction *t) {
         t->cached = dns_answer_unref(t->cached);
         t->cached_rcode = 0;
 
-        /* Before trying the cache, let's make sure we figured out a
-         * server to use. Should this cause a change of server this
-         * might flush the cache. */
-        dns_scope_get_dns_server(t->scope);
+        /* Check the cache, but only if this transaction is not used
+         * for probing or verifying a zone item. */
+        if (set_isempty(t->zone_items)) {
 
-        /* Let's then prune all outdated entries */
-        dns_cache_prune(&t->scope->cache);
+                /* Before trying the cache, let's make sure we figured out a
+                 * server to use. Should this cause a change of server this
+                 * might flush the cache. */
+                dns_scope_get_dns_server(t->scope);
 
-        r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached);
-        if (r < 0)
-                return r;
-        if (r > 0) {
-                log_debug("Cache hit!");
-                if (t->cached_rcode == DNS_RCODE_SUCCESS)
-                        dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
-                else
-                        dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
+                /* Let's then prune all outdated entries */
+                dns_cache_prune(&t->scope->cache);
+
+                r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        log_debug("Cache hit!");
+                        if (t->cached_rcode == DNS_RCODE_SUCCESS)
+                                dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+                        else
+                                dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
+                        return 0;
+                }
+        }
+
+        if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) {
+                usec_t jitter;
+
+                /* RFC 4795 Section 2.7 suggests all queries should be
+                 * delayed by a random time from 0 to JITTER_INTERVAL. */
+
+                t->initial_jitter = true;
+
+                random_bytes(&jitter, sizeof(jitter));
+                jitter %= LLMNR_JITTER_INTERVAL_USEC;
+
+                r = sd_event_add_time(
+                                t->scope->manager->event,
+                                &t->timeout_event_source,
+                                clock_boottime_or_monotonic(),
+                                now(clock_boottime_or_monotonic()) + jitter,
+                                LLMNR_JITTER_INTERVAL_USEC,
+                                on_transaction_timeout, t);
+                if (r < 0)
+                        return r;
+
+                t->n_attempts = 0;
+                t->state = DNS_TRANSACTION_PENDING;
+
+                log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter);
                 return 0;
         }
 
@@ -508,7 +570,7 @@ int dns_transaction_go(DnsTransaction *t) {
                 r = dns_transaction_open_tcp(t);
         } else {
                 /* Try via UDP, and if that fails due to large size try via TCP */
-                r = dns_scope_send(t->scope, t->sent);
+                r = dns_scope_emit(t->scope, t->sent);
                 if (r == -EMSGSIZE)
                         r = dns_transaction_open_tcp(t);
         }
@@ -518,6 +580,11 @@ int dns_transaction_go(DnsTransaction *t) {
                 return 0;
         }
         if (r < 0) {
+                if (t->scope->protocol != DNS_PROTOCOL_DNS) {
+                        dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+                        return 0;
+                }
+
                 /* Couldn't send? Try immediately again, with a new server */
                 dns_scope_next_dns_server(t->scope);