From 3e0080355b4b89bfab98eec17bddb05e005d9fc1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 3 Sep 2026 08:11:07 +0900 Subject: [PATCH] resolve: detach DnsServiceBrowser object from Manager when freed or disconnected When a DnsServiceBrowser object is detached from the manager, all queries, event sources, and then the reference to Manager should be dropped. Otherwise, spurious query may be triggered for already disconnected varlink. (cherry picked from commit 219abd95aeb3fff17cf26103abf8dae99887911d) --- src/resolve/resolved-dns-browse-services.c | 46 ++++++++++++++++------ src/resolve/resolved-dns-browse-services.h | 2 +- src/resolve/resolved-manager.c | 3 -- src/resolve/resolved-varlink.c | 27 +++++-------- 4 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/resolve/resolved-dns-browse-services.c b/src/resolve/resolved-dns-browse-services.c index ca9b54075f..df360fd4d6 100644 --- a/src/resolve/resolved-dns-browse-services.c +++ b/src/resolve/resolved-dns-browse-services.c @@ -160,6 +160,7 @@ int dns_add_new_service(DnsServiceBrowser *sb, DnsResourceRecord *rr, int owner_ int r; assert(sb); + assert(sb->manager); assert(rr); s = new(DnssdDiscoveredService, 1); @@ -725,6 +726,7 @@ static int mdns_next_query_schedule(sd_event_source *s, uint64_t usec, void *use assert(userdata); assert_se(sb = dns_service_browser_ref(userdata)); + assert(sb->manager); /* If the varlink connection has a userdata, then that means the previous query has not been finished. */ if (!sd_varlink_get_userdata(sb->link)) { @@ -804,6 +806,14 @@ void dns_browse_services_restart(Manager *m) { } } +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + dns_service_browser_hash_ops, + void, + trivial_hash_func, + trivial_compare_func, + DnsServiceBrowser, + dns_service_browser_detach); + int dns_subscribe_browse_service( Manager *m, sd_varlink *link, const char *domain, const char *type, int ifindex, uint64_t flags) { @@ -855,8 +865,6 @@ int dns_subscribe_browse_service( *sb = (DnsServiceBrowser) { .n_ref = 1, - .manager = m, - .link = sd_varlink_ref(link), .question_utf8 = dns_question_ref(question_utf8), .question_idna = dns_question_ref(question_idna), .key = dns_question_first_key(question_utf8), @@ -880,35 +888,51 @@ int dns_subscribe_browse_service( if (r < 0) return r; - r = hashmap_ensure_put(&m->dns_service_browsers, NULL, link, sb); + r = hashmap_ensure_put(&m->dns_service_browsers, &dns_service_browser_hash_ops, link, sb); if (r < 0) return log_error_errno(r, "Failed to add service browser to the hashmap: %m"); + sb->manager = m; + sb->link = sd_varlink_ref(link); TAKE_PTR(sb); return 0; } -DnsServiceBrowser *dns_service_browser_free(DnsServiceBrowser *sb) { - DnsQuery *q; - - if (!sb) - return NULL; +static DnsServiceBrowser* dns_service_browser_detach_impl(DnsServiceBrowser *sb) { + assert(sb); + assert(!sb->manager == !sb->link); while (sb->dns_services) dns_remove_service(sb, sb->dns_services); sb->schedule_event = sd_event_source_disable_unref(sb->schedule_event); - q = sd_varlink_get_userdata(sb->link); + if (!sb->manager) + return NULL; /* already detached */ + + DnsQuery *q = sd_varlink_get_userdata(sb->link); if (q && DNS_TRANSACTION_IS_LIVE(q->state)) dns_query_complete(q, DNS_TRANSACTION_ABORTED); + hashmap_remove(sb->manager->dns_service_browsers, sb->link); + sb->link = sd_varlink_unref(sb->link); + sb->manager = NULL; + return sb; /* indicate that the object is detached */ +} + +void dns_service_browser_detach(DnsServiceBrowser *sb) { + dns_service_browser_unref(dns_service_browser_detach_impl(sb)); +} + +static DnsServiceBrowser* dns_service_browser_free(DnsServiceBrowser *sb) { + if (!sb) + return NULL; + + dns_service_browser_detach_impl(sb); sb->question_idna = dns_question_unref(sb->question_idna); sb->question_utf8 = dns_question_unref(sb->question_utf8); - sb->link = sd_varlink_unref(sb->link); - return mfree(sb); } diff --git a/src/resolve/resolved-dns-browse-services.h b/src/resolve/resolved-dns-browse-services.h index 84095d5559..8a3afa200e 100644 --- a/src/resolve/resolved-dns-browse-services.h +++ b/src/resolve/resolved-dns-browse-services.h @@ -52,10 +52,10 @@ struct DnsServiceBrowser { LIST_HEAD(DnssdDiscoveredService, dns_services); }; -DnsServiceBrowser *dns_service_browser_free(DnsServiceBrowser *sb); void dns_remove_service(DnsServiceBrowser *sb, DnssdDiscoveredService *service); DnssdDiscoveredService *dns_service_free(DnssdDiscoveredService *service); +void dns_service_browser_detach(DnsServiceBrowser *sb); DECLARE_TRIVIAL_REF_UNREF_FUNC(DnsServiceBrowser, dns_service_browser); DECLARE_TRIVIAL_REF_UNREF_FUNC(DnssdDiscoveredService, dnssd_discovered_service); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 10b3b5977f..8997ba0c35 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -841,7 +841,6 @@ int manager_start(Manager *m) { Manager* manager_free(Manager *m) { Link *l; DnssdRegisteredService *s; - DnsServiceBrowser *sb; if (!m) return NULL; @@ -923,8 +922,6 @@ Manager* manager_free(Manager *m) { manager_etc_hosts_flush(m); manager_static_records_flush(m); - while ((sb = hashmap_first(m->dns_service_browsers))) - dns_service_browser_free(sb); hashmap_free(m->dns_service_browsers); hashmap_free(m->hooks); diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c index d29bd92ac5..5d5cdc172b 100644 --- a/src/resolve/resolved-varlink.c +++ b/src/resolve/resolved-varlink.c @@ -157,28 +157,19 @@ static int reply_query_state(DnsQuery *q) { } static void vl_on_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) { - DnsQuery *q; - Manager *m; - assert(s); assert(link); - m = sd_varlink_server_get_userdata(s); - if (!m) - return; + DnsQuery *q = sd_varlink_get_userdata(link); + if (q && DNS_TRANSACTION_IS_LIVE(q->state)) { + log_debug("Client of active query vanished, aborting query."); + dns_query_complete(q, DNS_TRANSACTION_ABORTED); + } - DnsServiceBrowser *sb = hashmap_remove(m->dns_service_browsers, link); - dns_service_browser_unref(sb); - - q = sd_varlink_get_userdata(link); - if (!q) - return; - - if (!DNS_TRANSACTION_IS_LIVE(q->state)) - return; - - log_debug("Client of active query vanished, aborting query."); - dns_query_complete(q, DNS_TRANSACTION_ABORTED); + Manager *m = ASSERT_PTR(sd_varlink_server_get_userdata(s)); + DnsServiceBrowser *sb = hashmap_get(m->dns_service_browsers, link); + if (sb) + dns_service_browser_detach(sb); } static void vl_on_notification_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) {