mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-20 00:51:42 +00:00
[wpiutil] Add mDNS resolver and announcer (#3733)
This commit is contained in:
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