[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

@@ -1,41 +0,0 @@
// 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 "DynamicDns.h"
using namespace wpi;
DynamicDns& DynamicDns::GetDynamicDns() {
static DynamicDns dns;
return dns;
}
DynamicDns::DynamicDns() {
HMODULE library = LoadLibraryW(L"dnsapi");
if (library == nullptr) {
return;
}
DnsServiceFreeInstancePtr = (DnsServiceFreeInstanceFunc)GetProcAddress(
library, "DnsServiceFreeInstance");
DnsServiceConstructInstancePtr =
(DnsServiceConstructInstanceFunc)GetProcAddress(
library, "DnsServiceConstructInstance");
DnsServiceRegisterPtr =
(DnsServiceRegisterFunc)GetProcAddress(library, "DnsServiceRegister");
DnsServiceDeRegisterPtr =
(DnsServiceDeRegisterFunc)GetProcAddress(library, "DnsServiceDeRegister");
CanDnsAnnounce = DnsServiceFreeInstancePtr &&
DnsServiceConstructInstancePtr && DnsServiceRegisterPtr &&
DnsServiceDeRegisterPtr;
DnsServiceBrowsePtr =
(DnsServiceBrowseFunc)GetProcAddress(library, "DnsServiceBrowse");
DnsServiceBrowseCancelPtr = (DnsServiceBrowseCancelFunc)GetProcAddress(
library, "DnsServiceBrowseCancel");
CanDnsResolve = DnsServiceBrowsePtr && DnsServiceBrowseCancelPtr;
}

View File

@@ -1,59 +0,0 @@
// 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
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <WinDNS.h>
namespace wpi {
class DynamicDns {
public:
using DnsServiceFreeInstanceFunc =
VOID(WINAPI*)(_In_ PDNS_SERVICE_INSTANCE pInstance);
using DnsServiceConstructInstanceFunc = PDNS_SERVICE_INSTANCE(WINAPI*)(
_In_ PCWSTR pServiceName, _In_ PCWSTR pHostName,
_In_opt_ PIP4_ADDRESS pIp4, _In_opt_ PIP6_ADDRESS pIp6, _In_ WORD wPort,
_In_ WORD wPriority, _In_ WORD wWeight, _In_ DWORD dwPropertiesCount,
_In_reads_(dwPropertiesCount) PCWSTR* keys,
_In_reads_(dwPropertiesCount) PCWSTR* values);
using DnsServiceRegisterFunc =
DWORD(WINAPI*)(_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest,
_Inout_opt_ PDNS_SERVICE_CANCEL pCancel);
using DnsServiceDeRegisterFunc =
DWORD(WINAPI*)(_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest,
_Inout_opt_ PDNS_SERVICE_CANCEL pCancel);
using DnsServiceBrowseFunc =
DNS_STATUS(WINAPI*)(_In_ PDNS_SERVICE_BROWSE_REQUEST pRequest,
_Inout_ PDNS_SERVICE_CANCEL pCancel);
using DnsServiceBrowseCancelFunc =
DNS_STATUS(WINAPI*)(_In_ PDNS_SERVICE_CANCEL pCancelHandle);
DnsServiceBrowseFunc DnsServiceBrowsePtr{nullptr};
DnsServiceBrowseCancelFunc DnsServiceBrowseCancelPtr{nullptr};
DnsServiceFreeInstanceFunc DnsServiceFreeInstancePtr{nullptr};
DnsServiceConstructInstanceFunc DnsServiceConstructInstancePtr{nullptr};
DnsServiceRegisterFunc DnsServiceRegisterPtr{nullptr};
DnsServiceDeRegisterFunc DnsServiceDeRegisterPtr{nullptr};
bool CanDnsAnnounce{false};
bool CanDnsResolve{false};
static DynamicDns& GetDynamicDns();
private:
DynamicDns();
};
} // namespace wpi

View File

@@ -1,212 +0,0 @@
// 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.
#ifndef UNICODE
#define UNICODE
#endif
#include "wpi/MulticastServiceAnnouncer.h"
#include <string>
#include <vector>
#include "DynamicDns.h"
#include "wpi/ConvertUTF.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
#include "wpi/StringExtras.h"
#include "wpi/hostname.h"
using namespace wpi;
struct ImplBase {
wpi::DynamicDns& dynamicDns = wpi::DynamicDns::GetDynamicDns();
PDNS_SERVICE_INSTANCE serviceInstance = nullptr;
HANDLE event = nullptr;
};
struct MulticastServiceAnnouncer::Impl : ImplBase {
std::wstring serviceType;
std::wstring serviceInstanceName;
std::wstring hostName;
int port;
std::vector<std::wstring> keys;
std::vector<PCWSTR> keyPtrs;
std::vector<std::wstring> values;
std::vector<PCWSTR> valuePtrs;
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 (!dynamicDns.CanDnsAnnounce) {
return;
}
this->port = port;
wpi::SmallVector<wchar_t, 128> wideStorage;
std::string hostName = wpi::GetHostname() + ".local";
for (auto&& i : txt) {
wideStorage.clear();
wpi::sys::windows::UTF8ToUTF16(i.first, wideStorage);
this->keys.emplace_back(
std::wstring{wideStorage.data(), wideStorage.size()});
wideStorage.clear();
wpi::sys::windows::UTF8ToUTF16(i.second, wideStorage);
this->values.emplace_back(
std::wstring{wideStorage.data(), wideStorage.size()});
}
for (size_t i = 0; i < this->keys.size(); i++) {
this->keyPtrs.emplace_back(this->keys[i].c_str());
this->valuePtrs.emplace_back(this->values[i].c_str());
}
wpi::SmallString<128> storage;
wideStorage.clear();
wpi::sys::windows::UTF8ToUTF16(hostName, wideStorage);
this->hostName = std::wstring{wideStorage.data(), wideStorage.size()};
wideStorage.clear();
if (wpi::ends_with_lower(serviceType, ".local")) {
wpi::sys::windows::UTF8ToUTF16(serviceType, wideStorage);
} else {
storage.clear();
storage.append(serviceType);
storage.append(".local");
wpi::sys::windows::UTF8ToUTF16(storage.str(), wideStorage);
}
this->serviceType = std::wstring{wideStorage.data(), wideStorage.size()};
wideStorage.clear();
storage.clear();
storage.append(serviceName);
storage.append(".");
storage.append(serviceType);
if (!wpi::ends_with_lower(serviceType, ".local")) {
storage.append(".local");
}
wpi::sys::windows::UTF8ToUTF16(storage.str(), wideStorage);
this->serviceInstanceName =
std::wstring{wideStorage.data(), wideStorage.size()};
}
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->dynamicDns.CanDnsAnnounce;
}
static void WINAPI DnsServiceRegisterCallback(DWORD /*Status*/,
PVOID pQueryContext,
PDNS_SERVICE_INSTANCE pInstance) {
ImplBase* impl = reinterpret_cast<ImplBase*>(pQueryContext);
impl->serviceInstance = pInstance;
SetEvent(impl->event);
}
void MulticastServiceAnnouncer::Start() {
if (pImpl->serviceInstance) {
return;
}
if (!pImpl->dynamicDns.CanDnsAnnounce) {
return;
}
PDNS_SERVICE_INSTANCE serviceInst =
pImpl->dynamicDns.DnsServiceConstructInstancePtr(
pImpl->serviceInstanceName.c_str(), pImpl->hostName.c_str(), nullptr,
nullptr, pImpl->port, 0, 0, static_cast<DWORD>(pImpl->keyPtrs.size()),
pImpl->keyPtrs.data(), pImpl->valuePtrs.data());
if (serviceInst == nullptr) {
return;
}
DNS_SERVICE_REGISTER_REQUEST registerRequest = {};
registerRequest.pQueryContext = static_cast<ImplBase*>(pImpl.get());
registerRequest.pRegisterCompletionCallback = DnsServiceRegisterCallback;
registerRequest.Version = DNS_QUERY_REQUEST_VERSION1;
registerRequest.unicastEnabled = false;
registerRequest.pServiceInstance = serviceInst;
registerRequest.InterfaceIndex = 0;
pImpl->event = CreateEvent(NULL, true, false, NULL);
if (pImpl->dynamicDns.DnsServiceRegisterPtr(&registerRequest, nullptr) ==
DNS_REQUEST_PENDING) {
WaitForSingleObject(pImpl->event, INFINITE);
}
pImpl->dynamicDns.DnsServiceFreeInstancePtr(serviceInst);
CloseHandle(pImpl->event);
pImpl->event = nullptr;
}
static void WINAPI DnsServiceDeRegisterCallback(
DWORD /*Status*/, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) {
ImplBase* impl = reinterpret_cast<ImplBase*>(pQueryContext);
if (pInstance != nullptr) {
impl->dynamicDns.DnsServiceFreeInstancePtr(pInstance);
pInstance = nullptr;
}
SetEvent(impl->event);
}
void MulticastServiceAnnouncer::Stop() {
if (!pImpl->dynamicDns.CanDnsAnnounce) {
return;
}
if (pImpl->serviceInstance == nullptr) {
return;
}
pImpl->event = CreateEvent(NULL, true, false, NULL);
DNS_SERVICE_REGISTER_REQUEST registerRequest = {};
registerRequest.pQueryContext = static_cast<ImplBase*>(pImpl.get());
registerRequest.pRegisterCompletionCallback = DnsServiceDeRegisterCallback;
registerRequest.Version = DNS_QUERY_REQUEST_VERSION1;
registerRequest.unicastEnabled = false;
registerRequest.pServiceInstance = pImpl->serviceInstance;
registerRequest.InterfaceIndex = 0;
if (pImpl->dynamicDns.DnsServiceDeRegisterPtr(&registerRequest, nullptr) ==
DNS_REQUEST_PENDING) {
WaitForSingleObject(pImpl->event, INFINITE);
}
pImpl->dynamicDns.DnsServiceFreeInstancePtr(pImpl->serviceInstance);
pImpl->serviceInstance = nullptr;
CloseHandle(pImpl->event);
pImpl->event = nullptr;
}

View File

@@ -1,205 +0,0 @@
// 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.
#ifndef UNICODE
#define UNICODE
#endif
#include "wpi/MulticastServiceResolver.h"
#include <string>
#include "DynamicDns.h"
#include "wpi/ConvertUTF.h"
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
#include "wpi/StringExtras.h"
#pragma comment(lib, "dnsapi")
using namespace wpi;
struct MulticastServiceResolver::Impl {
wpi::DynamicDns& dynamicDns = wpi::DynamicDns::GetDynamicDns();
std::wstring serviceType;
DNS_SERVICE_CANCEL serviceCancel{nullptr};
MulticastServiceResolver* resolver;
void onFound(ServiceData&& data) {
resolver->PushData(std::forward<ServiceData>(data));
}
};
MulticastServiceResolver::MulticastServiceResolver(
std::string_view serviceType) {
pImpl = std::make_unique<Impl>();
pImpl->resolver = this;
if (!pImpl->dynamicDns.CanDnsResolve) {
return;
}
wpi::SmallVector<wchar_t, 128> wideStorage;
if (wpi::ends_with_lower(serviceType, ".local")) {
wpi::sys::windows::UTF8ToUTF16(serviceType, wideStorage);
} else {
wpi::SmallString<128> storage;
storage.append(serviceType);
storage.append(".local");
wpi::sys::windows::UTF8ToUTF16(storage.str(), wideStorage);
}
pImpl->serviceType = std::wstring{wideStorage.data(), wideStorage.size()};
}
MulticastServiceResolver::~MulticastServiceResolver() noexcept {
Stop();
}
bool MulticastServiceResolver::HasImplementation() const {
return pImpl->dynamicDns.CanDnsResolve;
}
static _Function_class_(DNS_QUERY_COMPLETION_ROUTINE) VOID WINAPI
DnsCompletion(_In_ PVOID pQueryContext,
_Inout_ PDNS_QUERY_RESULT pQueryResults) {
MulticastServiceResolver::Impl* impl =
reinterpret_cast<MulticastServiceResolver::Impl*>(pQueryContext);
wpi::SmallVector<DNS_RECORDW*, 4> PtrRecords;
wpi::SmallVector<DNS_RECORDW*, 4> SrvRecords;
wpi::SmallVector<DNS_RECORDW*, 4> TxtRecords;
wpi::SmallVector<DNS_RECORDW*, 4> ARecords;
{
DNS_RECORDW* current = pQueryResults->pQueryRecords;
while (current != nullptr) {
switch (current->wType) {
case DNS_TYPE_PTR:
PtrRecords.push_back(current);
break;
case DNS_TYPE_SRV:
SrvRecords.push_back(current);
break;
case DNS_TYPE_TEXT:
TxtRecords.push_back(current);
break;
case DNS_TYPE_A:
ARecords.push_back(current);
break;
}
current = current->pNext;
}
}
for (DNS_RECORDW* Ptr : PtrRecords) {
if (std::wstring_view{Ptr->pName} != impl->serviceType) {
continue;
}
std::wstring_view nameHost = Ptr->Data.Ptr.pNameHost;
DNS_RECORDW* foundSrv = nullptr;
for (DNS_RECORDW* Srv : SrvRecords) {
if (std::wstring_view{Srv->pName} == nameHost) {
foundSrv = Srv;
break;
}
}
if (!foundSrv) {
continue;
}
for (DNS_RECORDW* A : ARecords) {
if (std::wstring_view{A->pName} ==
std::wstring_view{foundSrv->Data.Srv.pNameTarget}) {
MulticastServiceResolver::ServiceData data;
wpi::SmallString<128> storage;
for (DNS_RECORDW* Txt : TxtRecords) {
if (std::wstring_view{Txt->pName} == nameHost) {
for (DWORD i = 0; i < Txt->Data.Txt.dwStringCount; i++) {
std::wstring_view wideView = Txt->Data.TXT.pStringArray[i];
size_t splitIndex = wideView.find(L'=');
if (splitIndex == wideView.npos) {
// Todo make this just do key
continue;
}
storage.clear();
wpi::span<const wpi::UTF16> wideStr{
reinterpret_cast<const wpi::UTF16*>(wideView.data()),
splitIndex};
wpi::convertUTF16ToUTF8String(wideStr, storage);
auto& pair = data.txt.emplace_back(
std::pair<std::string, std::string>{storage.string(), {}});
storage.clear();
wideStr = wpi::span<const wpi::UTF16>{
reinterpret_cast<const wpi::UTF16*>(wideView.data() +
splitIndex + 1),
wideView.size() - splitIndex - 1};
wpi::convertUTF16ToUTF8String(wideStr, storage);
pair.second = storage.string();
}
}
}
storage.clear();
wpi::span<const wpi::UTF16> wideHostName{
reinterpret_cast<const wpi::UTF16*>(A->pName), wcslen(A->pName)};
wpi::convertUTF16ToUTF8String(wideHostName, storage);
storage.append(".");
data.hostName = storage.string();
storage.clear();
int len = nameHost.find(impl->serviceType.c_str());
wpi::span<const wpi::UTF16> wideServiceName{
reinterpret_cast<const wpi::UTF16*>(nameHost.data()),
nameHost.size()};
if (len != nameHost.npos) {
wideServiceName = wideServiceName.subspan(0, len - 1);
}
wpi::convertUTF16ToUTF8String(wideServiceName, storage);
data.serviceName = storage.string();
data.port = foundSrv->Data.Srv.wPort;
data.ipv4Address = A->Data.A.IpAddress;
impl->onFound(std::move(data));
}
}
}
DnsFree(pQueryResults->pQueryRecords, DNS_FREE_TYPE::DnsFreeRecordList);
}
void MulticastServiceResolver::Start() {
if (pImpl->serviceCancel.reserved != nullptr) {
return;
}
if (!pImpl->dynamicDns.CanDnsResolve) {
return;
}
DNS_SERVICE_BROWSE_REQUEST request = {};
request.InterfaceIndex = 0;
request.pQueryContext = pImpl.get();
request.QueryName = pImpl->serviceType.c_str();
request.Version = 2;
request.pBrowseCallbackV2 = DnsCompletion;
pImpl->dynamicDns.DnsServiceBrowsePtr(&request, &pImpl->serviceCancel);
}
void MulticastServiceResolver::Stop() {
if (!pImpl->dynamicDns.CanDnsResolve) {
return;
}
if (pImpl->serviceCancel.reserved == nullptr) {
return;
}
pImpl->dynamicDns.DnsServiceBrowseCancelPtr(&pImpl->serviceCancel);
pImpl->serviceCancel.reserved = nullptr;
}