mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
187 lines
4.7 KiB
C++
187 lines
4.7 KiB
C++
|
|
#include "stdafx.h"
|
||
|
|
#include "upnp_handler.h"
|
||
|
|
#include "util/logs.hpp"
|
||
|
|
|
||
|
|
#include <miniwget.h>
|
||
|
|
#include <upnpcommands.h>
|
||
|
|
|
||
|
|
LOG_CHANNEL(upnp_log, "UPNP");
|
||
|
|
|
||
|
|
upnp_handler::~upnp_handler()
|
||
|
|
{
|
||
|
|
std::lock_guard lock(m_mutex);
|
||
|
|
|
||
|
|
for (const auto& [protocol, prot_bindings] : m_bindings)
|
||
|
|
{
|
||
|
|
for (const auto& [internal_port, external_port] : prot_bindings)
|
||
|
|
{
|
||
|
|
remove_port_redir_external(external_port, protocol);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
m_active = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void upnp_handler::upnp_enable()
|
||
|
|
{
|
||
|
|
std::lock_guard lock(m_mutex);
|
||
|
|
|
||
|
|
m_cfg.load();
|
||
|
|
|
||
|
|
auto check_igd = [&](const char* url) -> bool
|
||
|
|
{
|
||
|
|
int desc_xml_size = 0;
|
||
|
|
int status_code = 0;
|
||
|
|
|
||
|
|
m_igd_data = {};
|
||
|
|
m_igd_urls = {};
|
||
|
|
|
||
|
|
char* desc_xml = static_cast<char*>(miniwget(url, &desc_xml_size, 1, &status_code));
|
||
|
|
|
||
|
|
if (!desc_xml)
|
||
|
|
return false;
|
||
|
|
|
||
|
|
parserootdesc(desc_xml, desc_xml_size, &m_igd_data);
|
||
|
|
free(desc_xml);
|
||
|
|
desc_xml = nullptr;
|
||
|
|
GetUPNPUrls(&m_igd_urls, &m_igd_data, url, 1);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
};
|
||
|
|
|
||
|
|
std::string dev_url = m_cfg.get_device_url();
|
||
|
|
|
||
|
|
if (!dev_url.empty())
|
||
|
|
{
|
||
|
|
if (check_igd(dev_url.c_str()))
|
||
|
|
{
|
||
|
|
upnp_log.notice("Saved UPNP(%s) enabled", dev_url);
|
||
|
|
m_active = true;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
upnp_log.error("Saved UPNP(%s) isn't available anymore", dev_url);
|
||
|
|
}
|
||
|
|
|
||
|
|
upnp_log.notice("Starting UPNP search");
|
||
|
|
|
||
|
|
int upnperror = 0;
|
||
|
|
UPNPDev* devlist = upnpDiscover(2000, nullptr, nullptr, 0, 0, 2, &upnperror);
|
||
|
|
|
||
|
|
if (!devlist)
|
||
|
|
{
|
||
|
|
upnp_log.error("No UPNP device was found");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const UPNPDev* dev = devlist;
|
||
|
|
for (; dev; dev = dev->pNext)
|
||
|
|
{
|
||
|
|
if (strstr(dev->st, "InternetGatewayDevice"))
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (dev)
|
||
|
|
{
|
||
|
|
int desc_xml_size = 0;
|
||
|
|
int status_code = 0;
|
||
|
|
char* desc_xml = static_cast<char*>(miniwget(dev->descURL, &desc_xml_size, 1, &status_code));
|
||
|
|
|
||
|
|
if (desc_xml)
|
||
|
|
{
|
||
|
|
IGDdatas igd_data{};
|
||
|
|
UPNPUrls igd_urls{};
|
||
|
|
parserootdesc(desc_xml, desc_xml_size, &igd_data);
|
||
|
|
free(desc_xml);
|
||
|
|
desc_xml = nullptr;
|
||
|
|
GetUPNPUrls(&igd_urls, &igd_data, dev->descURL, 1);
|
||
|
|
|
||
|
|
upnp_log.notice("Found UPnP device type:%s at %s", dev->st, dev->descURL);
|
||
|
|
|
||
|
|
m_cfg.set_device_url(dev->descURL);
|
||
|
|
m_cfg.save();
|
||
|
|
|
||
|
|
m_active = true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
upnp_log.error("Failed to retrieve UPNP xml for %s", dev->descURL);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
upnp_log.error("No UPNP IGD device was found");
|
||
|
|
}
|
||
|
|
|
||
|
|
freeUPNPDevlist(devlist);
|
||
|
|
}
|
||
|
|
|
||
|
|
void upnp_handler::add_port_redir(std::string_view addr, u16 internal_port, std::string_view protocol)
|
||
|
|
{
|
||
|
|
if (!m_active)
|
||
|
|
return;
|
||
|
|
|
||
|
|
std::lock_guard lock(m_mutex);
|
||
|
|
|
||
|
|
u16 external_port = internal_port;
|
||
|
|
std::string internal_port_str = fmt::format("%d", internal_port);
|
||
|
|
int res = 0;
|
||
|
|
|
||
|
|
for (u16 external_port = internal_port; external_port < internal_port + 100; external_port++)
|
||
|
|
{
|
||
|
|
std::string external_port_str = fmt::format("%d", external_port);
|
||
|
|
res = UPNP_AddPortMapping(m_igd_urls.controlURL, m_igd_data.first.servicetype, external_port_str.c_str(), internal_port_str.c_str(), addr.data(), "RPCS3", protocol.data(), nullptr, nullptr);
|
||
|
|
if (res == UPNPCOMMAND_SUCCESS)
|
||
|
|
{
|
||
|
|
m_bindings[std::string(protocol)][internal_port] = external_port;
|
||
|
|
upnp_log.notice("Successfully bound %s:%d(%s) to IGD:%d", addr, internal_port, protocol, external_port);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// need more testing, may vary per router, etc, for now assume port conflict silently
|
||
|
|
// else if (res != 718) // ConflictInMappingEntry
|
||
|
|
// {
|
||
|
|
// upnp_log.error("Failed to bind %s:%d(%s) to IGD:%d: %d", addr, internal_port, protocol, external_port, res);
|
||
|
|
// return;
|
||
|
|
// }
|
||
|
|
}
|
||
|
|
|
||
|
|
upnp_log.error("Failed to bind %s:%d(%s) to IGD:(%d=>%d): %d", addr, internal_port, protocol, internal_port, external_port, res);
|
||
|
|
}
|
||
|
|
|
||
|
|
void upnp_handler::remove_port_redir(u16 internal_port, std::string_view protocol)
|
||
|
|
{
|
||
|
|
if (!m_active)
|
||
|
|
return;
|
||
|
|
|
||
|
|
std::lock_guard lock(m_mutex);
|
||
|
|
|
||
|
|
const std::string str_protocol(protocol);
|
||
|
|
|
||
|
|
if (!m_bindings.contains(str_protocol) || !::at32(m_bindings, str_protocol).contains(internal_port))
|
||
|
|
{
|
||
|
|
upnp_log.error("tried to unbind port mapping %d to IGD(%s) but it isn't bound", internal_port, protocol);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const u16 external_port = ::at32(::at32(m_bindings, str_protocol), internal_port);
|
||
|
|
|
||
|
|
remove_port_redir_external(external_port, protocol);
|
||
|
|
|
||
|
|
ensure(::at32(m_bindings, str_protocol).erase(internal_port));
|
||
|
|
upnp_log.notice("Successfully deleted port mapping %d to IGD:%d(%s)", internal_port, external_port, protocol);
|
||
|
|
}
|
||
|
|
|
||
|
|
void upnp_handler::remove_port_redir_external(u16 external_port, std::string_view protocol, bool verbose)
|
||
|
|
{
|
||
|
|
const std::string str_ext_port = fmt::format("%d", external_port);
|
||
|
|
|
||
|
|
if (int res = UPNP_DeletePortMapping(m_igd_urls.controlURL, m_igd_data.first.servicetype, str_ext_port.c_str(), protocol.data(), nullptr); res != 0 && verbose)
|
||
|
|
upnp_log.error("Failed to delete port mapping IGD:%s(%s): %d", str_ext_port, protocol, res);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool upnp_handler::is_active() const
|
||
|
|
{
|
||
|
|
return m_active;
|
||
|
|
}
|