DownloadManager: Always use Nintendo servers + additional streamlining

- Download manager now always uses Nintendo servers. Requires only a valid OTP and SEEPROM dump so you can use it in combination with a Pretendo setup even without a NNID
- Account drop down removed from download manager since it's not required
- Internally all our API requests now support overriding which service to use
- Drop support for act-url and ecs-url command line parameters. Usage of network_services.xml ("custom" option in the UI) is preferred
This commit is contained in:
Exzap
2024-04-20 12:19:06 +02:00
parent 989e2b8c8c
commit efbbb817fe
29 changed files with 323 additions and 338 deletions
+1 -1
View File
@@ -498,7 +498,7 @@ namespace iosu
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, task_header_callback);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &(*it));
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0x3C);
if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo) // remove Pretendo Function once SSL is in the Service
if (IsNetworkServiceSSLDisabled(ActiveSettings::GetNetworkService()))
{
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
}
-10
View File
@@ -292,16 +292,6 @@ void iosuCrypto_generateDeviceCertificate()
BN_CTX_free(context);
}
bool iosuCrypto_hasAllDataForLogin()
{
if (hasOtpMem == false)
return false;
if (hasSeepromMem == false)
return false;
// todo - check if certificates are available
return true;
}
sint32 iosuCrypto_getDeviceCertificateBase64Encoded(char* output)
{
iosuCrypto_base64Encode((uint8*)&g_wiiuDeviceCert, sizeof(g_wiiuDeviceCert), output);
-1
View File
@@ -2,7 +2,6 @@
void iosuCrypto_init();
bool iosuCrypto_hasAllDataForLogin();
bool iosuCrypto_getDeviceId(uint32* deviceId);
void iosuCrypto_getDeviceSerialString(char* serialString);
+1 -1
View File
@@ -228,7 +228,7 @@ namespace iosu
}
}
auto result = NAPI::IDBE_Request(titleId);
auto result = NAPI::IDBE_Request(ActiveSettings::GetNetworkService(), titleId);
if (!result)
{
memset(idbeIconOutput, 0, sizeof(NAPI::IDBEIconDataV0));
+1 -1
View File
@@ -42,7 +42,7 @@ namespace nn
void asyncDownloadIconFile(uint64 titleId, nnIdbeEncryptedIcon_t* iconOut, OSThread_t* thread)
{
std::vector<uint8> idbeData = NAPI::IDBE_RequestRawEncrypted(titleId);
std::vector<uint8> idbeData = NAPI::IDBE_RequestRawEncrypted(ActiveSettings::GetNetworkService(), titleId);
if (idbeData.size() != sizeof(nnIdbeEncryptedIcon_t))
{
// icon does not exist or has the wrong size
@@ -43,7 +43,7 @@ namespace nn
return res;
CurlRequestHelper req;
req.initate(reqUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
req.initate(ActiveSettings::GetNetworkService(), reqUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
InitializeOliveRequest(req);
StackAllocator<coreinit::OSEvent> requestDoneEvent;
@@ -195,7 +195,7 @@ namespace nn
break;
}
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
req.initate(ActiveSettings::GetNetworkService(), requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
InitializeOliveRequest(req);
StackAllocator<coreinit::OSEvent> requestDoneEvent;
@@ -50,7 +50,7 @@ namespace nn
CurlRequestHelper req;
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
req.initate(ActiveSettings::GetNetworkService(), requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
InitializeOliveRequest(req);
StackAllocator<coreinit::OSEvent> requestDoneEvent;
@@ -40,7 +40,7 @@ namespace nn
snprintf(requestUrl, sizeof(requestUrl), "%s/v1/communities/%lu.favorite", g_DiscoveryResults.apiEndpoint, pParam->communityId.value());
CurlRequestHelper req;
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
req.initate(ActiveSettings::GetNetworkService(), requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::OLIVE);
InitializeOliveRequest(req);
StackAllocator<coreinit::OSEvent> requestDoneEvent;
@@ -33,14 +33,7 @@ void DownloadManager::downloadTitleVersionList()
{
if (m_hasTitleVersionList)
return;
NAPI::AuthInfo authInfo;
authInfo.accountId = m_authInfo.nnidAccountName;
authInfo.passwordHash = m_authInfo.passwordHash;
authInfo.deviceId = m_authInfo.deviceId;
authInfo.serial = m_authInfo.serial;
authInfo.country = m_authInfo.country;
authInfo.region = m_authInfo.region;
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
NAPI::AuthInfo authInfo = GetAuthInfo(false);
auto versionListVersionResult = NAPI::TAG_GetVersionListVersion(authInfo);
if (!versionListVersionResult.isValid)
return;
@@ -195,15 +188,7 @@ public:
bool DownloadManager::_connect_refreshIASAccountIdAndDeviceToken()
{
NAPI::AuthInfo authInfo;
authInfo.accountId = m_authInfo.nnidAccountName;
authInfo.passwordHash = m_authInfo.passwordHash;
authInfo.deviceId = m_authInfo.deviceId;
authInfo.serial = m_authInfo.serial;
authInfo.country = m_authInfo.country;
authInfo.region = m_authInfo.region;
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
NAPI::AuthInfo authInfo = GetAuthInfo(false);
// query IAS/ECS account id and device token (if not cached)
auto rChallenge = NAPI::IAS_GetChallenge(authInfo);
if (rChallenge.apiError != NAPI_RESULT::SUCCESS)
@@ -211,7 +196,6 @@ bool DownloadManager::_connect_refreshIASAccountIdAndDeviceToken()
auto rRegistrationInfo = NAPI::IAS_GetRegistrationInfo_QueryInfo(authInfo, rChallenge.challenge);
if (rRegistrationInfo.apiError != NAPI_RESULT::SUCCESS)
return false;
m_iasToken.serviceAccountId = rRegistrationInfo.accountId;
m_iasToken.deviceToken = rRegistrationInfo.deviceToken;
// store to cache
@@ -221,24 +205,13 @@ bool DownloadManager::_connect_refreshIASAccountIdAndDeviceToken()
std::vector<uint8> serializedData;
if (!storedTokenInfo.serialize(serializedData))
return false;
s_nupFileCache->AddFileAsync({ fmt::format("{}/token_info", m_authInfo.nnidAccountName) }, serializedData.data(), serializedData.size());
s_nupFileCache->AddFileAsync({ fmt::format("{}/token_info", m_authInfo.cachefileName) }, serializedData.data(), serializedData.size());
return true;
}
bool DownloadManager::_connect_queryAccountStatusAndServiceURLs()
{
NAPI::AuthInfo authInfo;
authInfo.accountId = m_authInfo.nnidAccountName;
authInfo.passwordHash = m_authInfo.passwordHash;
authInfo.deviceId = m_authInfo.deviceId;
authInfo.serial = m_authInfo.serial;
authInfo.country = m_authInfo.country;
authInfo.region = m_authInfo.region;
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
NAPI::AuthInfo authInfo = GetAuthInfo(true);
NAPI::NAPI_ECSGetAccountStatus_Result accountStatusResult = NAPI::ECS_GetAccountStatus(authInfo);
if (accountStatusResult.apiError != NAPI_RESULT::SUCCESS)
{
@@ -291,7 +264,7 @@ void DownloadManager::loadTicketCache()
m_ticketCache.clear();
cemu_assert_debug(m_ticketCache.empty());
std::vector<uint8> ticketCacheBlob;
if (!s_nupFileCache->GetFile({ fmt::format("{}/eticket_cache", m_authInfo.nnidAccountName) }, ticketCacheBlob))
if (!s_nupFileCache->GetFile({ fmt::format("{}/eticket_cache", m_authInfo.cachefileName) }, ticketCacheBlob))
return;
MemStreamReader memReader(ticketCacheBlob.data(), ticketCacheBlob.size());
uint8 version = memReader.readBE<uint8>();
@@ -343,23 +316,12 @@ void DownloadManager::storeTicketCache()
memWriter.writePODVector(cert);
}
auto serializedBlob = memWriter.getResult();
s_nupFileCache->AddFileAsync({ fmt::format("{}/eticket_cache", m_authInfo.nnidAccountName) }, serializedBlob.data(), serializedBlob.size());
s_nupFileCache->AddFileAsync({ fmt::format("{}/eticket_cache", m_authInfo.cachefileName) }, serializedBlob.data(), serializedBlob.size());
}
bool DownloadManager::syncAccountTickets()
{
NAPI::AuthInfo authInfo;
authInfo.accountId = m_authInfo.nnidAccountName;
authInfo.passwordHash = m_authInfo.passwordHash;
authInfo.deviceId = m_authInfo.deviceId;
authInfo.serial = m_authInfo.serial;
authInfo.country = m_authInfo.country;
authInfo.region = m_authInfo.region;
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
NAPI::AuthInfo authInfo = GetAuthInfo(true);
// query TIV list from server
NAPI::NAPI_ECSAccountListETicketIds_Result resultTicketIds = NAPI::ECS_AccountListETicketIds(authInfo);
if (!resultTicketIds.isValid())
@@ -425,19 +387,7 @@ bool DownloadManager::syncAccountTickets()
bool DownloadManager::syncSystemTitleTickets()
{
setStatusMessage(_("Downloading system tickets...").utf8_string(), DLMGR_STATUS_CODE::CONNECTING);
// todo - add GetAuth() function
NAPI::AuthInfo authInfo;
authInfo.accountId = m_authInfo.nnidAccountName;
authInfo.passwordHash = m_authInfo.passwordHash;
authInfo.deviceId = m_authInfo.deviceId;
authInfo.serial = m_authInfo.serial;
authInfo.country = m_authInfo.country;
authInfo.region = m_authInfo.region;
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
NAPI::AuthInfo authInfo = GetAuthInfo(true);
auto querySystemTitleTicket = [&](uint64 titleId) -> void
{
// check if cached already
@@ -520,8 +470,7 @@ bool DownloadManager::syncUpdateTickets()
if (findTicketByTitleIdAndVersion(itr.titleId, itr.availableTitleVersion))
continue;
NAPI::AuthInfo dummyAuth;
auto cetkResult = NAPI::CCS_GetCETK(dummyAuth, itr.titleId, itr.availableTitleVersion);
auto cetkResult = NAPI::CCS_GetCETK(GetDownloadMgrNetworkService(), itr.titleId, itr.availableTitleVersion);
if (!cetkResult.isValid)
continue;
NCrypto::ETicketParser ticketParser;
@@ -657,7 +606,7 @@ void DownloadManager::_handle_connect()
if (s_nupFileCache)
{
std::vector<uint8> serializationBlob;
if (s_nupFileCache->GetFile({ fmt::format("{}/token_info", m_authInfo.nnidAccountName) }, serializationBlob))
if (s_nupFileCache->GetFile({ fmt::format("{}/token_info", m_authInfo.cachefileName) }, serializationBlob))
{
StoredTokenInfo storedTokenInfo;
if (storedTokenInfo.deserialize(serializationBlob))
@@ -683,7 +632,7 @@ void DownloadManager::_handle_connect()
if (!_connect_queryAccountStatusAndServiceURLs())
{
m_connectState.store(CONNECT_STATE::FAILED);
setStatusMessage(_("Failed to query account status. Invalid account information?").utf8_string(), DLMGR_STATUS_CODE::FAILED);
setStatusMessage(_("Failed to query account status").utf8_string(), DLMGR_STATUS_CODE::FAILED);
return;
}
// load ticket cache and sync
@@ -692,7 +641,7 @@ void DownloadManager::_handle_connect()
if (!syncTicketCache())
{
m_connectState.store(CONNECT_STATE::FAILED);
setStatusMessage(_("Failed to request tickets (invalid NNID?)").utf8_string(), DLMGR_STATUS_CODE::FAILED);
setStatusMessage(_("Failed to request tickets").utf8_string(), DLMGR_STATUS_CODE::FAILED);
return;
}
searchForIncompleteDownloads();
@@ -713,22 +662,10 @@ void DownloadManager::connect(
std::string_view serial,
std::string_view deviceCertBase64)
{
if (nnidAccountName.empty())
{
m_connectState.store(CONNECT_STATE::FAILED);
setStatusMessage(_("This account is not linked with an NNID").utf8_string(), DLMGR_STATUS_CODE::FAILED);
return;
}
runManager();
m_authInfo.nnidAccountName = nnidAccountName;
m_authInfo.passwordHash = passwordHash;
if (std::all_of(m_authInfo.passwordHash.begin(), m_authInfo.passwordHash.end(), [](uint8 v) { return v == 0; }))
{
cemuLog_log(LogType::Force, "DLMgr: Invalid password hash");
m_connectState.store(CONNECT_STATE::FAILED);
setStatusMessage(_("Failed. Account does not have password set").utf8_string(), DLMGR_STATUS_CODE::FAILED);
return;
}
m_authInfo.cachefileName = nnidAccountName.empty() ? "DefaultName" : nnidAccountName;
m_authInfo.region = region;
m_authInfo.country = country;
m_authInfo.deviceCertBase64 = deviceCertBase64;
@@ -744,6 +681,31 @@ bool DownloadManager::IsConnected() const
return m_connectState.load() != CONNECT_STATE::UNINITIALIZED;
}
NetworkService DownloadManager::GetDownloadMgrNetworkService()
{
return NetworkService::Nintendo;
}
NAPI::AuthInfo DownloadManager::GetAuthInfo(bool withIasToken)
{
NAPI::AuthInfo authInfo;
authInfo.serviceOverwrite = GetDownloadMgrNetworkService();
authInfo.accountId = m_authInfo.nnidAccountName;
authInfo.passwordHash = m_authInfo.passwordHash;
authInfo.deviceId = m_authInfo.deviceId;
authInfo.serial = m_authInfo.serial;
authInfo.country = m_authInfo.country;
authInfo.region = m_authInfo.region;
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
if(withIasToken)
{
cemu_assert_debug(!m_iasToken.serviceAccountId.empty());
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
}
return authInfo;
}
/* package / downloading */
// start/resume/retry download
@@ -1022,17 +984,7 @@ void DownloadManager::reportPackageProgress(Package* package, uint32 currentProg
void DownloadManager::asyncPackageDownloadTMD(Package* package)
{
NAPI::AuthInfo authInfo;
authInfo.accountId = m_authInfo.nnidAccountName;
authInfo.passwordHash = m_authInfo.passwordHash;
authInfo.deviceId = m_authInfo.deviceId;
authInfo.serial = m_authInfo.serial;
authInfo.country = m_authInfo.country;
authInfo.region = m_authInfo.region;
authInfo.deviceCertBase64 = m_authInfo.deviceCertBase64;
authInfo.IASToken.accountId = m_iasToken.serviceAccountId;
authInfo.IASToken.deviceToken = m_iasToken.deviceToken;
NAPI::AuthInfo authInfo = GetAuthInfo(true);
TitleIdParser titleIdParser(package->titleId);
NAPI::NAPI_CCSGetTMD_Result tmdResult;
if (titleIdParser.GetType() == TitleIdParser::TITLE_TYPE::AOC)
@@ -1196,7 +1148,7 @@ void DownloadManager::asyncPackageDownloadContentFile(Package* package, uint16 i
setPackageError(package, _("Cannot create file").utf8_string());
return;
}
if (!NAPI::CCS_GetContentFile(titleId, contentId, CallbackInfo::writeCallback, &callbackInfoData))
if (!NAPI::CCS_GetContentFile(GetDownloadMgrNetworkService(), titleId, contentId, CallbackInfo::writeCallback, &callbackInfoData))
{
setPackageError(package, _("Download failed").utf8_string());
delete callbackInfoData.fileOutput;
@@ -1490,7 +1442,7 @@ void DownloadManager::prepareIDBE(uint64 titleId)
if (s_nupFileCache->GetFile({ fmt::format("idbe/{0:016x}", titleId) }, idbeFile) && idbeFile.size() == sizeof(NAPI::IDBEIconDataV0))
return addToCache(titleId, (NAPI::IDBEIconDataV0*)(idbeFile.data()));
// not cached, query from server
std::optional<NAPI::IDBEIconDataV0> iconData = NAPI::IDBE_Request(titleId);
std::optional<NAPI::IDBEIconDataV0> iconData = NAPI::IDBE_Request(GetDownloadMgrNetworkService(), titleId);
if (!iconData)
return;
s_nupFileCache->AddFileAsync({ fmt::format("idbe/{0:016x}", titleId) }, (uint8*)&(*iconData), sizeof(NAPI::IDBEIconDataV0));
@@ -2,17 +2,14 @@
#include "util/helpers/Semaphore.h"
#include "Cemu/ncrypto/ncrypto.h"
#include "Cafe/TitleList/TitleId.h"
#include "util/helpers/ConcurrentQueue.h"
#include "config/NetworkSettings.h"
#include <functional>
#include <optional>
#include <future>
// forward declarations
namespace NAPI
{
struct IDBEIconDataV0;
struct AuthInfo;
}
namespace NCrypto
@@ -86,7 +83,6 @@ public:
bool IsConnected() const;
private:
/* connect / login */
@@ -101,6 +97,7 @@ private:
struct
{
std::string cachefileName;
std::string nnidAccountName;
std::array<uint8, 32> passwordHash;
std::string deviceCertBase64;
@@ -122,7 +119,10 @@ private:
void _handle_connect();
bool _connect_refreshIASAccountIdAndDeviceToken();
bool _connect_queryAccountStatusAndServiceURLs();
NetworkService GetDownloadMgrNetworkService();
NAPI::AuthInfo GetAuthInfo(bool withIasToken);
/* idbe cache */
public:
void prepareIDBE(uint64 titleId);
+13 -10
View File
@@ -1,6 +1,7 @@
#pragma once
#include <optional>
#include "config/CemuConfig.h" // for ConsoleLanguage
#include "config/NetworkSettings.h" // for NetworkService
#include "config/ActiveSettings.h" // for GetNetworkService()
enum class NAPI_RESULT
{
@@ -16,8 +17,6 @@ namespace NAPI
// common auth info structure shared by ACT, ECS and IAS service
struct AuthInfo
{
// todo - constructor for account name + raw password
// nnid
std::string accountId;
std::array<uint8, 32> passwordHash;
@@ -41,9 +40,13 @@ namespace NAPI
std::string deviceToken;
}IASToken;
// ACT token (for account.nintendo.net requests)
// service selection, if not set fall back to global setting
std::optional<NetworkService> serviceOverwrite;
NetworkService GetService() const
{
return serviceOverwrite.value_or(ActiveSettings::GetNetworkService());
}
};
bool NAPI_MakeAuthInfoFromCurrentAccount(AuthInfo& authInfo); // helper function. Returns false if online credentials/dumped files are not available
@@ -232,9 +235,9 @@ namespace NAPI
NAPI_CCSGetTMD_Result CCS_GetTMD(AuthInfo& authInfo, uint64 titleId, uint16 titleVersion);
NAPI_CCSGetTMD_Result CCS_GetTMD(AuthInfo& authInfo, uint64 titleId);
NAPI_CCSGetETicket_Result CCS_GetCETK(AuthInfo& authInfo, uint64 titleId, uint16 titleVersion);
bool CCS_GetContentFile(uint64 titleId, uint32 contentId, bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData);
NAPI_CCSGetContentH3_Result CCS_GetContentH3File(uint64 titleId, uint32 contentId);
NAPI_CCSGetETicket_Result CCS_GetCETK(NetworkService service, uint64 titleId, uint16 titleVersion);
bool CCS_GetContentFile(NetworkService service, uint64 titleId, uint32 contentId, bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData);
NAPI_CCSGetContentH3_Result CCS_GetContentH3File(NetworkService service, uint64 titleId, uint32 contentId);
/* IDBE */
@@ -286,8 +289,8 @@ namespace NAPI
static_assert(sizeof(IDBEHeader) == 2+32);
std::optional<IDBEIconDataV0> IDBE_Request(uint64 titleId);
std::vector<uint8> IDBE_RequestRawEncrypted(uint64 titleId); // same as IDBE_Request but doesn't strip the header and decrypt the IDBE
std::optional<IDBEIconDataV0> IDBE_Request(NetworkService networkService, uint64 titleId);
std::vector<uint8> IDBE_RequestRawEncrypted(NetworkService networkService, uint64 titleId); // same as IDBE_Request but doesn't strip the header and decrypt the IDBE
/* Version list */
+33 -16
View File
@@ -14,6 +14,21 @@
namespace NAPI
{
std::string _getACTUrl(NetworkService service)
{
switch (service)
{
case NetworkService::Nintendo:
return NintendoURLs::ACTURL;
case NetworkService::Pretendo:
return PretendoURLs::ACTURL;
case NetworkService::Custom:
return GetNetworkConfig().urls.ACT.GetValue();
default:
return NintendoURLs::ACTURL;
}
}
struct ACTOauthToken : public _NAPI_CommonResultACT
{
std::string token;
@@ -91,7 +106,7 @@ namespace NAPI
struct OAuthTokenCacheEntry
{
OAuthTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, std::string_view token, std::string_view refreshToken, uint64 expiresIn) : accountId(accountId), passwordHash(passwordHash), token(token), refreshToken(refreshToken)
OAuthTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, std::string_view token, std::string_view refreshToken, uint64 expiresIn, NetworkService service) : accountId(accountId), passwordHash(passwordHash), token(token), refreshToken(refreshToken), service(service)
{
expires = HighResolutionTimer::now().getTickInSeconds() + expiresIn;
};
@@ -107,10 +122,10 @@ namespace NAPI
}
std::string accountId;
std::array<uint8, 32> passwordHash;
std::string token;
std::string refreshToken;
uint64 expires;
NetworkService service;
};
std::vector<OAuthTokenCacheEntry> g_oauthTokenCache;
@@ -122,11 +137,12 @@ namespace NAPI
ACTOauthToken result{};
// check cache first
NetworkService service = authInfo.GetService();
g_oauthTokenCacheMtx.lock();
auto cacheItr = g_oauthTokenCache.begin();
while (cacheItr != g_oauthTokenCache.end())
{
if (cacheItr->CheckIfSameAccount(authInfo))
if (cacheItr->CheckIfSameAccount(authInfo) && cacheItr->service == service)
{
if (cacheItr->CheckIfExpired())
{
@@ -145,7 +161,7 @@ namespace NAPI
// token not cached, request from server via oauth2
CurlRequestHelper req;
req.initate(fmt::format("{}/v1/api/oauth20/access_token/generate", LaunchSettings::GetActURLPrefix()), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/oauth20/access_token/generate", _getACTUrl(authInfo.GetService())), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
_ACTSetCommonHeaderParameters(req, authInfo);
_ACTSetDeviceParameters(req, authInfo);
_ACTSetRegionAndCountryParameters(req, authInfo);
@@ -220,7 +236,7 @@ namespace NAPI
if (expiration > 0)
{
g_oauthTokenCacheMtx.lock();
g_oauthTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, result.token, result.refreshToken, expiration);
g_oauthTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, result.token, result.refreshToken, expiration, service);
g_oauthTokenCacheMtx.unlock();
}
return result;
@@ -230,14 +246,13 @@ namespace NAPI
{
CurlRequestHelper req;
req.initate(fmt::format("{}/v1/api/people/@me/profile", LaunchSettings::GetActURLPrefix()), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/people/@me/profile", _getACTUrl(authInfo.GetService())), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
_ACTSetCommonHeaderParameters(req, authInfo);
_ACTSetDeviceParameters(req, authInfo);
// get oauth2 token
ACTOauthToken oauthToken = ACT_GetOauthToken_WithCache(authInfo, 0x0005001010001C00, 0x0001C);
cemu_assert_unimplemented();
return true;
@@ -245,15 +260,16 @@ namespace NAPI
struct NexTokenCacheEntry
{
NexTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, uint32 gameServerId, ACTNexToken& nexToken) : accountId(accountId), passwordHash(passwordHash), nexToken(nexToken), gameServerId(gameServerId) {};
NexTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, NetworkService networkService, uint32 gameServerId, ACTNexToken& nexToken) : accountId(accountId), passwordHash(passwordHash), networkService(networkService), nexToken(nexToken), gameServerId(gameServerId) {};
bool IsMatch(const AuthInfo& authInfo, const uint32 gameServerId) const
{
return authInfo.accountId == accountId && authInfo.passwordHash == passwordHash && this->gameServerId == gameServerId;
return authInfo.accountId == accountId && authInfo.passwordHash == passwordHash && authInfo.GetService() == networkService && this->gameServerId == gameServerId;
}
std::string accountId;
std::array<uint8, 32> passwordHash;
NetworkService networkService;
uint32 gameServerId;
ACTNexToken nexToken;
@@ -297,7 +313,7 @@ namespace NAPI
}
// do request
CurlRequestHelper req;
req.initate(fmt::format("{}/v1/api/provider/nex_token/@me?game_server_id={:08X}", LaunchSettings::GetActURLPrefix(), serverId), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/provider/nex_token/@me?game_server_id={:08X}", _getACTUrl(authInfo.GetService()), serverId), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
_ACTSetCommonHeaderParameters(req, authInfo);
_ACTSetDeviceParameters(req, authInfo);
_ACTSetRegionAndCountryParameters(req, authInfo);
@@ -374,21 +390,21 @@ namespace NAPI
result.nexToken.port = (uint16)StringHelpers::ToInt(port);
result.apiError = NAPI_RESULT::SUCCESS;
g_nexTokenCacheMtx.lock();
g_nexTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, serverId, result.nexToken);
g_nexTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, authInfo.GetService(), serverId, result.nexToken);
g_nexTokenCacheMtx.unlock();
return result;
}
struct IndependentTokenCacheEntry
{
IndependentTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, std::string_view clientId, std::string_view independentToken, sint64 expiresIn) : accountId(accountId), passwordHash(passwordHash), clientId(clientId), independentToken(independentToken)
IndependentTokenCacheEntry(std::string_view accountId, std::array<uint8, 32>& passwordHash, NetworkService networkService, std::string_view clientId, std::string_view independentToken, sint64 expiresIn) : accountId(accountId), passwordHash(passwordHash), networkService(networkService), clientId(clientId), independentToken(independentToken)
{
expires = HighResolutionTimer::now().getTickInSeconds() + expiresIn;
};
bool IsMatch(const AuthInfo& authInfo, const std::string_view clientId) const
{
return authInfo.accountId == accountId && authInfo.passwordHash == passwordHash && this->clientId == clientId;
return authInfo.accountId == accountId && authInfo.passwordHash == passwordHash && authInfo.GetService() == networkService && this->clientId == clientId;
}
bool CheckIfExpired() const
@@ -398,6 +414,7 @@ namespace NAPI
std::string accountId;
std::array<uint8, 32> passwordHash;
NetworkService networkService;
std::string clientId;
sint64 expires;
@@ -449,7 +466,7 @@ namespace NAPI
}
// do request
CurlRequestHelper req;
req.initate(fmt::format("{}/v1/api/provider/service_token/@me?client_id={}", LaunchSettings::GetActURLPrefix(), clientId), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/provider/service_token/@me?client_id={}", _getACTUrl(authInfo.GetService()), clientId), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
_ACTSetCommonHeaderParameters(req, authInfo);
_ACTSetDeviceParameters(req, authInfo);
_ACTSetRegionAndCountryParameters(req, authInfo);
@@ -494,7 +511,7 @@ namespace NAPI
result.apiError = NAPI_RESULT::SUCCESS;
g_IndependentTokenCacheMtx.lock();
g_IndependentTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, clientId, result.token, 3600);
g_IndependentTokenCache.emplace_back(authInfo.accountId, authInfo.passwordHash, authInfo.GetService(), clientId, result.token, 3600);
g_IndependentTokenCacheMtx.unlock();
return result;
}
@@ -520,7 +537,7 @@ namespace NAPI
}
// do request
CurlRequestHelper req;
req.initate(fmt::format("{}/v1/api/admin/mapped_ids?input_type=user_id&output_type=pid&input={}", LaunchSettings::GetActURLPrefix(), nnid), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
req.initate(authInfo.GetService(), fmt::format("{}/v1/api/admin/mapped_ids?input_type=user_id&output_type=pid&input={}", _getACTUrl(authInfo.GetService()), nnid), CurlRequestHelper::SERVER_SSL_CONTEXT::ACT);
_ACTSetCommonHeaderParameters(req, authInfo);
_ACTSetDeviceParameters(req, authInfo);
_ACTSetRegionAndCountryParameters(req, authInfo);
+119 -110
View File
@@ -16,103 +16,112 @@
namespace NAPI
{
/* Service URL manager */
std::string s_serviceURL_ContentPrefixURL;
std::string s_serviceURL_UncachedContentPrefixURL;
std::string s_serviceURL_EcsURL;
std::string s_serviceURL_IasURL;
std::string s_serviceURL_CasURL;
std::string s_serviceURL_NusURL;
std::string _getNUSUrl()
struct CachedServiceUrls
{
if (!s_serviceURL_NusURL.empty())
return s_serviceURL_NusURL;
switch (ActiveSettings::GetNetworkService())
{
case NetworkService::Nintendo:
return NintendoURLs::NUSURL;
break;
case NetworkService::Pretendo:
return PretendoURLs::NUSURL;
break;
case NetworkService::Custom:
return GetNetworkConfig().urls.NUS;
break;
default:
return NintendoURLs::NUSURL;
break;
}
std::string s_serviceURL_ContentPrefixURL;
std::string s_serviceURL_UncachedContentPrefixURL;
std::string s_serviceURL_EcsURL;
std::string s_serviceURL_IasURL;
std::string s_serviceURL_CasURL;
std::string s_serviceURL_NusURL;
};
std::unordered_map<NetworkService, CachedServiceUrls> s_cachedServiceUrlsMap;
CachedServiceUrls& GetCachedServiceUrls(NetworkService service)
{
return s_cachedServiceUrlsMap[service];
}
std::string _getIASUrl()
std::string _getNUSUrl(NetworkService service)
{
if (!s_serviceURL_IasURL.empty())
return s_serviceURL_IasURL;
switch (ActiveSettings::GetNetworkService())
{
case NetworkService::Nintendo:
return NintendoURLs::IASURL;
break;
case NetworkService::Pretendo:
return PretendoURLs::IASURL;
break;
case NetworkService::Custom:
return GetNetworkConfig().urls.IAS;
break;
default:
return NintendoURLs::IASURL;
break;
}
auto& cachedServiceUrls = GetCachedServiceUrls(service);
if (!cachedServiceUrls.s_serviceURL_NusURL.empty())
return cachedServiceUrls.s_serviceURL_NusURL;
switch (service)
{
case NetworkService::Nintendo:
return NintendoURLs::NUSURL;
case NetworkService::Pretendo:
return PretendoURLs::NUSURL;
case NetworkService::Custom:
return GetNetworkConfig().urls.NUS;
default:
return NintendoURLs::NUSURL;
}
}
std::string _getECSUrl()
std::string _getIASUrl(NetworkService service)
{
auto& cachedServiceUrls = GetCachedServiceUrls(service);
if (!cachedServiceUrls.s_serviceURL_IasURL.empty())
return cachedServiceUrls.s_serviceURL_IasURL;
switch (service)
{
case NetworkService::Nintendo:
return NintendoURLs::IASURL;
case NetworkService::Pretendo:
return PretendoURLs::IASURL;
case NetworkService::Custom:
return GetNetworkConfig().urls.IAS;
default:
return NintendoURLs::IASURL;
}
}
std::string _getECSUrl(NetworkService service)
{
// this is the first url queried (GetAccountStatus). The others are dynamically set if provided by the server but will fallback to hardcoded defaults otherwise
if (!s_serviceURL_EcsURL.empty())
return s_serviceURL_EcsURL;
return LaunchSettings::GetServiceURL_ecs(); // by default this is "https://ecs.wup.shop.nintendo.net/ecs/services/ECommerceSOAP"
auto& cachedServiceUrls = GetCachedServiceUrls(service);
if (!cachedServiceUrls.s_serviceURL_EcsURL.empty())
return cachedServiceUrls.s_serviceURL_EcsURL;
switch (service)
{
case NetworkService::Nintendo:
return NintendoURLs::ECSURL;
case NetworkService::Pretendo:
return PretendoURLs::ECSURL;
case NetworkService::Custom:
return GetNetworkConfig().urls.ECS;
default:
return NintendoURLs::ECSURL;
}
}
std::string _getCCSUncachedUrl() // used for TMD requests
std::string _getCCSUncachedUrl(NetworkService service) // used for TMD requests
{
if (!s_serviceURL_UncachedContentPrefixURL.empty())
return s_serviceURL_UncachedContentPrefixURL;
switch (ActiveSettings::GetNetworkService())
{
case NetworkService::Nintendo:
return NintendoURLs::CCSUURL;
break;
case NetworkService::Pretendo:
return PretendoURLs::CCSUURL;
break;
case NetworkService::Custom:
return GetNetworkConfig().urls.CCSU;
break;
default:
return NintendoURLs::CCSUURL;
break;
}
auto& cachedServiceUrls = GetCachedServiceUrls(service);
if (!cachedServiceUrls.s_serviceURL_UncachedContentPrefixURL.empty())
return cachedServiceUrls.s_serviceURL_UncachedContentPrefixURL;
switch (service)
{
case NetworkService::Nintendo:
return NintendoURLs::CCSUURL;
case NetworkService::Pretendo:
return PretendoURLs::CCSUURL;
case NetworkService::Custom:
return GetNetworkConfig().urls.CCSU;
default:
return NintendoURLs::CCSUURL;
}
}
std::string _getCCSUrl() // used for game data downloads
std::string _getCCSUrl(NetworkService service) // used for game data downloads
{
if (!s_serviceURL_ContentPrefixURL.empty())
return s_serviceURL_ContentPrefixURL;
switch (ActiveSettings::GetNetworkService())
{
case NetworkService::Nintendo:
return NintendoURLs::CCSURL;
break;
case NetworkService::Pretendo:
return PretendoURLs::CCSURL;
break;
case NetworkService::Custom:
return GetNetworkConfig().urls.CCS;
break;
default:
return NintendoURLs::CCSURL;
break;
}
auto& cachedServiceUrls = GetCachedServiceUrls(service);
if (!cachedServiceUrls.s_serviceURL_ContentPrefixURL.empty())
return cachedServiceUrls.s_serviceURL_ContentPrefixURL;
switch (service)
{
case NetworkService::Nintendo:
return NintendoURLs::CCSURL;
case NetworkService::Pretendo:
return PretendoURLs::CCSURL;
case NetworkService::Custom:
return GetNetworkConfig().urls.CCS;
default:
return NintendoURLs::CCSURL;
}
}
/* NUS */
@@ -122,8 +131,8 @@ namespace NAPI
{
NAPI_NUSGetSystemCommonETicket_Result result{};
CurlSOAPHelper soapHelper;
soapHelper.SOAP_initate("nus", _getNUSUrl(), "GetSystemCommonETicket", "1.0");
CurlSOAPHelper soapHelper(authInfo.GetService());
soapHelper.SOAP_initate("nus", _getNUSUrl(authInfo.GetService()), "GetSystemCommonETicket", "1.0");
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
soapHelper.SOAP_addRequestField("RegionId", NCrypto::GetRegionAsString(authInfo.region));
@@ -175,8 +184,8 @@ namespace NAPI
{
NAPI_IASGetChallenge_Result result{};
CurlSOAPHelper soapHelper;
soapHelper.SOAP_initate("ias", _getIASUrl(), "GetChallenge", "2.0");
CurlSOAPHelper soapHelper(authInfo.GetService());
soapHelper.SOAP_initate("ias", _getIASUrl(authInfo.GetService()), "GetChallenge", "2.0");
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform())); // not validated but the generated Challenge is bound to this DeviceId
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
@@ -200,8 +209,8 @@ namespace NAPI
NAPI_IASGetRegistrationInfo_Result IAS_GetRegistrationInfo_QueryInfo(AuthInfo& authInfo, std::string challenge)
{
NAPI_IASGetRegistrationInfo_Result result;
CurlSOAPHelper soapHelper;
soapHelper.SOAP_initate("ias", _getIASUrl(), "GetRegistrationInfo", "2.0");
CurlSOAPHelper soapHelper(authInfo.GetService());
soapHelper.SOAP_initate("ias", _getIASUrl(authInfo.GetService()), "GetRegistrationInfo", "2.0");
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform())); // this must match the DeviceId used to generate Challenge
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
@@ -301,8 +310,8 @@ namespace NAPI
{
NAPI_ECSGetAccountStatus_Result result{};
CurlSOAPHelper soapHelper;
soapHelper.SOAP_initate("ecs", _getECSUrl(), "GetAccountStatus", "2.0");
CurlSOAPHelper soapHelper(authInfo.GetService());
soapHelper.SOAP_initate("ecs", _getECSUrl(authInfo.GetService()), "GetAccountStatus", "2.0");
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
@@ -367,19 +376,19 @@ namespace NAPI
}
// assign service URLs
auto& cachedServiceUrls = GetCachedServiceUrls(authInfo.GetService());
if (!result.serviceURLs.ContentPrefixURL.empty())
s_serviceURL_ContentPrefixURL = result.serviceURLs.ContentPrefixURL;
cachedServiceUrls.s_serviceURL_ContentPrefixURL = result.serviceURLs.ContentPrefixURL;
if (!result.serviceURLs.UncachedContentPrefixURL.empty())
s_serviceURL_UncachedContentPrefixURL = result.serviceURLs.UncachedContentPrefixURL;
cachedServiceUrls.s_serviceURL_UncachedContentPrefixURL = result.serviceURLs.UncachedContentPrefixURL;
if (!result.serviceURLs.IasURL.empty())
s_serviceURL_IasURL = result.serviceURLs.IasURL;
cachedServiceUrls.s_serviceURL_IasURL = result.serviceURLs.IasURL;
if (!result.serviceURLs.CasURL.empty())
s_serviceURL_CasURL = result.serviceURLs.CasURL;
cachedServiceUrls.s_serviceURL_CasURL = result.serviceURLs.CasURL;
if (!result.serviceURLs.NusURL.empty())
s_serviceURL_NusURL = result.serviceURLs.NusURL;
cachedServiceUrls.s_serviceURL_NusURL = result.serviceURLs.NusURL;
if (!result.serviceURLs.EcsURL.empty())
s_serviceURL_EcsURL = result.serviceURLs.EcsURL;
cachedServiceUrls.s_serviceURL_EcsURL = result.serviceURLs.EcsURL;
return result;
}
@@ -387,8 +396,8 @@ namespace NAPI
{
NAPI_ECSAccountListETicketIds_Result result{};
CurlSOAPHelper soapHelper;
soapHelper.SOAP_initate("ecs", _getECSUrl(), "AccountListETicketIds", "2.0");
CurlSOAPHelper soapHelper(authInfo.GetService());
soapHelper.SOAP_initate("ecs", _getECSUrl(authInfo.GetService()), "AccountListETicketIds", "2.0");
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
@@ -446,8 +455,8 @@ namespace NAPI
{
NAPI_ECSAccountGetETickets_Result result{};
CurlSOAPHelper soapHelper;
soapHelper.SOAP_initate("ecs", _getECSUrl(), "AccountGetETickets", "2.0");
CurlSOAPHelper soapHelper(authInfo.GetService());
soapHelper.SOAP_initate("ecs", _getECSUrl(authInfo.GetService()), "AccountGetETickets", "2.0");
soapHelper.SOAP_addRequestField("DeviceId", fmt::format("{}", authInfo.getDeviceIdWithPlatform()));
soapHelper.SOAP_addRequestField("Region", NCrypto::GetRegionAsString(authInfo.region));
@@ -512,7 +521,7 @@ namespace NAPI
{
NAPI_CCSGetTMD_Result result{};
CurlRequestHelper req;
req.initate(fmt::format("{}/{:016x}/tmd.{}?deviceId={}&accountId={}", _getCCSUncachedUrl(), titleId, titleVersion, authInfo.getDeviceIdWithPlatform(), authInfo.IASToken.accountId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.initate(authInfo.GetService(), fmt::format("{}/{:016x}/tmd.{}?deviceId={}&accountId={}", _getCCSUncachedUrl(authInfo.GetService()), titleId, titleVersion, authInfo.getDeviceIdWithPlatform(), authInfo.IASToken.accountId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.setTimeout(180);
if (!req.submitRequest(false))
{
@@ -528,7 +537,7 @@ namespace NAPI
{
NAPI_CCSGetTMD_Result result{};
CurlRequestHelper req;
req.initate(fmt::format("{}/{:016x}/tmd?deviceId={}&accountId={}", _getCCSUncachedUrl(), titleId, authInfo.getDeviceIdWithPlatform(), authInfo.IASToken.accountId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.initate(authInfo.GetService(), fmt::format("{}/{:016x}/tmd?deviceId={}&accountId={}", _getCCSUncachedUrl(authInfo.GetService()), titleId, authInfo.getDeviceIdWithPlatform(), authInfo.IASToken.accountId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.setTimeout(180);
if (!req.submitRequest(false))
{
@@ -540,11 +549,11 @@ namespace NAPI
return result;
}
NAPI_CCSGetETicket_Result CCS_GetCETK(AuthInfo& authInfo, uint64 titleId, uint16 titleVersion)
NAPI_CCSGetETicket_Result CCS_GetCETK(NetworkService service, uint64 titleId, uint16 titleVersion)
{
NAPI_CCSGetETicket_Result result{};
CurlRequestHelper req;
req.initate(fmt::format("{}/{:016x}/cetk", _getCCSUncachedUrl(), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.initate(service, fmt::format("{}/{:016x}/cetk", _getCCSUncachedUrl(service), titleId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.setTimeout(180);
if (!req.submitRequest(false))
{
@@ -556,10 +565,10 @@ namespace NAPI
return result;
}
bool CCS_GetContentFile(uint64 titleId, uint32 contentId, bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData)
bool CCS_GetContentFile(NetworkService service, uint64 titleId, uint32 contentId, bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData)
{
CurlRequestHelper req;
req.initate(fmt::format("{}/{:016x}/{:08x}", _getCCSUrl(), titleId, contentId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.initate(service, fmt::format("{}/{:016x}/{:08x}", _getCCSUrl(service), titleId, contentId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.setWriteCallback(cbWriteCallback, userData);
req.setTimeout(0);
if (!req.submitRequest(false))
@@ -570,11 +579,11 @@ namespace NAPI
return true;
}
NAPI_CCSGetContentH3_Result CCS_GetContentH3File(uint64 titleId, uint32 contentId)
NAPI_CCSGetContentH3_Result CCS_GetContentH3File(NetworkService service, uint64 titleId, uint32 contentId)
{
NAPI_CCSGetContentH3_Result result{};
CurlRequestHelper req;
req.initate(fmt::format("{}/{:016x}/{:08x}.h3", _getCCSUrl(), titleId, contentId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
req.initate(service, fmt::format("{}/{:016x}/{:08x}.h3", _getCCSUrl(service), titleId, contentId), CurlRequestHelper::SERVER_SSL_CONTEXT::CCS);
if (!req.submitRequest(false))
{
cemuLog_log(LogType::Force, fmt::format("Failed to request content hash file {:08x}.h3 for title {:016X}", contentId, titleId));
+16 -8
View File
@@ -119,7 +119,7 @@ CurlRequestHelper::~CurlRequestHelper()
curl_easy_cleanup(m_curl);
}
void CurlRequestHelper::initate(std::string url, SERVER_SSL_CONTEXT sslContext)
void CurlRequestHelper::initate(NetworkService service, std::string url, SERVER_SSL_CONTEXT sslContext)
{
// reset parameters
m_headerExtraFields.clear();
@@ -131,8 +131,10 @@ void CurlRequestHelper::initate(std::string url, SERVER_SSL_CONTEXT sslContext)
curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, 60);
// SSL
if (GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() == NetworkService::Custom || ActiveSettings::GetNetworkService() == NetworkService::Pretendo){ //Remove once Pretendo has SSL
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 1L);
if (IsNetworkServiceSSLDisabled(service))
{
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L);
}
else if (sslContext == SERVER_SSL_CONTEXT::ACT || sslContext == SERVER_SSL_CONTEXT::TAGAYA)
{
@@ -256,18 +258,24 @@ bool CurlRequestHelper::submitRequest(bool isPost)
return true;
}
CurlSOAPHelper::CurlSOAPHelper()
CurlSOAPHelper::CurlSOAPHelper(NetworkService service)
{
m_curl = curl_easy_init();
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, __curlWriteCallback);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, this);
// SSL
if (!GetNetworkConfig().disablesslver.GetValue() && ActiveSettings::GetNetworkService() != NetworkService::Pretendo && ActiveSettings::GetNetworkService() != NetworkService::Custom) { //Remove once Pretendo has SSL
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_SOAP);
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL);
if (!IsNetworkServiceSSLDisabled(service))
{
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_SOAP);
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL);
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 1L);
}
if(GetConfig().proxy_server.GetValue() != "")
else
{
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L);
}
if (GetConfig().proxy_server.GetValue() != "")
{
curl_easy_setopt(m_curl, CURLOPT_PROXY, GetConfig().proxy_server.GetValue().c_str());
}
+2 -2
View File
@@ -38,7 +38,7 @@ public:
return m_curl;
}
void initate(std::string url, SERVER_SSL_CONTEXT sslContext);
void initate(NetworkService service, std::string url, SERVER_SSL_CONTEXT sslContext);
void addHeaderField(const char* fieldName, std::string_view value);
void addPostField(const char* fieldName, std::string_view value);
void setWriteCallback(bool(*cbWriteCallback)(void* userData, const void* ptr, size_t len, bool isLast), void* userData);
@@ -74,7 +74,7 @@ private:
class CurlSOAPHelper // todo - make this use CurlRequestHelper
{
public:
CurlSOAPHelper();
CurlSOAPHelper(NetworkService service);
~CurlSOAPHelper();
CURL* getCURL()
+5 -5
View File
@@ -54,11 +54,11 @@ namespace NAPI
AES128_CBC_decrypt((uint8*)iconData, (uint8*)iconData, sizeof(IDBEIconDataV0), aesKey, iv);
}
std::vector<uint8> IDBE_RequestRawEncrypted(uint64 titleId)
std::vector<uint8> IDBE_RequestRawEncrypted(NetworkService networkService, uint64 titleId)
{
CurlRequestHelper req;
std::string requestUrl;
switch (ActiveSettings::GetNetworkService())
switch (networkService)
{
case NetworkService::Pretendo:
requestUrl = PretendoURLs::IDBEURL;
@@ -72,7 +72,7 @@ namespace NAPI
break;
}
requestUrl.append(fmt::format(fmt::runtime("/{0:02X}/{1:016X}.idbe"), (uint32)((titleId >> 8) & 0xFF), titleId));
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
req.initate(networkService, requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::IDBE);
if (!req.submitRequest(false))
{
@@ -90,7 +90,7 @@ namespace NAPI
return receivedData;
}
std::optional<IDBEIconDataV0> IDBE_Request(uint64 titleId)
std::optional<IDBEIconDataV0> IDBE_Request(NetworkService networkService, uint64 titleId)
{
if (titleId == 0x000500301001500A ||
titleId == 0x000500301001510A ||
@@ -101,7 +101,7 @@ namespace NAPI
return std::nullopt;
}
std::vector<uint8> idbeData = IDBE_RequestRawEncrypted(titleId);
std::vector<uint8> idbeData = IDBE_RequestRawEncrypted(networkService, titleId);
if (idbeData.size() < 0x22)
return std::nullopt;
if (idbeData[0] != 0)
+3 -3
View File
@@ -18,7 +18,7 @@ namespace NAPI
CurlRequestHelper req;
std::string requestUrl;
switch (ActiveSettings::GetNetworkService())
switch (authInfo.GetService())
{
case NetworkService::Pretendo:
requestUrl = PretendoURLs::TAGAYAURL;
@@ -32,7 +32,7 @@ namespace NAPI
break;
}
requestUrl.append(fmt::format(fmt::runtime("/{}/{}/latest_version"), NCrypto::GetRegionAsString(authInfo.region), authInfo.country));
req.initate(requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
req.initate(authInfo.GetService(), requestUrl, CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
if (!req.submitRequest(false))
{
@@ -63,7 +63,7 @@ namespace NAPI
{
NAPI_VersionList_Result result;
CurlRequestHelper req;
req.initate(fmt::format("https://{}/tagaya/versionlist/{}/{}/list/{}.versionlist", fqdnURL, NCrypto::GetRegionAsString(authInfo.region), authInfo.country, versionListVersion), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
req.initate(authInfo.GetService(), fmt::format("https://{}/tagaya/versionlist/{}/{}/list/{}.versionlist", fqdnURL, NCrypto::GetRegionAsString(authInfo.region), authInfo.country, versionListVersion), CurlRequestHelper::SERVER_SSL_CONTEXT::TAGAYA);
if (!req.submitRequest(false))
{
cemuLog_log(LogType::Force, fmt::format("Failed to request update list"));
+17 -1
View File
@@ -20,7 +20,8 @@ void iosuCrypto_readOtpData(void* output, sint32 wordIndex, sint32 size);
void iosuCrypto_readSeepromData(void* output, sint32 wordIndex, sint32 size);
extern bool hasSeepromMem; // remove later
extern bool hasSeepromMem; // remove later (migrate otp/seeprom loading & parsing to this class)
extern bool hasOtpMem; // remove later
namespace NCrypto
{
@@ -792,6 +793,16 @@ namespace NCrypto
return (CafeConsoleRegion)seepromRegionU32[3];
}
bool OTP_IsPresent()
{
return hasOtpMem;
}
bool HasDataForConsoleCert()
{
return SEEPROM_IsPresent() && OTP_IsPresent();
}
std::string GetRegionAsString(CafeConsoleRegion regionCode)
{
if (regionCode == CafeConsoleRegion::EUR)
@@ -957,6 +968,11 @@ namespace NCrypto
return it->second;
}
size_t GetCountryCount()
{
return g_countryTable.size();
}
void unitTests()
{
base64Tests();
+4
View File
@@ -205,7 +205,11 @@ namespace NCrypto
CafeConsoleRegion SEEPROM_GetRegion();
std::string GetRegionAsString(CafeConsoleRegion regionCode);
const char* GetCountryAsString(sint32 index); // returns NN if index is not valid or known
size_t GetCountryCount();
bool SEEPROM_IsPresent();
bool OTP_IsPresent();
bool HasDataForConsoleCert();
void unitTests();
}

Some files were not shown because too many files have changed in this diff Show More