// Entwickler-Werkzeuge nur für Admins - und nur, wenn der Server es gerade bestätigt hat: // 1. Die App liest aus dem Konto-Dienst (act) die Tokens des angemeldeten MBMNET-Kontos. Die hat nur ein 3DS, // auf dem man sich wirklich mit diesem Konto angemeldet hat (ausgestellt vom MBMNET-Account-Server). // 2. Sie schickt sie per HTTPS (MBMNET-CA) an /mbmnet/admin-check. Der Server prüft sie gegen seine // Datenbank 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) static volatile bool isAdmin; static char server[64]; static char why[96] = "Werkzeuge: kein MBMNET-Konto angemeldet."; // warum (noch) keine Werkzeuge static Result lastAct; // letzter Fehler von act (für die Wiederholung) static bool wanted; // MBMNET-Konto angemeldet: prüfen // Liest die Tokens des angemeldeten Kontos aus dem Konto-Dienst (lokal, ohne Server-Anfrage von act) und lässt // sie vom MBMNET-Server prüfen. Die Tokens hat dieser Server beim Anmelden ausgestellt, er kann sie selbst prüfen. // (Früher: Service-Token über act angefordert - act lehnte das mit "ungültiger Zustand" ab, C8A14F4B.) static void check_once(void) { lastAct = 0; u8 slot = 0; ACT_GetCommonInfo(&slot, sizeof(slot), INFO_TYPE_COMMON_CURRENT_ACCOUNT_SLOT); static AccountAccessToken at; memset(&at, 0, sizeof(at)); Result rc = ACT_GetAccountInfo(&at, sizeof(at), slot, INFO_TYPE_ACCOUNT_ACCESS_TOKEN); if (R_FAILED(rc) || (!at.accessToken[0] && !at.refreshToken[0])) { lastAct = rc; snprintf(why, sizeof(why), "Werkzeuge: keine Anmeldung im Konto-Dienst (%08lX).\nEinmal online gehen und neu öffnen.", (unsigned long)rc); return; } static char tokens[0x50]; snprintf(tokens, sizeof(tokens), "%s %s", at.accessToken[0] ? at.accessToken : "-", at.refreshToken); memset(&at, 0, sizeof(at)); // Vom Server prüfen lassen (nur HTTPS, MBMNET-CA) char body[32]; u32 len = 0; Result hrc = 0; u32 code = https_request(server, "/mbmnet/admin-check", NULL, "X-NN-Tokens", tokens, body, sizeof(body), &len, NULL, 0, &hrc); memset(tokens, 0, sizeof(tokens)); 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_prepare(const char *srv, bool mbmnetLoggedIn) { isAdmin = false; wanted = mbmnetLoggedIn; snprintf(why, sizeof(why), mbmnetLoggedIn ? "Werkzeuge: Server prüft noch ..." : "Werkzeuge: nur bei aktivem MBMNET mit angemeldetem Admin-Konto."); remove(OLD_CACHE_FILE); snprintf(server, sizeof(server), "%s", srv); } void admin_check(void) { if (!wanted) return; // act noch mit etwas anderem beschäftigt ("ungültiger Zustand"): kurz warten und noch einmal for (int attempt = 0; attempt < 4; attempt++) { check_once(); if (isAdmin || !lastAct || ((lastAct >> 21) & 0x3F) != 5) break; // 5 = InvalidState svcSleepThread(1000ULL * 1000 * 1000); } } bool admin_is(void) { return isAdmin; } bool admin_finish(void) { return true; } // kein eigener Thread mehr (läuft im Auffrischen)