mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
[wpiutil] Add mDNS resolver and announcer (#3733)
This commit is contained in:
115
wpiutil/src/main/native/linux/AvahiClient.cpp
Normal file
115
wpiutil/src/main/native/linux/AvahiClient.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
249
wpiutil/src/main/native/linux/AvahiClient.h
Normal file
249
wpiutil/src/main/native/linux/AvahiClient.h
Normal file
@@ -0,0 +1,249 @@
|
||||
// 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 };
|
||||
|
||||
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(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));
|
||||
|
||||
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
|
||||
154
wpiutil/src/main/native/linux/MulticastServiceAnnouncer.cpp
Normal file
154
wpiutil/src/main/native/linux/MulticastServiceAnnouncer.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
// 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 "wpi/MulticastServiceAnnouncer.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "AvahiClient.h"
|
||||
#include "fmt/format.h"
|
||||
#include "wpi/mutex.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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void EntryGroupCallback(AvahiEntryGroup*, AvahiEntryGroupState, void*) {}
|
||||
|
||||
static void RegisterService(AvahiClient* client,
|
||||
MulticastServiceAnnouncer::Impl* impl) {
|
||||
if (impl->group == nullptr) {
|
||||
impl->group =
|
||||
impl->table.entry_group_new(client, EntryGroupCallback, nullptr);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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>();
|
||||
|
||||
if (!pImpl->table.IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pImpl->serviceName = serviceName;
|
||||
pImpl->serviceType = serviceType;
|
||||
pImpl->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());
|
||||
}
|
||||
|
||||
pImpl->stringList =
|
||||
pImpl->table.string_list_new_from_array(txtArr.data(), txtArr.size());
|
||||
}
|
||||
|
||||
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>();
|
||||
|
||||
if (!pImpl->table.IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pImpl->serviceName = serviceName;
|
||||
pImpl->serviceType = serviceType;
|
||||
pImpl->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());
|
||||
}
|
||||
|
||||
pImpl->stringList =
|
||||
pImpl->table.string_list_new_from_array(txtArr.data(), txtArr.size());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
148
wpiutil/src/main/native/linux/MulticastServiceResolver.cpp
Normal file
148
wpiutil/src/main/native/linux/MulticastServiceResolver.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
// 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 "wpi/MulticastServiceResolver.h"
|
||||
|
||||
#include "AvahiClient.h"
|
||||
#include "wpi/SmallString.h"
|
||||
#include "wpi/mutex.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->eventQueue.push(std::move(data));
|
||||
resolver->event.Set();
|
||||
}
|
||||
};
|
||||
|
||||
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 = value.substr(0, splitIndex);
|
||||
value = value.substr(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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user