From 0cc7e35ed9774f800ad49ae4d3dd58be108abc41 Mon Sep 17 00:00:00 2001 From: loki Date: Mon, 28 Jun 2021 21:05:52 +0200 Subject: [PATCH] Automatic service discovery for Windows --- CMakeLists.txt | 2 + README.md | 2 +- appveyor.yml | 2 +- sunshine/main.cpp | 7 +- sunshine/network.cpp | 4 +- sunshine/platform/windows/publish.cpp | 194 ++++++++++++++++++++++++++ 6 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 sunshine/platform/windows/publish.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e3a9269a..7af013a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ if(WIN32) include_directories(third-party/ViGEmClient/include) set(PLATFORM_TARGET_FILES + sunshine/platform/windows/publish.cpp sunshine/platform/windows/misc.cpp sunshine/platform/windows/input.cpp sunshine/platform/windows/display.h @@ -88,6 +89,7 @@ if(WIN32) iphlpapi d3d11 dxgi D3DCompiler setupapi + dnsapi ) set_source_files_properties(third-party/ViGEmClient/src/ViGEmClient.cpp PROPERTIES COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650") diff --git a/README.md b/README.md index 2c4b3584..0806c972 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Sunshine is a Gamestream host for Moonlight Ubuntu 20.04: Install the following ``` -sudo apt install cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev libavahi-client-dev +sudo apt install cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev ``` ### Compilation: diff --git a/appveyor.yml b/appveyor.yml index ae6ca859..66851a1b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ environment: install: - sh: sudo apt update --ignore-missing - - sh: sudo apt install -y build-essential fakeroot gcc-10 g++-10 cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev libavahi-client-dev + - sh: sudo apt install -y build-essential fakeroot gcc-10 g++-10 cmake libssl-dev libavdevice-dev libboost-thread-dev libboost-filesystem-dev libboost-log-dev libpulse-dev libopus-dev libxtst-dev libx11-dev libxrandr-dev libxfixes-dev libevdev-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev - cmd: C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -S mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-opus mingw-w64-x86_64-x265 mingw-w64-x86_64-boost git yasm nasm diffutils make" before_build: diff --git a/sunshine/main.cpp b/sunshine/main.cpp index e7478629..912da577 100644 --- a/sunshine/main.cpp +++ b/sunshine/main.cpp @@ -208,6 +208,11 @@ int main(int argc, char *argv[]) { return 3; } + std::unique_ptr unregister; + auto sync = std::async(std::launch::async, [&unregister]() { + unregister = platf::publish::start(); + }); + //FIXME: Temporary workaround: Simple-Web_server needs to be updated or replaced if(shutdown_event->peek()) { return 0; @@ -215,9 +220,9 @@ int main(int argc, char *argv[]) { task_pool.start(1); - auto deinit = platf::publish::start(); std::thread httpThread { nvhttp::start }; std::thread configThread { confighttp::start }; + stream::rtpThread(); httpThread.join(); diff --git a/sunshine/network.cpp b/sunshine/network.cpp index 28e031ef..ccf9e32f 100644 --- a/sunshine/network.cpp +++ b/sunshine/network.cpp @@ -6,8 +6,8 @@ #include "utility.h" #include -namespace net { using namespace std::literals; +namespace net { // In the format "xxx.xxx.xxx.xxx/x" std::pair ip_block(const std::string_view &ip); @@ -16,7 +16,7 @@ std::vector> pc_ips { }; std::vector> lan_ips { ip_block("192.168.0.0/16"sv), - ip_block("172.16.0.0/12"), + ip_block("172.16.0.0/12"sv), ip_block("10.0.0.0/8"sv) }; diff --git a/sunshine/platform/windows/publish.cpp b/sunshine/platform/windows/publish.cpp new file mode 100644 index 00000000..7a0911b9 --- /dev/null +++ b/sunshine/platform/windows/publish.cpp @@ -0,0 +1,194 @@ +#include + +#include + +#include +#include + +#include + +#include "sunshine/config.h" +#include "sunshine/main.h" +#include "sunshine/nvhttp.h" +#include "sunshine/platform/common.h" +#include "sunshine/thread_safe.h" + +#include "sunshine/network.h" + + +using namespace std::literals; + +#define __SV(quote) L##quote##sv +#define SV(quote) __SV(quote) + +extern "C" { +constexpr auto DNS_REQUEST_PENDING = 9506L; +constexpr auto DNS_QUERY_REQUEST_VERSION1 = 0x1; +constexpr auto DNS_QUERY_RESULTS_VERSION1 = 0x1; + +#define SERVICE_DOMAIN "local" + +constexpr auto SERVICE_INSTANCE_NAME = SV(SERVICE_NAME "." SERVICE_TYPE "." SERVICE_DOMAIN); +constexpr auto SERVICE_TYPE_DOMAIN = SV(SERVICE_TYPE "." SERVICE_DOMAIN); + +typedef struct _DNS_SERVICE_INSTANCE { + LPWSTR pszInstanceName; + LPWSTR pszHostName; + + IP4_ADDRESS *ip4Address; + IP6_ADDRESS *ip6Address; + + WORD wPort; + WORD wPriority; + WORD wWeight; + + // Property list + DWORD dwPropertyCount; + + PWSTR *keys; + PWSTR *values; + + DWORD dwInterfaceIndex; +} DNS_SERVICE_INSTANCE, *PDNS_SERVICE_INSTANCE; + +typedef VOID WINAPI DNS_SERVICE_REGISTER_COMPLETE( + _In_ DWORD Status, + _In_ PVOID pQueryContext, + _In_ PDNS_SERVICE_INSTANCE pInstance); + +typedef DNS_SERVICE_REGISTER_COMPLETE *PDNS_SERVICE_REGISTER_COMPLETE; + +typedef struct _DNS_SERVICE_CANCEL { + PVOID reserved; +} DNS_SERVICE_CANCEL, *PDNS_SERVICE_CANCEL; + +typedef struct _DNS_SERVICE_REGISTER_REQUEST { + ULONG Version; + ULONG InterfaceIndex; + PDNS_SERVICE_INSTANCE pServiceInstance; + PDNS_SERVICE_REGISTER_COMPLETE pRegisterCompletionCallback; + PVOID pQueryContext; + HANDLE hCredentials; + BOOL unicastEnabled; +} DNS_SERVICE_REGISTER_REQUEST, *PDNS_SERVICE_REGISTER_REQUEST; + +VOID DnsServiceFreeInstance( + _In_ PDNS_SERVICE_INSTANCE pInstance); + +DWORD DnsServiceDeRegister( + _In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, + _Inout_opt_ PDNS_SERVICE_CANCEL pCancel); + +DWORD DnsServiceRegister( + _In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, + _Inout_opt_ PDNS_SERVICE_CANCEL pCancel); + +PDNS_SERVICE_INSTANCE DnsServiceConstructInstance( + _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); +} /* extern "C" */ + +void print_status(const std::string_view &prefix, HRESULT status) { + char err_string[1024]; + + DWORD bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + status, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + err_string, + sizeof(err_string), + nullptr); + + BOOST_LOG(error) << prefix << ": "sv << std::string_view { err_string, bytes }; +} + +namespace platf::publish { +VOID WINAPI register_cb(DWORD status, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) { + auto alarm = (safe::alarm_t::element_type *)pQueryContext; + + auto fg = util::fail_guard([&]() { + if(pInstance) { + DnsServiceFreeInstance(pInstance); + } + }); + + if(status) { + print_status("register_cb()"sv, status); + alarm->ring(-1); + + return; + } + + alarm->ring(0); +} + +static int service(bool enable) { + auto alarm = safe::make_alarm(); + + std::wstring_convert, wchar_t> converter; + + std::wstring name { SERVICE_INSTANCE_NAME.data(), SERVICE_INSTANCE_NAME.size() }; + std::wstring domain { SERVICE_TYPE_DOMAIN.data(), SERVICE_TYPE_DOMAIN.size() }; + + auto host = converter.from_bytes(boost::asio::ip::host_name() + ".local"); + + DNS_SERVICE_INSTANCE instance {}; + instance.pszInstanceName = name.data(); + instance.wPort = nvhttp::PORT_HTTP; + instance.pszHostName = host.data(); + + DNS_SERVICE_REGISTER_REQUEST req {}; + req.Version = DNS_QUERY_REQUEST_VERSION1; + req.pQueryContext = alarm.get(); + req.pServiceInstance = &instance; + req.pRegisterCompletionCallback = register_cb; + + DNS_STATUS status {}; + + if(enable) { + status = DnsServiceRegister(&req, nullptr); + } + else { + status = DnsServiceDeRegister(&req, nullptr); + } + + alarm->wait(); + + status = *alarm->status(); + if(status) { + BOOST_LOG(error) << "No mDNS service"sv; + return -1; + } + + return 0; +} + +class deinit_t : public ::platf::deinit_t { +public: + ~deinit_t() override { + if(service(false)) { + std::abort(); + } + + BOOST_LOG(info) << "Unregistered Sunshine Gamestream service"sv; + } +}; + +std::unique_ptr<::platf::deinit_t> start() { + if(service(true)) { + return nullptr; + } + + BOOST_LOG(info) << "Registered Sunshine Gamestream service"sv; + + return std::make_unique(); +} +} // namespace platf::publish \ No newline at end of file