From 3dae2c9f756721a220f8b59c70306031568a3ff2 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Thu, 17 Dec 2015 11:21:09 -0800 Subject: Fix avahi host name resolution scenarios When host name collision is detected, avahi picks a new host name, however it fails to update any published services with the new name which leads to wrong IP address resolution for the service. As a work-around, when we detect an avahi-client restart, we just re-publish our service, so it picks up the new host name. BUG: 26237307 Change-Id: I980735890c62957e1bc433a07c319eff646c358c --- buffet/avahi_mdns_client.cc | 44 ++++++++++++++++++++++++++++++++++++++------ buffet/avahi_mdns_client.h | 8 +++++++- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/buffet/avahi_mdns_client.cc b/buffet/avahi_mdns_client.cc index 852f59a..c60b727 100644 --- a/buffet/avahi_mdns_client.cc +++ b/buffet/avahi_mdns_client.cc @@ -54,8 +54,9 @@ AvahiMdnsClient::AvahiMdnsClient() int ret = 0; - client_.reset(avahi_client_new(avahi_threaded_poll_get(thread_pool_.get()), - {}, nullptr, this, &ret)); + client_.reset(avahi_client_new( + avahi_threaded_poll_get(thread_pool_.get()), {}, + &AvahiMdnsClient::OnAvahiClientStateUpdate, this, &ret)); CHECK(client_) << avahi_strerror(ret); avahi_threaded_poll_start(thread_pool_.get()); @@ -71,14 +72,17 @@ AvahiMdnsClient::~AvahiMdnsClient() { avahi_threaded_poll_stop(thread_pool_.get()); } -// TODO(rginda): Report errors back to the caller. -// TODO(rginda): Support publishing more than one service. void AvahiMdnsClient::PublishService(const std::string& service_type, uint16_t port, const std::vector& txt) { CHECK(group_); CHECK_EQ("_privet._tcp", service_type); + if (prev_port_ == port && prev_service_type_ == service_type && + txt_records_ == txt) { + return; + } + // Create txt record. std::unique_ptr txt_list{ nullptr, &avahi_string_list_free}; @@ -95,8 +99,9 @@ void AvahiMdnsClient::PublishService(const std::string& service_type, } int ret = 0; + txt_records_ = txt; - if (prev_port_ == port && prev_type_ == service_type) { + if (prev_port_ == port && prev_service_type_ == service_type) { ret = avahi_entry_group_update_service_txt_strlst( group_.get(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, {}, service_name_.c_str(), service_type.c_str(), nullptr, txt_list.get()); @@ -104,7 +109,7 @@ void AvahiMdnsClient::PublishService(const std::string& service_type, CHECK_GE(ret, 0) << avahi_strerror(ret); } else { prev_port_ = port; - prev_type_ = service_type; + prev_service_type_ = service_type; avahi_entry_group_reset(group_.get()); CHECK(avahi_entry_group_is_empty(group_.get())); @@ -123,6 +128,33 @@ void AvahiMdnsClient::PublishService(const std::string& service_type, void AvahiMdnsClient::StopPublishing(const std::string& service_type) { CHECK(group_); avahi_entry_group_reset(group_.get()); + prev_service_type_.clear(); + prev_port_ = 0; + txt_records_.clear(); +} + +void AvahiMdnsClient::OnAvahiClientStateUpdate(AvahiClient* s, + AvahiClientState state, + void* userdata) { + // Avahi service has been re-initialized (probably due to host name conflict), + // so we need to republish the service if it has been previously published. + if (state == AVAHI_CLIENT_S_RUNNING) { + AvahiMdnsClient* self = static_cast(userdata); + self->RepublishService(); + } +} + +void AvahiMdnsClient::RepublishService() { + // If we don't have a service to publish, there is nothing else to do here. + if (prev_service_type_.empty()) + return; + + LOG(INFO) << "Republishing mDNS service"; + std::string service_type = std::move(prev_service_type_); + uint16_t port = prev_port_; + std::vector txt = std::move(txt_records_); + StopPublishing(service_type); + PublishService(service_type, port, txt); } } // namespace buffet diff --git a/buffet/avahi_mdns_client.h b/buffet/avahi_mdns_client.h index 405180d..6bc896e 100644 --- a/buffet/avahi_mdns_client.h +++ b/buffet/avahi_mdns_client.h @@ -40,9 +40,15 @@ class AvahiMdnsClient : public MdnsClient { void StopPublishing(const std::string& service_type) override; private: + static void OnAvahiClientStateUpdate(AvahiClient* s, + AvahiClientState state, + void* userdata); + void RepublishService(); + uint16_t prev_port_{0}; - std::string prev_type_; + std::string prev_service_type_; std::string service_name_; + std::vector txt_records_; std::unique_ptr thread_pool_{nullptr, &avahi_threaded_poll_free}; std::unique_ptr< ::AvahiClient, decltype(&avahi_client_free)> client_{ -- cgit v1.2.3