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
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 @@
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 @@
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<std::string>& 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<AvahiStringList, decltype(&avahi_string_list_free)> txt_list{
nullptr, &avahi_string_list_free};
@@ -95,8 +99,9 @@
}
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 @@
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::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<AvahiMdnsClient*>(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<std::string> 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 @@
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<std::string> txt_records_;
std::unique_ptr<AvahiThreadedPoll, decltype(&avahi_threaded_poll_free)>
thread_pool_{nullptr, &avahi_threaded_poll_free};
std::unique_ptr< ::AvahiClient, decltype(&avahi_client_free)> client_{