X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-transaction.c;h=74b0634142be5fbd9fd141969402c27ac44617be;hp=48da432a1200f94fb2b6105801e790aa24eaae8d;hb=30fbcf24460263961855e0ce4d8867fd2af3e149;hpb=2c27fbca2d88214bd305272308a370a962818f1e diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 48da432a1..74b063414 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -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);