Add support for upnp
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -7,3 +7,6 @@
|
|||||||
[submodule "ViGEmClient"]
|
[submodule "ViGEmClient"]
|
||||||
path = third-party/ViGEmClient
|
path = third-party/ViGEmClient
|
||||||
url = https://github.com/ViGEm/ViGEmClient
|
url = https://github.com/ViGEm/ViGEmClient
|
||||||
|
[submodule "third-party/miniupnp"]
|
||||||
|
path = third-party/miniupnp
|
||||||
|
url = https://github.com/miniupnp/miniupnp
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
|||||||
|
|
||||||
add_subdirectory(third-party/Simple-Web-Server)
|
add_subdirectory(third-party/Simple-Web-Server)
|
||||||
|
|
||||||
|
set(UPNPC_BUILD_SHARED OFF CACHE BOOL "no shared libraries")
|
||||||
|
set(UPNPC_BUILD_TESTS OFF CACHE BOOL "Don't build tests for miniupnpc")
|
||||||
|
set(UPNPC_BUILD_SAMPLE OFF CACHE BOOL "Don't build samples for miniupnpc")
|
||||||
|
set(UPNPC_NO_INSTALL ON CACHE BOOL "Don't install any libraries build for miniupnpc")
|
||||||
|
add_subdirectory(third-party/miniupnp/miniupnpc)
|
||||||
|
include_directories(third-party/miniupnp)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# Ugly hack to compile with #include <qos2.h>
|
# Ugly hack to compile with #include <qos2.h>
|
||||||
add_compile_definitions(
|
add_compile_definitions(
|
||||||
@@ -155,6 +162,8 @@ set(SUNSHINE_TARGET_FILES
|
|||||||
third-party/moonlight-common-c/src/Rtsp.h
|
third-party/moonlight-common-c/src/Rtsp.h
|
||||||
third-party/moonlight-common-c/src/RtspParser.c
|
third-party/moonlight-common-c/src/RtspParser.c
|
||||||
third-party/moonlight-common-c/src/Video.h
|
third-party/moonlight-common-c/src/Video.h
|
||||||
|
sunshine/upnp.cpp
|
||||||
|
sunshine/upnp.h
|
||||||
sunshine/cbs.cpp
|
sunshine/cbs.cpp
|
||||||
sunshine/utility.h
|
sunshine/utility.h
|
||||||
sunshine/uuid.h
|
sunshine/uuid.h
|
||||||
@@ -193,6 +202,8 @@ set(SUNSHINE_TARGET_FILES
|
|||||||
sunshine/round_robin.h
|
sunshine/round_robin.h
|
||||||
${PLATFORM_TARGET_FILES})
|
${PLATFORM_TARGET_FILES})
|
||||||
|
|
||||||
|
set_source_files_properties(sunshine/upnp.cpp PROPERTIES COMPILE_FLAGS -Wno-pedantic)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/third-party
|
${CMAKE_CURRENT_SOURCE_DIR}/third-party
|
||||||
@@ -222,6 +233,7 @@ list(APPEND CBS_EXTERNAL_LIBRARIES
|
|||||||
cbs)
|
cbs)
|
||||||
|
|
||||||
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
|
list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
|
||||||
|
libminiupnpc-static
|
||||||
${CBS_EXTERNAL_LIBRARIES}
|
${CBS_EXTERNAL_LIBRARIES}
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
stdc++fs
|
stdc++fs
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "nvhttp.h"
|
#include "nvhttp.h"
|
||||||
#include "rtsp.h"
|
#include "rtsp.h"
|
||||||
#include "thread_pool.h"
|
#include "thread_pool.h"
|
||||||
|
#include "upnp.h"
|
||||||
#include "video.h"
|
#include "video.h"
|
||||||
|
|
||||||
#include "platform/common.h"
|
#include "platform/common.h"
|
||||||
@@ -208,9 +209,14 @@ int main(int argc, char *argv[]) {
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<platf::deinit_t> unregister;
|
std::unique_ptr<platf::deinit_t> mDNS;
|
||||||
auto sync = std::async(std::launch::async, [&unregister]() {
|
auto sync_mDNS = std::async(std::launch::async, [&mDNS]() {
|
||||||
unregister = platf::publish::start();
|
mDNS = platf::publish::start();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::unique_ptr<platf::deinit_t> upnp_unmap;
|
||||||
|
auto sync_upnp = std::async(std::launch::async, [&upnp_unmap]() {
|
||||||
|
upnp_unmap = upnp::start();
|
||||||
});
|
});
|
||||||
|
|
||||||
//FIXME: Temporary workaround: Simple-Web_server needs to be updated or replaced
|
//FIXME: Temporary workaround: Simple-Web_server needs to be updated or replaced
|
||||||
|
|||||||
166
sunshine/upnp.cpp
Normal file
166
sunshine/upnp.cpp
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
#include <miniupnpc/miniupnpc.h>
|
||||||
|
#include <miniupnpc/upnpcommands.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "upnp.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
namespace upnp {
|
||||||
|
constexpr auto INET6_ADDRSTRLEN = 46;
|
||||||
|
|
||||||
|
constexpr auto IPv4 = 0;
|
||||||
|
constexpr auto IPv6 = 1;
|
||||||
|
|
||||||
|
using device_t = util::safe_ptr<UPNPDev, freeUPNPDevlist>;
|
||||||
|
|
||||||
|
KITTY_USING_MOVE_T(urls_t, UPNPUrls, , {
|
||||||
|
FreeUPNPUrls(&el);
|
||||||
|
});
|
||||||
|
|
||||||
|
struct mapping_t {
|
||||||
|
mapping_t(std::string &&wan, std::string &&lan, std::string &&description, bool tcp)
|
||||||
|
: port { std::move(wan), std::move(lan) }, description { std::move(description) }, tcp { tcp } {}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::string wan;
|
||||||
|
std::string lan;
|
||||||
|
} port;
|
||||||
|
|
||||||
|
std::string description;
|
||||||
|
bool tcp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::vector<mapping_t> mappings {
|
||||||
|
{ "48010"s, "48010"s, "RTSP setup port"s, false },
|
||||||
|
{ "47998"s, "47998"s, "Video stream port"s, false },
|
||||||
|
{ "47999"s, "47998"s, "Control stream port"s, false },
|
||||||
|
{ "48000"s, "48000"s, "Audio stream port"s, false },
|
||||||
|
{ "47989"s, "47989"s, "Gamestream http port"s, true },
|
||||||
|
{ "47984"s, "47984"s, "Gamestream https port"s, true },
|
||||||
|
{ "47990"s, "47990"s, "Sunshine Web Manager port"s, true },
|
||||||
|
};
|
||||||
|
|
||||||
|
void unmap(
|
||||||
|
const urls_t &urls,
|
||||||
|
const IGDdatas &data,
|
||||||
|
std::vector<mapping_t>::const_reverse_iterator begin,
|
||||||
|
std::vector<mapping_t>::const_reverse_iterator end) {
|
||||||
|
|
||||||
|
BOOST_LOG(debug) << "Unmapping UPNP ports"sv;
|
||||||
|
|
||||||
|
for(auto it = begin; it != end; ++it) {
|
||||||
|
auto status = UPNP_DeletePortMapping(
|
||||||
|
urls->controlURL,
|
||||||
|
data.first.servicetype,
|
||||||
|
it->port.wan.c_str(),
|
||||||
|
it->tcp ? "TCP" : "UDP",
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if(status) {
|
||||||
|
BOOST_LOG(warning) << "Failed to unmap port ["sv << it->port.wan << "] to port ["sv << it->port.lan << "]: error code ["sv << status << ']';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class deinit_t : public platf::deinit_t {
|
||||||
|
public:
|
||||||
|
using iter_t = std::vector<mapping_t>::const_reverse_iterator;
|
||||||
|
deinit_t(urls_t &&urls, IGDdatas data, iter_t begin, iter_t end)
|
||||||
|
: urls { std::move(urls) }, data { data }, begin { begin }, end { end } {}
|
||||||
|
|
||||||
|
~deinit_t() {
|
||||||
|
unmap(urls, data, begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
urls_t urls;
|
||||||
|
IGDdatas data;
|
||||||
|
|
||||||
|
iter_t begin;
|
||||||
|
iter_t end;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string_view status_string(int status) {
|
||||||
|
switch(status) {
|
||||||
|
case 0:
|
||||||
|
return "No IGD device found"sv;
|
||||||
|
case 1:
|
||||||
|
return "Valid IGD device found"sv;
|
||||||
|
case 2:
|
||||||
|
return "A UPnP device has been found, but it wasn't recognized as an IGD"sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown status"sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<platf::deinit_t> start() {
|
||||||
|
int err {};
|
||||||
|
|
||||||
|
device_t device { upnpDiscover(2000, nullptr, nullptr, 0, IPv4, 2, &err) };
|
||||||
|
if(!device || err) {
|
||||||
|
BOOST_LOG(error) << "Couldn't discover any UPNP devices"sv;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto dev = device.get(); dev != nullptr; dev = dev->pNext) {
|
||||||
|
BOOST_LOG(debug) << "Found device: "sv << dev->descURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<char, INET6_ADDRSTRLEN> lan_addr;
|
||||||
|
std::array<char, INET6_ADDRSTRLEN> wan_addr;
|
||||||
|
|
||||||
|
urls_t urls;
|
||||||
|
IGDdatas data;
|
||||||
|
|
||||||
|
auto status = UPNP_GetValidIGD(device.get(), &urls.el, &data, lan_addr.data(), lan_addr.size());
|
||||||
|
if(status != 1) {
|
||||||
|
BOOST_LOG(error) << status_string(status);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG(debug) << "Found valid IGD device: "sv << urls->rootdescURL;
|
||||||
|
|
||||||
|
if(UPNP_GetExternalIPAddress(urls->controlURL, data.first.servicetype, wan_addr.data())) {
|
||||||
|
BOOST_LOG(warning) << "Could not get external ip"sv;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BOOST_LOG(debug) << "Found external ip: "sv << wan_addr.data();
|
||||||
|
if(config::nvhttp.external_ip.empty()) {
|
||||||
|
config::nvhttp.external_ip = wan_addr.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::begin(mappings);
|
||||||
|
|
||||||
|
status = 0;
|
||||||
|
for(; it != std::end(mappings); ++it) {
|
||||||
|
status = UPNP_AddPortMapping(
|
||||||
|
urls->controlURL,
|
||||||
|
data.first.servicetype,
|
||||||
|
it->port.wan.c_str(),
|
||||||
|
it->port.lan.c_str(),
|
||||||
|
lan_addr.data(),
|
||||||
|
it->description.c_str(),
|
||||||
|
it->tcp ? "TCP" : "UDP",
|
||||||
|
nullptr,
|
||||||
|
"86400");
|
||||||
|
|
||||||
|
if(status) {
|
||||||
|
BOOST_LOG(error) << "Failed to map port ["sv << it->port.wan << "] to port ["sv << it->port.lan << "]: error code ["sv << status << ']';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status) {
|
||||||
|
unmap(urls, data, std::make_reverse_iterator(it), std::rend(mappings));
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_unique<deinit_t>(std::move(urls), data, std::rbegin(mappings), std::rend(mappings));
|
||||||
|
}
|
||||||
|
} // namespace upnp
|
||||||
10
sunshine/upnp.h
Normal file
10
sunshine/upnp.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef SUNSHINE_UPNP_H
|
||||||
|
#define SUNSHINE_UPNP_H
|
||||||
|
|
||||||
|
#include "platform/common.h"
|
||||||
|
|
||||||
|
namespace upnp {
|
||||||
|
[[nodiscard]] std::unique_ptr<platf::deinit_t> start();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
1
third-party/miniupnp
vendored
Submodule
1
third-party/miniupnp
vendored
Submodule
Submodule third-party/miniupnp added at 6f848ae082
Reference in New Issue
Block a user