From f6e9fc7d71dce0474d90d14e0e69049c757a9fc8 Mon Sep 17 00:00:00 2001 From: Thad House Date: Fri, 26 Nov 2021 23:20:54 -0800 Subject: [PATCH] [wpiutil] Handle multicast service collision on linux (#3734) --- wpiutil/src/main/native/linux/AvahiClient.cpp | 3 + wpiutil/src/main/native/linux/AvahiClient.h | 67 +++++++++++++++++++ .../linux/MulticastServiceAnnouncer.cpp | 47 ++++++++++--- 3 files changed, 107 insertions(+), 10 deletions(-) diff --git a/wpiutil/src/main/native/linux/AvahiClient.cpp b/wpiutil/src/main/native/linux/AvahiClient.cpp index 5bb9ca061e..6a17ea0876 100644 --- a/wpiutil/src/main/native/linux/AvahiClient.cpp +++ b/wpiutil/src/main/native/linux/AvahiClient.cpp @@ -40,6 +40,8 @@ AvahiFunctionTable::AvahiFunctionTable() { AvahiFunctionLoad(string_list_new_from_array); AvahiFunctionLoad(string_list_free); AvahiFunctionLoad(unescape_label); + AvahiFunctionLoad(alternative_service_name); + AvahiFunctionLoad(free); lib = dlopen("libavahi-client.so.3", RTLD_LAZY); @@ -60,6 +62,7 @@ AvahiFunctionTable::AvahiFunctionTable() { AvahiFunctionLoad(entry_group_reset); AvahiFunctionLoad(entry_group_is_empty); AvahiFunctionLoad(entry_group_commit); + AvahiFunctionLoad(entry_group_get_client); valid = true; } diff --git a/wpiutil/src/main/native/linux/AvahiClient.h b/wpiutil/src/main/native/linux/AvahiClient.h index d747162541..ca679bb41f 100644 --- a/wpiutil/src/main/native/linux/AvahiClient.h +++ b/wpiutil/src/main/native/linux/AvahiClient.h @@ -159,6 +159,70 @@ enum { AVAHI_IF_UNSPEC = -1 }; enum { AVAHI_PROTO_INET = 0, AVAHI_PROTO_INET6 = 1, AVAHI_PROTO_UNSPEC = -1 }; +enum { + AVAHI_OK = 0, + AVAHI_ERR_FAILURE = -1, + AVAHI_ERR_BAD_STATE = -2, + AVAHI_ERR_INVALID_HOST_NAME = -3, + AVAHI_ERR_INVALID_DOMAIN_NAME = -4, + AVAHI_ERR_NO_NETWORK = -5, + AVAHI_ERR_INVALID_TTL = -6, + AVAHI_ERR_IS_PATTERN = -7, + AVAHI_ERR_COLLISION = -8, + AVAHI_ERR_INVALID_RECORD = -9, + + AVAHI_ERR_INVALID_SERVICE_NAME = -10, + AVAHI_ERR_INVALID_SERVICE_TYPE = -11, + AVAHI_ERR_INVALID_PORT = -12, + AVAHI_ERR_INVALID_KEY = -13, + AVAHI_ERR_INVALID_ADDRESS = -14, + AVAHI_ERR_TIMEOUT = -15, + AVAHI_ERR_TOO_MANY_CLIENTS = -16, + AVAHI_ERR_TOO_MANY_OBJECTS = -17, + AVAHI_ERR_TOO_MANY_ENTRIES = -18, + AVAHI_ERR_OS = -19, + + AVAHI_ERR_ACCESS_DENIED = -20, + AVAHI_ERR_INVALID_OPERATION = -21, + AVAHI_ERR_DBUS_ERROR = -22, + AVAHI_ERR_DISCONNECTED = -23, + AVAHI_ERR_NO_MEMORY = -24, + AVAHI_ERR_INVALID_OBJECT = -25, + AVAHI_ERR_NO_DAEMON = -26, + AVAHI_ERR_INVALID_INTERFACE = -27, + AVAHI_ERR_INVALID_PROTOCOL = -28, + AVAHI_ERR_INVALID_FLAGS = -29, + + AVAHI_ERR_NOT_FOUND = -30, + AVAHI_ERR_INVALID_CONFIG = -31, + AVAHI_ERR_VERSION_MISMATCH = -32, + AVAHI_ERR_INVALID_SERVICE_SUBTYPE = -33, + AVAHI_ERR_INVALID_PACKET = -34, + AVAHI_ERR_INVALID_DNS_ERROR = -35, + AVAHI_ERR_DNS_FORMERR = -36, + AVAHI_ERR_DNS_SERVFAIL = -37, + AVAHI_ERR_DNS_NXDOMAIN = -38, + AVAHI_ERR_DNS_NOTIMP = -39, + + AVAHI_ERR_DNS_REFUSED = -40, + AVAHI_ERR_DNS_YXDOMAIN = -41, + AVAHI_ERR_DNS_YXRRSET = -42, + AVAHI_ERR_DNS_NXRRSET = -43, + AVAHI_ERR_DNS_NOTAUTH = -44, + AVAHI_ERR_DNS_NOTZONE = -45, + AVAHI_ERR_INVALID_RDATA = -46, + AVAHI_ERR_INVALID_DNS_CLASS = -47, + AVAHI_ERR_INVALID_DNS_TYPE = -48, + AVAHI_ERR_NOT_SUPPORTED = -49, + + AVAHI_ERR_NOT_PERMITTED = -50, + AVAHI_ERR_INVALID_ARGUMENT = -51, + AVAHI_ERR_IS_EMPTY = -52, + AVAHI_ERR_NO_CHANGE = -53, + + AVAHI_ERR_MAX = -54 +}; + namespace wpi { class AvahiFunctionTable { public: @@ -208,6 +272,7 @@ class AvahiFunctionTable { AvahiFunction(entry_group_reset, int, (AvahiEntryGroup*)); AvahiFunction(entry_group_is_empty, int, (AvahiEntryGroup*)); AvahiFunction(entry_group_commit, int, (AvahiEntryGroup*)); + AvahiFunction(entry_group_get_client, AvahiClient*, (AvahiEntryGroup*)); AvahiFunction(string_list_new_from_array, AvahiStringList*, (const char** array, int len)); @@ -217,6 +282,8 @@ class AvahiFunctionTable { (AvahiServiceBrowser*)); AvahiFunction(unescape_label, char*, (const char**, char*, size_t)); + AvahiFunction(alternative_service_name, char*, (const char*)); + AvahiFunction(free, void, (void*)); bool IsValid() const { return valid; } diff --git a/wpiutil/src/main/native/linux/MulticastServiceAnnouncer.cpp b/wpiutil/src/main/native/linux/MulticastServiceAnnouncer.cpp index 0df050b171..da60910535 100644 --- a/wpiutil/src/main/native/linux/MulticastServiceAnnouncer.cpp +++ b/wpiutil/src/main/native/linux/MulticastServiceAnnouncer.cpp @@ -29,22 +29,49 @@ struct MulticastServiceAnnouncer::Impl { } }; -static void EntryGroupCallback(AvahiEntryGroup*, AvahiEntryGroupState, void*) {} +static void RegisterService(AvahiClient* client, + MulticastServiceAnnouncer::Impl* impl); + +static void EntryGroupCallback(AvahiEntryGroup* group, + AvahiEntryGroupState state, void* userdata) { + if (state == AVAHI_ENTRY_GROUP_COLLISION) { + // Remote collision + MulticastServiceAnnouncer::Impl* impl = + reinterpret_cast(userdata); + char* newName = + impl->table.alternative_service_name(impl->serviceName.c_str()); + impl->serviceName = newName; + impl->table.free(newName); + RegisterService(impl->table.entry_group_get_client(group), impl); + } +} static void RegisterService(AvahiClient* client, MulticastServiceAnnouncer::Impl* impl) { if (impl->group == nullptr) { - impl->group = - impl->table.entry_group_new(client, EntryGroupCallback, nullptr); + impl->group = impl->table.entry_group_new(client, EntryGroupCallback, impl); } - if (impl->table.entry_group_is_empty(impl->group)) { - impl->table.entry_group_add_service_strlst( - impl->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, - AVAHI_PUBLISH_USE_MULTICAST, impl->serviceName.c_str(), - impl->serviceType.c_str(), "local", nullptr, impl->port, - impl->stringList); - impl->table.entry_group_commit(impl->group); + while (true) { + if (impl->table.entry_group_is_empty(impl->group)) { + auto ret = impl->table.entry_group_add_service_strlst( + impl->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + AVAHI_PUBLISH_USE_MULTICAST, impl->serviceName.c_str(), + impl->serviceType.c_str(), "local", nullptr, impl->port, + impl->stringList); + if (ret == AVAHI_ERR_COLLISION) { + // Local collision + char* newName = + impl->table.alternative_service_name(impl->serviceName.c_str()); + impl->serviceName = newName; + impl->table.free(newName); + continue; + } else if (ret != AVAHI_OK) { + break; + } + impl->table.entry_group_commit(impl->group); + break; + } } }