diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 2c23a0df77..6616bc4e0e 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -124,6 +124,8 @@ DnsScope* dns_scope_free(DnsScope *s) { ordered_hashmap_free(s->conflict_queue); sd_event_source_unref(s->conflict_event_source); + sd_event_source_unref(s->announce_event_source); + dns_cache_flush(&s->cache); dns_zone_flush(&s->zone); @@ -791,8 +793,15 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, /* Try to find an ongoing transaction that is a equal to the * specified question */ t = hashmap_get(scope->transactions_by_key, key); - if (!t) - return NULL; + if (!t) { + DnsResourceKey *key_any; + + key_any = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_ANY, dns_resource_key_name(key)); + t = hashmap_get(scope->transactions_by_key, key_any); + key_any = dns_resource_key_unref(key_any); + if (!t) + return NULL; + } /* Refuse reusing transactions that completed based on cached * data instead of a real packet, if that's requested. */ @@ -936,17 +945,19 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { assert(scope); assert(p); - if (p->protocol != DNS_PROTOCOL_LLMNR) + if (!IN_SET(p->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) return; if (DNS_PACKET_RRCOUNT(p) <= 0) return; - if (DNS_PACKET_LLMNR_C(p) != 0) - return; + if (p->protocol == DNS_PROTOCOL_LLMNR) { + if (DNS_PACKET_LLMNR_C(p) != 0) + return; - if (DNS_PACKET_LLMNR_T(p) != 0) - return; + if (DNS_PACKET_LLMNR_T(p) != 0) + return; + } if (manager_our_packet(scope->manager, p)) return; @@ -1049,3 +1060,76 @@ int dns_scope_ifindex(DnsScope *s) { return 0; } + +static int on_announcement_timeout(sd_event_source *s, usec_t usec, void *userdata) { + DnsScope *scope = userdata; + + assert(s); + + scope->announce_event_source = sd_event_source_unref(scope->announce_event_source); + + dns_scope_announce(scope); + return 0; +} + +void dns_scope_announce(DnsScope *scope) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + LinkAddress *a; + int r; + + if (!scope) + return; + + if (scope->protocol != DNS_PROTOCOL_MDNS) + return; + + answer = dns_answer_new(4); + LIST_FOREACH(addresses, a, scope->link->addresses) { + r = dns_answer_add(answer, a->mdns_address_rr, 0); + if (r < 0) { + log_debug_errno(r, "Failed to add address RR to answer: %m"); + return; + } + r = dns_answer_add(answer, a->mdns_ptr_rr, 0); + if (r < 0) { + log_debug_errno(r, "Failed to add PTR RR to answer: %m"); + return; + } + } + + if (dns_answer_isempty(answer)) + return; + + r = dns_scope_make_reply_packet(scope, 0, DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &p); + if (r < 0) { + log_debug_errno(r, "Failed to build reply packet: %m"); + return; + } + r = dns_scope_emit_udp(scope, -1, p); + if (r < 0) { + log_debug_errno(r, "Failed to send reply packet: %m"); + return; + } + + /* In section 8.3 of RFC6762: "The Multicast DNS responder MUST send at least two unsolicited + * responses, one second apart." */ + if (!scope->announced) { + usec_t ts; + + scope->announced = true; + + assert_se(sd_event_now(scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); + ts += MDNS_ANNOUNCE_DELAY; + + r = sd_event_add_time( + scope->manager->event, + &scope->announce_event_source, + clock_boottime_or_monotonic(), + ts, + MDNS_JITTER_RANGE_USEC, + on_announcement_timeout, scope); + if (r < 0) + log_debug_errno(r, "Failed to schedule second announcement: %m"); + } +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index d501835e45..65b61c56aa 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -56,6 +56,9 @@ struct DnsScope { OrderedHashmap *conflict_queue; sd_event_source *conflict_event_source; + bool announced:1; + sd_event_source *announce_event_source; + RateLimit ratelimit; usec_t resend_timeout; @@ -113,3 +116,5 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name); bool dns_scope_network_good(DnsScope *s); int dns_scope_ifindex(DnsScope *s); + +void dns_scope_announce(DnsScope *scope); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 03d25dd984..bd36e5e1a1 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -363,6 +363,9 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items) dns_zone_item_notify(z); SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done); + if (t->probing) { + dns_scope_announce(t->scope); + } SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions) dns_transaction_notify(d, t); @@ -1209,7 +1212,10 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) { case DNS_PROTOCOL_MDNS: assert(t->n_attempts > 0); - return (1 << (t->n_attempts - 1)) * USEC_PER_SEC; + if (t->probing) + return MDNS_PROBING_INTERVAL_USEC; + else + return (1 << (t->n_attempts - 1)) * USEC_PER_SEC; case DNS_PROTOCOL_LLMNR: return t->scope->resend_timeout; diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 5a1df70422..c4fb51958d 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -78,6 +78,8 @@ struct DnsTransaction { bool clamp_ttl:1; + bool probing:1; + DnsPacket *sent, *received; DnsAnswer *answer; @@ -172,10 +174,20 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_; #define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC) #define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC) +/* mDNS probing interval, see RFC 6762 Section 8.1 */ +#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC) + /* Maximum attempts to send DNS requests, across all DNS servers */ #define DNS_TRANSACTION_ATTEMPTS_MAX 16 /* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */ #define LLMNR_TRANSACTION_ATTEMPTS_MAX 3 -#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX) +/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */ +#define MDNS_TRANSACTION_ATTEMPTS_MAX 3 + +#define TRANSACTION_ATTEMPTS_MAX(p) (((p) == DNS_PROTOCOL_LLMNR) ? \ + LLMNR_TRANSACTION_ATTEMPTS_MAX : \ + (((p) == DNS_PROTOCOL_MDNS) ? \ + MDNS_TRANSACTION_ATTEMPTS_MAX : \ + DNS_TRANSACTION_ATTEMPTS_MAX)) diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 746a979f47..ad024b54f5 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -196,6 +196,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { goto gc; i->probe_transaction = t; + t->probing = true; if (t->state == DNS_TRANSACTION_NULL) { diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h index 5d274648f4..06bd3296be 100644 --- a/src/resolve/resolved-mdns.h +++ b/src/resolve/resolved-mdns.h @@ -22,6 +22,7 @@ #include "resolved-manager.h" #define MDNS_PORT 5353 +#define MDNS_ANNOUNCE_DELAY (1 * USEC_PER_SEC) int manager_mdns_ipv4_fd(Manager *m); int manager_mdns_ipv6_fd(Manager *m);