Skip to content

Commit

Permalink
DownloadManager: Always use Nintendo servers + additional streamlining
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
Exzap committed Apr 20, 2024
1 parent 989e2b8 commit efbbb81
Show file tree
Hide file tree
Showing 29 changed files with 323 additions and 338 deletions.
2 changes: 1 addition & 1 deletion src/Cafe/IOSU/legacy/iosu_boss.cpp
Expand Up @@ -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);
}
Expand Down
10 changes: 0 additions & 10 deletions src/Cafe/IOSU/legacy/iosu_crypto.cpp
Expand Up @@ -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);
Expand Down
1 change: 0 additions & 1 deletion src/Cafe/IOSU/legacy/iosu_crypto.h
Expand Up @@ -2,7 +2,6 @@

void iosuCrypto_init();

bool iosuCrypto_hasAllDataForLogin();
bool iosuCrypto_getDeviceId(uint32* deviceId);
void iosuCrypto_getDeviceSerialString(char* serialString);

Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/IOSU/legacy/iosu_nim.cpp
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nn_idbe/nn_idbe.cpp
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nn_olv/nn_olv_InitializeTypes.cpp
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp
Expand Up @@ -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;
Expand Down
130 changes: 41 additions & 89 deletions src/Cemu/Tools/DownloadManager/DownloadManager.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -195,23 +188,14 @@ struct StoredTokenInfo : public SerializerHelper

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)
return false;
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
Expand All @@ -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)
{
Expand Down Expand Up @@ -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>();
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand All @@ -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
Expand All @@ -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();
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down
16 changes: 8 additions & 8 deletions src/Cemu/Tools/DownloadManager/DownloadManager.h
Expand Up @@ -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
Expand Down Expand Up @@ -86,7 +83,6 @@ class DownloadManager

bool IsConnected() const;


private:
/* connect / login */

Expand All @@ -101,6 +97,7 @@ class DownloadManager

struct
{
std::string cachefileName;
std::string nnidAccountName;
std::array<uint8, 32> passwordHash;
std::string deviceCertBase64;
Expand All @@ -122,7 +119,10 @@ class DownloadManager
void _handle_connect();
bool _connect_refreshIASAccountIdAndDeviceToken();
bool _connect_queryAccountStatusAndServiceURLs();


NetworkService GetDownloadMgrNetworkService();
NAPI::AuthInfo GetAuthInfo(bool withIasToken);

/* idbe cache */
public:
void prepareIDBE(uint64 titleId);
Expand Down

0 comments on commit efbbb81

Please sign in to comment.