89 lines
4.1 KiB
C
89 lines
4.1 KiB
C
// Entwickler-Werkzeuge nur für Admins - und nur, wenn der Server es gerade bestätigt hat:
|
|
// 1. Der Konto-Dienst (act) holt beim MBMNET-Account-Server einen signierten Anmelde-Nachweis (Service-Token)
|
|
// für das angemeldete MBMNET-Konto. Den kann nur ausstellen, wer mit diesem Konto angemeldet ist.
|
|
// 2. Die App schickt ihn per HTTPS (MBMNET-CA) an /mbmnet/admin-check. Der Server prüft die Signatur und
|
|
// antwortet admin=1 nur für Admins.
|
|
// Nichts wird gemerkt: Ohne Antwort vom Server (offline, Nintendo aktiv, kein MBMNET-Konto) keine Werkzeuge.
|
|
#include <3ds.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "admin.h"
|
|
#include "https.h"
|
|
|
|
#define OLD_CACHE_FILE "sdmc:/3ds/mbmnet/admin" // gemerkte Antwort älterer Versionen (wird gelöscht)
|
|
// Client-ID für den Nachweis (die von Miiverse; der MBMNET-Server nimmt jede)
|
|
static char CLIENT_ID[] = "87cd32617f1985439ea608c2746e4610";
|
|
|
|
static volatile bool isAdmin;
|
|
static Thread thread;
|
|
static char server[64];
|
|
static char why[96] = "Werkzeuge: kein MBMNET-Konto angemeldet."; // warum (noch) keine Werkzeuge
|
|
|
|
static void worker(void *arg)
|
|
{
|
|
(void)arg;
|
|
// 1. Anmelde-Nachweis vom Konto-Dienst (für das gerade angemeldete Konto). "shared": danach für jeden
|
|
// Prozess abholbar - nur für diesen Prozess angefordert, gab act ihn nicht heraus (E0A14CCE).
|
|
Handle done;
|
|
snprintf(why, sizeof(why), "Werkzeuge: Server prüft noch ...");
|
|
if (R_FAILED(svcCreateEvent(&done, RESET_ONESHOT))) return;
|
|
u32 pid = 0;
|
|
svcGetProcessId(&pid, CUR_PROCESS_HANDLE);
|
|
Result rc = ACT_AcquireIndependentServiceToken(0xFE, CLIENT_ID, 0, false, true, pid, done);
|
|
Result wait = R_SUCCEEDED(rc) ? svcWaitSynchronization(done, 15ULL * 1000 * 1000 * 1000) : rc;
|
|
svcCloseHandle(done);
|
|
if (wait != 0) { snprintf(why, sizeof(why), "Werkzeuge: Anmelde-Nachweis nicht erhalten (%08lX).", (unsigned long)wait); return; }
|
|
// Ergebnis der Anfrage beim Account-Server (z. B. abgelehnt oder keine Verbindung)
|
|
Result result = ACT_GetLastResponseResult();
|
|
static IndependentServiceTokenV1 tok;
|
|
memset(&tok, 0, sizeof(tok));
|
|
rc = ACT_GetIndependentServiceToken(&tok);
|
|
// Notlösung: aus dem Zwischenspeicher von act (gerade eben geholt)
|
|
if (R_FAILED(rc) || !tok.token[0]) rc = ACT_GetCachedIndependentServiceToken(&tok, 0xFE, CLIENT_ID, 3600, false, true);
|
|
if (R_FAILED(rc) || !tok.token[0]) {
|
|
Result shown = R_FAILED(result) ? result : rc;
|
|
Result code = ACT_ResultToErrorCode(shown); // 022-XXXX wie in den Einstellungen
|
|
snprintf(why, sizeof(why), "Werkzeuge: kein Anmelde-Nachweis vom Server\n(%08lX, Fehler %03lu-%04lu).",
|
|
(unsigned long)shown, (unsigned long)(code / 10000), (unsigned long)(code % 10000));
|
|
return;
|
|
}
|
|
|
|
// 2. Vom Server prüfen lassen (nur HTTPS)
|
|
char body[32];
|
|
u32 len = 0;
|
|
Result hrc = 0;
|
|
u32 code = https_request(server, "/mbmnet/admin-check", NULL, "X-Service-Token", tok.token, body, sizeof(body), &len, NULL, 0, &hrc);
|
|
memset(&tok, 0, sizeof(tok));
|
|
isAdmin = code == 200 && strncmp(body, "admin=1", 7) == 0;
|
|
if (isAdmin) why[0] = 0;
|
|
else if (code == 200) snprintf(why, sizeof(why), "Werkzeuge gibt es nur für Admins.");
|
|
else if (code) snprintf(why, sizeof(why), "Werkzeuge: Server antwortet HTTP %lu.", (unsigned long)code);
|
|
else snprintf(why, sizeof(why), "Werkzeuge: keine HTTPS-Verbindung zum Server (%08lX).", (unsigned long)hrc);
|
|
}
|
|
|
|
const char *admin_why(void) { return why; }
|
|
|
|
void admin_start(const char *srv, bool mbmnetLoggedIn)
|
|
{
|
|
isAdmin = false;
|
|
snprintf(why, sizeof(why), "Werkzeuge: nur bei aktivem MBMNET mit angemeldetem Admin-Konto.");
|
|
remove(OLD_CACHE_FILE);
|
|
if (!mbmnetLoggedIn) return;
|
|
snprintf(server, sizeof(server), "%s", srv);
|
|
s32 prio = 0x30;
|
|
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
|
|
thread = threadCreate(worker, NULL, 16 * 1024, prio + 1, -2, false);
|
|
}
|
|
|
|
bool admin_is(void) { return isAdmin; }
|
|
|
|
bool admin_finish(void)
|
|
{
|
|
if (!thread) return true;
|
|
if (R_FAILED(threadJoin(thread, 100ULL * 1000 * 1000))) return false; // läuft noch
|
|
threadFree(thread);
|
|
thread = NULL;
|
|
return true;
|
|
}
|