// 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 #include #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) 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, false, pid, done); if (R_SUCCEEDED(rc)) rc = svcWaitSynchronization(done, 15ULL * 1000 * 1000 * 1000); svcCloseHandle(done); if (rc != 0) { snprintf(why, sizeof(why), "Werkzeuge: Anmelde-Nachweis nicht erhalten (%08lX).", (unsigned long)rc); return; } static IndependentServiceTokenV1 tok; memset(&tok, 0, sizeof(tok)); if (R_FAILED(rc = ACT_GetIndependentServiceToken(&tok)) || !tok.token[0]) { snprintf(why, sizeof(why), "Werkzeuge: Anmelde-Nachweis leer (%08lX).", (unsigned long)rc); 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; }