[wpinet] Move network portions of wpiutil into new wpinet library (#4077)

This commit is contained in:
Peter Johnson
2022-05-07 10:54:14 -07:00
committed by GitHub
parent b33715db15
commit d673ead481
327 changed files with 1783 additions and 1179 deletions

View File

@@ -0,0 +1,118 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "AvahiClient.h"
#include <wpi/mutex.h>
#include <thread>
#include "dlfcn.h"
using namespace wpi;
#define AvahiFunctionLoad(snake_name) \
do { \
snake_name = \
reinterpret_cast<snake_name##_func>(dlsym(lib, "avahi_" #snake_name)); \
if (!snake_name) { \
return; \
} \
} while (false)
AvahiFunctionTable::AvahiFunctionTable() {
void* lib = dlopen("libavahi-common.so.3", RTLD_LAZY);
valid = false;
if (lib == nullptr) {
return;
}
AvahiFunctionLoad(threaded_poll_new);
AvahiFunctionLoad(threaded_poll_free);
AvahiFunctionLoad(threaded_poll_get);
AvahiFunctionLoad(threaded_poll_start);
AvahiFunctionLoad(threaded_poll_stop);
AvahiFunctionLoad(threaded_poll_lock);
AvahiFunctionLoad(threaded_poll_unlock);
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);
if (lib == nullptr) {
return;
}
AvahiFunctionLoad(client_new);
AvahiFunctionLoad(client_free);
AvahiFunctionLoad(service_browser_new);
AvahiFunctionLoad(service_browser_get_client);
AvahiFunctionLoad(service_browser_free);
AvahiFunctionLoad(service_resolver_new);
AvahiFunctionLoad(service_resolver_free);
AvahiFunctionLoad(entry_group_new);
AvahiFunctionLoad(entry_group_free);
AvahiFunctionLoad(entry_group_add_service_strlst);
AvahiFunctionLoad(entry_group_reset);
AvahiFunctionLoad(entry_group_is_empty);
AvahiFunctionLoad(entry_group_commit);
AvahiFunctionLoad(entry_group_get_client);
valid = true;
}
AvahiFunctionTable& AvahiFunctionTable::Get() {
static AvahiFunctionTable table;
return table;
}
static wpi::mutex ThreadLoopLock;
static std::weak_ptr<AvahiThread> ThreadLoop;
std::shared_ptr<AvahiThread> AvahiThread::Get() {
std::scoped_lock lock{ThreadLoopLock};
auto locked = ThreadLoop.lock();
if (!locked) {
locked = std::make_unique<AvahiThread>(private_init{});
ThreadLoop = locked;
}
return locked;
}
AvahiThread::AvahiThread(const private_init&) {
if (!table.IsValid()) {
return;
}
threadedPoll = table.threaded_poll_new();
table.threaded_poll_start(threadedPoll);
}
AvahiThread::~AvahiThread() noexcept {
if (!table.IsValid()) {
return;
}
if (threadedPoll) {
table.threaded_poll_stop(threadedPoll);
table.threaded_poll_free(threadedPoll);
}
}
void AvahiThread::lock() {
table.threaded_poll_lock(threadedPoll);
}
void AvahiThread::unlock() {
table.threaded_poll_unlock(threadedPoll);
}
const AvahiPoll* AvahiThread::GetPoll() const {
return table.threaded_poll_get(threadedPoll);
}

View File

@@ -0,0 +1,316 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#include <memory>
/***
This file is part of avahi.
avahi is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
avahi is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with avahi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
typedef struct AvahiPoll AvahiPoll;
typedef enum {
AVAHI_SERVER_INVALID,
AVAHI_SERVER_REGISTERING,
AVAHI_SERVER_RUNNING,
AVAHI_SERVER_COLLISION,
AVAHI_SERVER_FAILURE
} AvahiServerState;
typedef struct AvahiClient AvahiClient;
typedef enum {
AVAHI_CLIENT_S_REGISTERING = AVAHI_SERVER_REGISTERING,
AVAHI_CLIENT_S_RUNNING = AVAHI_SERVER_RUNNING,
AVAHI_CLIENT_S_COLLISION = AVAHI_SERVER_COLLISION,
AVAHI_CLIENT_FAILURE = 100,
AVAHI_CLIENT_CONNECTING = 101
} AvahiClientState;
typedef enum {
AVAHI_CLIENT_IGNORE_USER_CONFIG = 1,
AVAHI_CLIENT_NO_FAIL = 2
} AvahiClientFlags;
typedef void (*AvahiClientCallback)(AvahiClient* s, AvahiClientState state,
void* userdata);
typedef struct AvahiServiceBrowser AvahiServiceBrowser;
typedef int AvahiProtocol;
typedef int AvahiIfIndex;
typedef enum {
AVAHI_BROWSER_NEW,
AVAHI_BROWSER_REMOVE,
AVAHI_BROWSER_CACHE_EXHAUSTED,
AVAHI_BROWSER_ALL_FOR_NOW,
AVAHI_BROWSER_FAILURE
} AvahiBrowserEvent;
typedef enum {
AVAHI_LOOKUP_RESULT_CACHED = 1,
AVAHI_LOOKUP_RESULT_WIDE_AREA = 2,
AVAHI_LOOKUP_RESULT_MULTICAST = 4,
AVAHI_LOOKUP_RESULT_LOCAL = 8,
AVAHI_LOOKUP_RESULT_OUR_OWN = 16,
AVAHI_LOOKUP_RESULT_STATIC = 32
} AvahiLookupResultFlags;
typedef void (*AvahiServiceBrowserCallback)(
AvahiServiceBrowser* b, AvahiIfIndex interface, AvahiProtocol protocol,
AvahiBrowserEvent event, const char* name, const char* type,
const char* domain, AvahiLookupResultFlags flags, void* userdata);
typedef enum {
AVAHI_LOOKUP_USE_WIDE_AREA = 1,
AVAHI_LOOKUP_USE_MULTICAST = 2,
AVAHI_LOOKUP_NO_TXT = 4,
AVAHI_LOOKUP_NO_ADDRESS = 8
} AvahiLookupFlags;
typedef struct AvahiServiceResolver AvahiServiceResolver;
typedef enum {
AVAHI_RESOLVER_FOUND,
AVAHI_RESOLVER_FAILURE
} AvahiResolverEvent;
typedef struct AvahiIPv4Address {
uint32_t address;
} AvahiIPv4Address;
typedef struct AvahiIPv6Address {
uint8_t address[16];
} AvahiIPv6Address;
typedef struct AvahiAddress {
AvahiProtocol proto;
union {
AvahiIPv6Address ipv6;
AvahiIPv4Address ipv4;
uint8_t data[1];
} data;
} AvahiAddress;
typedef struct AvahiStringList {
struct AvahiStringList* next;
size_t size;
uint8_t text[1];
} AvahiStringList;
typedef void (*AvahiServiceResolverCallback)(
AvahiServiceResolver* r, AvahiIfIndex interface, AvahiProtocol protocol,
AvahiResolverEvent event, const char* name, const char* type,
const char* domain, const char* host_name, const AvahiAddress* a,
uint16_t port, AvahiStringList* txt, AvahiLookupResultFlags flags,
void* userdata);
typedef struct AvahiThreadedPoll AvahiThreadedPoll;
typedef struct AvahiEntryGroup AvahiEntryGroup;
typedef enum {
AVAHI_ENTRY_GROUP_UNCOMMITED,
AVAHI_ENTRY_GROUP_REGISTERING,
AVAHI_ENTRY_GROUP_ESTABLISHED,
AVAHI_ENTRY_GROUP_COLLISION,
AVAHI_ENTRY_GROUP_FAILURE
} AvahiEntryGroupState;
typedef void (*AvahiEntryGroupCallback)(AvahiEntryGroup* g,
AvahiEntryGroupState state,
void* userdata);
typedef enum {
AVAHI_PUBLISH_UNIQUE = 1,
AVAHI_PUBLISH_NO_PROBE = 2,
AVAHI_PUBLISH_NO_ANNOUNCE = 4,
AVAHI_PUBLISH_ALLOW_MULTIPLE = 8,
AVAHI_PUBLISH_NO_REVERSE = 16,
AVAHI_PUBLISH_NO_COOKIE = 32,
AVAHI_PUBLISH_UPDATE = 64,
AVAHI_PUBLISH_USE_WIDE_AREA = 128,
AVAHI_PUBLISH_USE_MULTICAST = 256
} AvahiPublishFlags;
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:
#define AvahiFunction(CapName, RetType, Parameters) \
using CapName##_func = RetType(*) Parameters; \
CapName##_func CapName = nullptr
AvahiFunction(threaded_poll_new, AvahiThreadedPoll*, (void));
AvahiFunction(threaded_poll_free, void, (AvahiThreadedPoll*));
AvahiFunction(threaded_poll_get, const AvahiPoll*, (AvahiThreadedPoll*));
AvahiFunction(threaded_poll_start, int, (AvahiThreadedPoll*));
AvahiFunction(threaded_poll_stop, int, (AvahiThreadedPoll*));
AvahiFunction(threaded_poll_lock, int, (AvahiThreadedPoll*));
AvahiFunction(threaded_poll_unlock, int, (AvahiThreadedPoll*));
AvahiFunction(client_new, AvahiClient*,
(const AvahiPoll* poll_api, AvahiClientFlags flags,
AvahiClientCallback callback, void* userdata, int* error));
AvahiFunction(client_free, void, (AvahiClient*));
AvahiFunction(service_browser_new, AvahiServiceBrowser*,
(AvahiClient * client, AvahiIfIndex interface,
AvahiProtocol protocol, const char* type, const char* domain,
AvahiLookupFlags flags, AvahiServiceBrowserCallback callback,
void* userdata));
AvahiFunction(service_browser_free, int, (AvahiServiceBrowser*));
AvahiFunction(service_resolver_new, AvahiServiceResolver*,
(AvahiClient * client, AvahiIfIndex interface,
AvahiProtocol protocol, const char* name, const char* type,
const char* domain, AvahiProtocol aprotocol,
AvahiLookupFlags flags, AvahiServiceResolverCallback callback,
void* userdata));
AvahiFunction(service_resolver_free, int, (AvahiServiceResolver*));
AvahiFunction(entry_group_new, AvahiEntryGroup*,
(AvahiClient*, AvahiEntryGroupCallback, void*));
AvahiFunction(entry_group_free, int, (AvahiEntryGroup*));
AvahiFunction(entry_group_add_service_strlst, int,
(AvahiEntryGroup * group, AvahiIfIndex interface,
AvahiProtocol protocol, AvahiPublishFlags flags,
const char* name, const char* type, const char* domain,
const char* host, uint16_t port, AvahiStringList*));
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));
AvahiFunction(string_list_free, void, (AvahiStringList*));
AvahiFunction(service_browser_get_client, AvahiClient*,
(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; }
static AvahiFunctionTable& Get();
private:
AvahiFunctionTable();
bool valid;
};
class AvahiThread {
private:
struct private_init {};
public:
explicit AvahiThread(const private_init&);
~AvahiThread() noexcept;
void lock();
const AvahiPoll* GetPoll() const;
void unlock();
static std::shared_ptr<AvahiThread> Get();
private:
AvahiThreadedPoll* threadedPoll;
AvahiFunctionTable& table = AvahiFunctionTable::Get();
};
} // namespace wpi

View File

@@ -0,0 +1,170 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpinet/MulticastServiceAnnouncer.h"
#include <vector>
#include <fmt/format.h>
#include <wpi/mutex.h>
#include "AvahiClient.h"
using namespace wpi;
struct MulticastServiceAnnouncer::Impl {
AvahiFunctionTable& table = AvahiFunctionTable::Get();
std::shared_ptr<AvahiThread> thread = AvahiThread::Get();
AvahiClient* client;
AvahiEntryGroup* group = nullptr;
std::string serviceName;
std::string serviceType;
int port;
AvahiStringList* stringList = nullptr;
~Impl() noexcept {
if (stringList != nullptr && table.IsValid()) {
table.string_list_free(stringList);
}
}
template <typename T>
Impl(std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<T, T>> txt);
};
template <typename T>
MulticastServiceAnnouncer::Impl::Impl(std::string_view serviceName,
std::string_view serviceType, int port,
wpi::span<const std::pair<T, T>> txt) {
if (!this->table.IsValid()) {
return;
}
this->serviceName = serviceName;
this->serviceType = serviceType;
this->port = port;
std::vector<std::string> txts;
for (auto&& i : txt) {
txts.push_back(fmt::format("{}={}", i.first, i.second));
}
std::vector<const char*> txtArr;
for (auto&& i : txts) {
txtArr.push_back(i.c_str());
}
this->stringList =
this->table.string_list_new_from_array(txtArr.data(), txtArr.size());
}
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<MulticastServiceAnnouncer::Impl*>(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, impl);
}
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;
}
}
}
static void ClientCallback(AvahiClient* client, AvahiClientState state,
void* userdata) {
MulticastServiceAnnouncer::Impl* impl =
reinterpret_cast<MulticastServiceAnnouncer::Impl*>(userdata);
if (state == AVAHI_CLIENT_S_RUNNING) {
RegisterService(client, impl);
} else if (state == AVAHI_CLIENT_S_COLLISION ||
state == AVAHI_CLIENT_S_REGISTERING) {
if (impl->group) {
impl->table.entry_group_reset(impl->group);
}
}
}
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<std::string, std::string>> txt) {
pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
}
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
std::string_view serviceName, std::string_view serviceType, int port,
wpi::span<const std::pair<std::string_view, std::string_view>> txt) {
pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
}
MulticastServiceAnnouncer::~MulticastServiceAnnouncer() noexcept {
Stop();
}
bool MulticastServiceAnnouncer::HasImplementation() const {
return pImpl->table.IsValid();
}
void MulticastServiceAnnouncer::Start() {
if (!pImpl->table.IsValid()) {
return;
}
std::scoped_lock lock{*pImpl->thread};
if (pImpl->client) {
return;
}
pImpl->client =
pImpl->table.client_new(pImpl->thread->GetPoll(), AVAHI_CLIENT_NO_FAIL,
ClientCallback, pImpl.get(), nullptr);
}
void MulticastServiceAnnouncer::Stop() {
if (!pImpl->table.IsValid()) {
return;
}
std::scoped_lock lock{*pImpl->thread};
if (pImpl->client) {
if (pImpl->group) {
pImpl->table.entry_group_free(pImpl->group);
pImpl->group = nullptr;
}
pImpl->table.client_free(pImpl->client);
pImpl->client = nullptr;
}
}

View File

@@ -0,0 +1,150 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpinet/MulticastServiceResolver.h"
#include <wpi/SmallString.h>
#include <wpi/StringExtras.h>
#include <wpi/mutex.h>
#include "AvahiClient.h"
using namespace wpi;
struct MulticastServiceResolver::Impl {
AvahiFunctionTable& table = AvahiFunctionTable::Get();
std::shared_ptr<AvahiThread> thread = AvahiThread::Get();
AvahiClient* client;
AvahiServiceBrowser* browser;
std::string serviceType;
MulticastServiceResolver* resolver;
void onFound(ServiceData&& data) {
resolver->PushData(std::forward<ServiceData>(data));
}
};
MulticastServiceResolver::MulticastServiceResolver(
std::string_view serviceType) {
pImpl = std::make_unique<Impl>();
pImpl->serviceType = serviceType;
pImpl->resolver = this;
}
MulticastServiceResolver::~MulticastServiceResolver() noexcept {
Stop();
}
bool MulticastServiceResolver::HasImplementation() const {
return pImpl->table.IsValid();
}
static void ResolveCallback(AvahiServiceResolver* r, AvahiIfIndex interface,
AvahiProtocol protocol, AvahiResolverEvent event,
const char* name, const char* type,
const char* domain, const char* host_name,
const AvahiAddress* address, uint16_t port,
AvahiStringList* txt, AvahiLookupResultFlags flags,
void* userdata) {
MulticastServiceResolver::Impl* impl =
reinterpret_cast<MulticastServiceResolver::Impl*>(userdata);
if (event == AVAHI_RESOLVER_FOUND) {
if (address->proto == AVAHI_PROTO_INET) {
AvahiStringList* strLst = txt;
MulticastServiceResolver::ServiceData data;
while (strLst != nullptr) {
std::string_view value{reinterpret_cast<const char*>(strLst->text),
strLst->size};
strLst = strLst->next;
size_t splitIndex = value.find('=');
if (splitIndex == value.npos) {
// Todo make this just do key
continue;
}
std::string_view key = wpi::substr(value, 0, splitIndex);
value =
wpi::substr(value, splitIndex + 1, value.size() - splitIndex - 1);
data.txt.emplace_back(std::pair<std::string, std::string>{key, value});
}
wpi::SmallString<256> outputHostName;
char label[256];
do {
impl->table.unescape_label(&host_name, label, sizeof(label));
if (label[0] == '\0') {
break;
}
outputHostName.append(label);
outputHostName.append(".");
} while (true);
data.ipv4Address = address->data.ipv4.address;
data.port = port;
data.serviceName = name;
data.hostName = outputHostName.string();
impl->onFound(std::move(data));
}
}
impl->table.service_resolver_free(r);
}
static void BrowseCallback(AvahiServiceBrowser* b, AvahiIfIndex interface,
AvahiProtocol protocol, AvahiBrowserEvent event,
const char* name, const char* type,
const char* domain, AvahiLookupResultFlags flags,
void* userdata) {
MulticastServiceResolver::Impl* impl =
reinterpret_cast<MulticastServiceResolver::Impl*>(userdata);
if (event == AVAHI_BROWSER_NEW) {
impl->table.service_resolver_new(
impl->table.service_browser_get_client(b), interface, protocol, name,
type, domain, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_MULTICAST,
ResolveCallback, userdata);
}
}
static void ClientCallback(AvahiClient* client, AvahiClientState state,
void* userdata) {
MulticastServiceResolver::Impl* impl =
reinterpret_cast<MulticastServiceResolver::Impl*>(userdata);
if (state == AVAHI_CLIENT_S_RUNNING) {
impl->browser = impl->table.service_browser_new(
client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, impl->serviceType.c_str(),
"local", AvahiLookupFlags::AVAHI_LOOKUP_USE_MULTICAST, BrowseCallback,
userdata);
}
}
void MulticastServiceResolver::Start() {
if (!pImpl->table.IsValid()) {
return;
}
std::scoped_lock lock{*pImpl->thread};
if (pImpl->client) {
return;
}
pImpl->client =
pImpl->table.client_new(pImpl->thread->GetPoll(), AVAHI_CLIENT_NO_FAIL,
ClientCallback, pImpl.get(), nullptr);
}
void MulticastServiceResolver::Stop() {
if (!pImpl->table.IsValid()) {
return;
}
std::scoped_lock lock{*pImpl->thread};
if (pImpl->client) {
if (pImpl->browser) {
pImpl->table.service_browser_free(pImpl->browser);
pImpl->browser = nullptr;
}
pImpl->table.client_free(pImpl->client);
pImpl->client = nullptr;
}
}