mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Merge pull request #20670 from cipherxof/portableops
Support for PortableOps Infrastructure
This commit is contained in:
@@ -63,8 +63,10 @@ bool Connection::Resolve(const char *host, int port, DNSType type) {
|
||||
char port_str[16];
|
||||
snprintf(port_str, sizeof(port_str), "%d", port);
|
||||
|
||||
std::string processedHostname = ProcessHostnameWithInfraDNS(std::string(host));
|
||||
|
||||
std::string err;
|
||||
if (!net::DNSResolve(host, port_str, &resolved_, err, type)) {
|
||||
if (!net::DNSResolve(processedHostname.c_str(), port_str, &resolved_, err, type)) {
|
||||
WARN_LOG(Log::IO, "Failed to resolve host '%s': '%s' (%s)", host, err.c_str(), DNSTypeAsString(type));
|
||||
// Zero port so that future calls fail.
|
||||
port_ = 0;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "Core/Config.h"
|
||||
#include "Core/HLE/sceNet.h"
|
||||
#include "ppsspp_config.h"
|
||||
#include "Common/Net/Resolve.h"
|
||||
|
||||
@@ -502,4 +504,53 @@ bool DirectDNSLookupIPV4(const char *dns_server_ip, const char *domain, uint32_t
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ProcessHostnameWithInfraDNS(const std::string& hostname) {
|
||||
std::string resolvedHostname = hostname;
|
||||
|
||||
// Resolve any aliases. First check the ini file, then check the hardcoded DNS config.
|
||||
auto aliasIter = g_Config.mHostToAlias.find(hostname);
|
||||
if (aliasIter != g_Config.mHostToAlias.end()) {
|
||||
const std::string& alias = aliasIter->second;
|
||||
INFO_LOG(Log::sceNet, "%s - Resolved alias %s from hostname %s", __FUNCTION__, alias.c_str(), hostname.c_str());
|
||||
resolvedHostname = alias;
|
||||
}
|
||||
|
||||
if (g_Config.bInfrastructureAutoDNS) {
|
||||
// Also look up into the preconfigured fixed DNS JSON.
|
||||
auto fixedDNSIter = GetInfraDNSConfig().fixedDNS.find(resolvedHostname);
|
||||
if (fixedDNSIter != GetInfraDNSConfig().fixedDNS.end()) {
|
||||
const std::string& domainIP = fixedDNSIter->second;
|
||||
INFO_LOG(Log::sceNet, "%s - Resolved IP %s from fixed DNS lookup with '%s'", __FUNCTION__, domainIP.c_str(), resolvedHostname.c_str());
|
||||
resolvedHostname = domainIP;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if hostname is already an IPv4 address, if so we do not need further lookup. This usually happens
|
||||
// after the mHostToAlias or fixedDNSIter lookups, which effectively both are hardcoded DNS.
|
||||
uint32_t resolvedAddr;
|
||||
if (inet_pton(AF_INET, resolvedHostname.c_str(), &resolvedAddr)) {
|
||||
INFO_LOG(Log::sceNet, "Not looking up '%s', already an IP address.", resolvedHostname.c_str());
|
||||
return resolvedHostname;
|
||||
}
|
||||
|
||||
// Now use the configured primary DNS server to do a lookup.
|
||||
// If auto DNS, use the server from that config.
|
||||
std::string dnsServer;
|
||||
if (g_Config.bInfrastructureAutoDNS && !GetInfraDNSConfig().dns.empty()) {
|
||||
dnsServer = GetInfraDNSConfig().dns;
|
||||
} else {
|
||||
dnsServer = g_Config.sInfrastructureDNSServer;
|
||||
}
|
||||
|
||||
if (net::DirectDNSLookupIPV4(dnsServer.c_str(), resolvedHostname.c_str(), &resolvedAddr)) {
|
||||
char temp[32];
|
||||
inet_ntop(AF_INET, &resolvedAddr, temp, sizeof(temp));
|
||||
INFO_LOG(Log::sceNet, "Direct lookup of '%s' from '%s' succeeded: %s", resolvedHostname.c_str(), dnsServer.c_str(), temp);
|
||||
return std::string(temp);
|
||||
}
|
||||
|
||||
WARN_LOG(Log::sceNet, "Direct DNS lookup of '%s' at DNS server '%s' failed. Will try OS DNS...", resolvedHostname.c_str(), dnsServer.c_str());
|
||||
return resolvedHostname;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
||||
@@ -26,5 +26,6 @@ int inet_pton(int af, const char* src, void* dst);
|
||||
|
||||
// Does a DNS lookup without involving the OS, so you can hit any DNS server.
|
||||
bool DirectDNSLookupIPV4(const char *dnsServer, const char *host, uint32_t *ipv4_addr);
|
||||
std::string ProcessHostnameWithInfraDNS(const std::string& hostname);
|
||||
|
||||
} // namespace net
|
||||
|
||||
@@ -242,6 +242,16 @@ template<int func(u32, int, const char*, u32, u32)> void WrapI_UICUU() {
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<int func(u32, u32, int, const char*)> void WrapI_UUIC() {
|
||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), Memory::GetCharPointer(PARAM(3)));
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<int func(u32, u32, int, u32, int)> void WrapI_UUIUI() {
|
||||
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
|
||||
RETURN(retval);
|
||||
}
|
||||
|
||||
template<int func()> void WrapI_V() {
|
||||
int retval = func();
|
||||
RETURN(retval);
|
||||
|
||||
@@ -747,8 +747,6 @@ static int sceHttpCreateConnectionWithURL(int templateID, const char *url, int e
|
||||
if (!baseURL.Valid())
|
||||
return hleLogError(Log::sceNet, SCE_HTTP_ERROR_INVALID_URL, "invalid url");
|
||||
|
||||
// TODO: Here we should look up baseURL.Host() in DNS.
|
||||
|
||||
httpObjects.emplace_back(std::make_shared<HTTPConnection>(templateID, baseURL.Host().c_str(), baseURL.Protocol().c_str(), baseURL.Port(), enableKeepalive));
|
||||
int retid = (int)httpObjects.size();
|
||||
return hleLogDebug(Log::sceNet, retid);
|
||||
|
||||
@@ -86,72 +86,23 @@ static int sceNetResolverTerm() {
|
||||
|
||||
// Note: timeouts are in seconds
|
||||
static int NetResolver_StartNtoA(NetResolver *resolver, u32 hostnamePtr, u32 inAddrPtr, int timeout, int retry) {
|
||||
addrinfo *resolved = nullptr;
|
||||
|
||||
std::string err;
|
||||
std::string hostname = std::string(safe_string(Memory::GetCharPointer(hostnamePtr)));
|
||||
|
||||
SockAddrIN4 addr{};
|
||||
addr.in.sin_addr.s_addr = INADDR_NONE;
|
||||
|
||||
// Resolve any aliases. First check the ini file, then check the hardcoded DNS config.
|
||||
auto aliasIter = g_Config.mHostToAlias.find(hostname);
|
||||
if (aliasIter != g_Config.mHostToAlias.end()) {
|
||||
const std::string& alias = aliasIter->second;
|
||||
INFO_LOG(Log::sceNet, "%s - Resolved alias %s from hostname %s", __FUNCTION__, alias.c_str(), hostname.c_str());
|
||||
hostname = alias;
|
||||
}
|
||||
|
||||
if (g_Config.bInfrastructureAutoDNS) {
|
||||
// Also look up into the preconfigured fixed DNS JSON.
|
||||
auto fixedDNSIter = GetInfraDNSConfig().fixedDNS.find(hostname);
|
||||
if (fixedDNSIter != GetInfraDNSConfig().fixedDNS.end()) {
|
||||
const std::string& domainIP = fixedDNSIter->second;
|
||||
INFO_LOG(Log::sceNet, "%s - Resolved IP %s from fixed DNS lookup with '%s'", __FUNCTION__, domainIP.c_str(), hostname.c_str());
|
||||
hostname = domainIP;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if hostname is already an IPv4 address, if so we do not need further lookup. This usually happens
|
||||
// after the mHostToAlias or fixedDNSIter lookups, which effectively both are hardcoded DNS.
|
||||
uint32_t resolvedAddr;
|
||||
if (inet_pton(AF_INET, hostname.c_str(), &resolvedAddr)) {
|
||||
INFO_LOG(Log::sceNet, "Not looking up '%s', already an IP address.", hostname.c_str());
|
||||
Memory::Write_U32(resolvedAddr, inAddrPtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Process hostname with infra-DNS
|
||||
std::string processedHostname = net::ProcessHostnameWithInfraDNS(hostname);
|
||||
|
||||
// Flag resolver as in-progress - not relevant for sync functions but potentially relevant for async
|
||||
resolver->isRunning = true;
|
||||
|
||||
// Now use the configured primary DNS server to do a lookup.
|
||||
// If auto DNS, use the server from that config.
|
||||
std::string dnsServer;
|
||||
if (g_Config.bInfrastructureAutoDNS && !GetInfraDNSConfig().dns.empty()) {
|
||||
dnsServer = GetInfraDNSConfig().dns;
|
||||
} else {
|
||||
dnsServer = g_Config.sInfrastructureDNSServer;
|
||||
}
|
||||
|
||||
if (net::DirectDNSLookupIPV4(dnsServer.c_str(), hostname.c_str(), &resolvedAddr)) {
|
||||
char temp[32];
|
||||
inet_ntop(AF_INET, &resolvedAddr, temp, sizeof(temp));
|
||||
addrinfo *resolved = nullptr;
|
||||
std::string err;
|
||||
if (!net::DNSResolve(processedHostname, "", &resolved, err)) {
|
||||
ERROR_LOG(Log::sceNet, "OS DNS Error Resolving %s (%s)", processedHostname.c_str(), err.c_str());
|
||||
resolver->isRunning = false;
|
||||
Memory::Write_U32(resolvedAddr, inAddrPtr);
|
||||
INFO_LOG(Log::sceNet, "Direct lookup of '%s' from '%s' succeeded: %s (%08x)", hostname.c_str(), dnsServer.c_str(), temp, resolvedAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WARN_LOG(Log::sceNet, "Direct DNS lookup of '%s' at DNS server '%s' failed. Trying OS DNS...", hostname.c_str(), g_Config.sInfrastructureDNSServer.c_str());
|
||||
|
||||
// Attempt to execute an OS DNS resolution
|
||||
if (!net::DNSResolve(hostname, "", &resolved, err)) {
|
||||
// TODO: Return an error based on the outputted "err" (unfortunately it's already converted to string)
|
||||
ERROR_LOG(Log::sceNet, "OS DNS Error Resolving %s (%s)\n", hostname.c_str(), err.c_str());
|
||||
return SCE_NET_RESOLVER_ERROR_INVALID_HOST;
|
||||
}
|
||||
|
||||
// If successful, write to memory
|
||||
// Process results
|
||||
SockAddrIN4 addr{};
|
||||
if (resolved) {
|
||||
for (auto ptr = resolved; ptr != nullptr; ptr = ptr->ai_next) {
|
||||
switch (ptr->ai_family) {
|
||||
@@ -161,9 +112,7 @@ static int NetResolver_StartNtoA(NetResolver *resolver, u32 hostnamePtr, u32 inA
|
||||
}
|
||||
}
|
||||
net::DNSResolveFree(resolved);
|
||||
|
||||
Memory::Write_U32(addr.in.sin_addr.s_addr, inAddrPtr);
|
||||
resolver->isRunning = false;
|
||||
INFO_LOG(Log::sceNet, "%s - Hostname: %s => IPv4: %s", __FUNCTION__, hostname.c_str(),
|
||||
ip2str(addr.in.sin_addr, false).c_str());
|
||||
}
|
||||
|
||||
+181
-3
@@ -54,7 +54,7 @@ static int sceUriParse(u32 parsedUriAreaAddr, const char* url, u32 workAreaAddr,
|
||||
*workAreaSz = sz;
|
||||
workAreaSz.NotifyWrite("UriParse");
|
||||
}
|
||||
return hleLogDebug(Log::sceNet, 0, "workAreaSize: %d, %d", sz, workAreaSize);
|
||||
return hleLogDebug(Log::sceNet, -1, "workAreaSize: %d, %d", sz, workAreaSize);
|
||||
}
|
||||
|
||||
auto parsedUri = PSPPointer<PSPParsedUri>::Create(parsedUriAreaAddr);
|
||||
@@ -153,12 +153,190 @@ static int sceUriParse(u32 parsedUriAreaAddr, const char* url, u32 workAreaAddr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sceUriBuild(u32 workAreaAddr, u32 workAreaSizeAddr, int workAreaSize, u32 parsedUriAddr, int flags) {
|
||||
WARN_LOG(Log::sceNet, "UNTESTED sceUriBuild(%x, %x, %d, %x, %d) at %08x", workAreaAddr, workAreaSizeAddr, workAreaSize, parsedUriAddr, flags, currentMIPS->pc);
|
||||
|
||||
auto workAreaSz = PSPPointer<u32>::Create(workAreaSizeAddr);
|
||||
|
||||
if (parsedUriAddr == 0) {
|
||||
return hleLogError(Log::sceNet, -1, "invalid parsedUriAddr");
|
||||
}
|
||||
|
||||
auto parsedUri = PSPPointer<PSPParsedUri>::Create(parsedUriAddr);
|
||||
if (!parsedUri.IsValid()) {
|
||||
return hleLogError(Log::sceNet, -1, "invalid parsedUri structure");
|
||||
}
|
||||
|
||||
auto getUriComponent = [](u32 addr, int flags, int flagMask) -> std::string {
|
||||
if (addr == 0 || (flags & flagMask) == 0) return "";
|
||||
const char* str = Memory::GetCharPointer(addr);
|
||||
return str ? std::string(str) : "";
|
||||
};
|
||||
|
||||
std::string scheme = getUriComponent(parsedUri->schemeAddr, flags, 0x1);
|
||||
std::string userInfoUserName = getUriComponent(parsedUri->userInfoUserNameAddr, flags, 0x10);
|
||||
std::string userInfoPassword = getUriComponent(parsedUri->userInfoPasswordAddr, flags, 0x20);
|
||||
std::string host = getUriComponent(parsedUri->hostAddr, flags, 0x2);
|
||||
std::string path = getUriComponent(parsedUri->pathAddr, flags, 0x8);
|
||||
std::string query = getUriComponent(parsedUri->queryAddr, flags, 0x40);
|
||||
std::string fragment = getUriComponent(parsedUri->fragmentAddr, flags, 0x80);
|
||||
int port = (flags & 0x4) != 0 ? parsedUri->port : -1;
|
||||
|
||||
// Build the complete URI
|
||||
std::string uri = "";
|
||||
|
||||
if (!scheme.empty()) {
|
||||
std::string finalScheme = scheme;
|
||||
if (finalScheme == "https") { // https is unsupported
|
||||
finalScheme = "http";
|
||||
}
|
||||
uri += finalScheme + ":";
|
||||
}
|
||||
|
||||
if (parsedUri->noSlash == 0) {
|
||||
uri += "//";
|
||||
}
|
||||
|
||||
if (!userInfoUserName.empty()) {
|
||||
uri += userInfoUserName;
|
||||
if (!userInfoPassword.empty()) {
|
||||
uri += ":" + userInfoPassword;
|
||||
}
|
||||
uri += "@";
|
||||
}
|
||||
|
||||
if (!host.empty()) {
|
||||
uri += host;
|
||||
}
|
||||
|
||||
if (port > 0) {
|
||||
int defaultPort = -1;
|
||||
if (parsedUri->schemeAddr != 0) {
|
||||
const char* protocol = Memory::GetCharPointer(parsedUri->schemeAddr);
|
||||
if (protocol) {
|
||||
std::string protocolStr(protocol);
|
||||
if (protocolStr == "http") defaultPort = 80;
|
||||
//else if (protocolStr == "https") defaultPort = 443;
|
||||
else if (protocolStr == "ftp") defaultPort = 21;
|
||||
else if (protocolStr == "ssh") defaultPort = 22;
|
||||
}
|
||||
}
|
||||
if (port != defaultPort) {
|
||||
uri += ":" + std::to_string(port);
|
||||
}
|
||||
}
|
||||
|
||||
if (!path.empty()) {
|
||||
uri += path;
|
||||
}
|
||||
|
||||
if (!query.empty()) {
|
||||
uri += query;
|
||||
}
|
||||
|
||||
if (!fragment.empty()) {
|
||||
uri += fragment;
|
||||
}
|
||||
|
||||
int requiredSize = (int)uri.length() + 1;
|
||||
|
||||
if (workAreaSz.IsValid()) {
|
||||
*workAreaSz = requiredSize;
|
||||
workAreaSz.NotifyWrite("UriBuild");
|
||||
}
|
||||
|
||||
if (workAreaAddr == 0) {
|
||||
return hleLogDebug(Log::sceNet, -1, "size query, required size: %d", requiredSize);
|
||||
}
|
||||
|
||||
if (requiredSize > workAreaSize) {
|
||||
return hleLogError(Log::sceNet, -1, "work area too small: need %d, have %d", requiredSize, workAreaSize);
|
||||
}
|
||||
|
||||
Memory::MemcpyUnchecked(workAreaAddr, uri.c_str(), requiredSize);
|
||||
|
||||
return hleLogDebug(Log::sceNet, 0, "built URI: %s", uri.c_str());
|
||||
}
|
||||
|
||||
static int sceUriEscape(u32 escapedAddr, u32 escapedLengthAddr, int escapedBufferLength, const char* source) {
|
||||
WARN_LOG(Log::sceNet, "UNTESTED sceUriEscape(%x, %x, %d, %s) at %08x", escapedAddr, escapedLengthAddr, escapedBufferLength, safe_string(source), currentMIPS->pc);
|
||||
|
||||
if (source == nullptr) {
|
||||
return hleLogError(Log::sceNet, -1, "invalid source");
|
||||
}
|
||||
|
||||
auto escapedLength = PSPPointer<u32>::Create(escapedLengthAddr);
|
||||
|
||||
// Helper function to check if a character needs to be escaped
|
||||
auto needsEscaping = [](unsigned char c) -> bool {
|
||||
// Characters that should NOT be escaped (unreserved characters in RFC 3986)
|
||||
// ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
if ((c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '.' || c == '_' || c == '~') {
|
||||
return false;
|
||||
}
|
||||
// Everything else needs to be escaped
|
||||
return true;
|
||||
};
|
||||
|
||||
// Helper function to convert a hex digit to character
|
||||
auto toHexChar = [](int digit) -> char {
|
||||
if (digit < 10) return '0' + digit;
|
||||
return 'A' + (digit - 10);
|
||||
};
|
||||
|
||||
std::string sourceStr(source);
|
||||
int requiredSize = 0;
|
||||
|
||||
for (unsigned char c : sourceStr) {
|
||||
if (needsEscaping(c)) {
|
||||
requiredSize += 3; // %XX format
|
||||
} else {
|
||||
requiredSize += 1; // unchanged character
|
||||
}
|
||||
}
|
||||
requiredSize += 1; // null terminator
|
||||
|
||||
if (escapedLength.IsValid()) {
|
||||
*escapedLength = requiredSize;
|
||||
escapedLength.NotifyWrite("UriEscape");
|
||||
}
|
||||
|
||||
if (escapedAddr == 0) {
|
||||
return hleLogError(Log::sceNet, -1, "size query, required size: %d", requiredSize);
|
||||
}
|
||||
|
||||
if (requiredSize > escapedBufferLength) {
|
||||
return hleLogError(Log::sceNet, -1, "buffer too small: need %d, have %d", requiredSize, escapedBufferLength);
|
||||
}
|
||||
|
||||
std::string escaped = "";
|
||||
|
||||
for (unsigned char c : sourceStr) {
|
||||
if (needsEscaping(c)) {
|
||||
// Escape as %XX
|
||||
escaped += '%';
|
||||
escaped += toHexChar((c >> 4) & 0xF);
|
||||
escaped += toHexChar(c & 0xF);
|
||||
} else {
|
||||
// Keep unchanged
|
||||
escaped += c;
|
||||
}
|
||||
}
|
||||
|
||||
Memory::MemcpyUnchecked(escapedAddr, escaped.c_str(), escaped.length() + 1);
|
||||
|
||||
return hleLogDebug(Log::sceNet, 0, "escaped '%s' to '%s'", source, escaped.c_str());
|
||||
}
|
||||
|
||||
const HLEFunction sceParseUri[] =
|
||||
{
|
||||
{0X49E950EC, nullptr, "sceUriEscape", '?', ""},
|
||||
{0X49E950EC, &WrapI_UUIC<sceUriEscape>, "sceUriEscape", 'i', "xxis"},
|
||||
{0X062BB07E, nullptr, "sceUriUnescape", '?', ""},
|
||||
{0X568518C9, &WrapI_UCUUI<sceUriParse>, "sceUriParse", 'i', "xsxxi" },
|
||||
{0X7EE318AF, nullptr, "sceUriBuild", '?', ""},
|
||||
{0X7EE318AF, &WrapI_UUIUI<sceUriBuild>, "sceUriBuild", 'i', "xxxxi" },
|
||||
};
|
||||
|
||||
void Register_sceParseUri()
|
||||
|
||||
@@ -91,6 +91,44 @@
|
||||
"ULJM05301"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Portable Ops",
|
||||
"comment": {
|
||||
"en_US": "Support for MPO and MPO+."
|
||||
},
|
||||
"revival_credits": {
|
||||
"group": "SaveMGO",
|
||||
"url": "https://discord.gg/mgo2pc"
|
||||
},
|
||||
"dns": "mpo.savemgo.com",
|
||||
"dyn_dns": "mpo.savemgo.com",
|
||||
"domains": {
|
||||
"mpoweb.konamionline.com": "mpo.savemgo.com",
|
||||
"info.service.konamionline.com": "mpo.savemgo.com",
|
||||
"mpostunus.konamionline.com": "stun.mgo2pc.com",
|
||||
"mpostunjp.konamionline.com": "stun.mgo2pc.com",
|
||||
"mpostuneu.konamionline.com": "stun.mgo2pc.com"
|
||||
},
|
||||
"score": 4,
|
||||
"known_working_ids": [
|
||||
"ULJM05261",
|
||||
"ULJM05284",
|
||||
"ULJM05284",
|
||||
"ULUS10290",
|
||||
"ULAS42123",
|
||||
"ULES01003",
|
||||
"ULUS10202",
|
||||
"ULJM05193",
|
||||
"VP033J1",
|
||||
"ULKS46119",
|
||||
"ULES00645",
|
||||
"ULJM05256",
|
||||
"ULJM08016",
|
||||
"ULUS10202GH",
|
||||
"NPJH50112",
|
||||
"ULJM05573"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Need for Speed: Undercover",
|
||||
"comment": {
|
||||
|
||||
Reference in New Issue
Block a user