Move command registry from components/command/ into components/core/. New modules: mod_canbus, mod_honeypot, mod_fallback, mod_redteam, mod_ota. Replace mod_proxy with tun_core (multiplexed SOCKS5 tunnel). Kconfig extended with per-module settings and async worker config.
228 lines
6.5 KiB
C
228 lines
6.5 KiB
C
#include "utils.h"
|
||
#include "esp_log.h"
|
||
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
|
||
static const char *TAG = "COMMAND";
|
||
|
||
static const command_t *registry[MAX_COMMANDS];
|
||
static size_t registry_count = 0;
|
||
|
||
/* Max longueur lue/copied par arg (sécurité si non \0) */
|
||
#ifndef COMMAND_MAX_ARG_LEN
|
||
#define COMMAND_MAX_ARG_LEN 128
|
||
#endif
|
||
|
||
/* Max args temporaires qu’on accepte ici (doit couvrir tes commandes) */
|
||
#ifndef COMMAND_MAX_ARGS
|
||
#define COMMAND_MAX_ARGS 16
|
||
#endif
|
||
|
||
/* =========================================================
|
||
* Register command
|
||
* ========================================================= */
|
||
void command_register(const command_t *cmd)
|
||
{
|
||
if (!cmd || !cmd->name || !cmd->handler) {
|
||
ESP_LOGE(TAG, "Invalid command registration");
|
||
return;
|
||
}
|
||
|
||
if (registry_count >= MAX_COMMANDS) {
|
||
ESP_LOGE(TAG, "Command registry full");
|
||
return;
|
||
}
|
||
|
||
registry[registry_count++] = cmd;
|
||
#ifdef CONFIG_ESPILON_LOG_CMD_REG_VERBOSE
|
||
ESPILON_LOGI_PURPLE(TAG, "Registered command: %s", cmd->name);
|
||
#endif
|
||
}
|
||
|
||
/* =========================================================
|
||
* Summary
|
||
* ========================================================= */
|
||
void command_log_registry_summary(void)
|
||
{
|
||
if (registry_count == 0) {
|
||
ESPILON_LOGI_PURPLE(TAG, "Registered commands: none");
|
||
return;
|
||
}
|
||
|
||
char buf[512];
|
||
int off = snprintf(
|
||
buf,
|
||
sizeof(buf),
|
||
"Registered commands (%d): ",
|
||
(int)registry_count
|
||
);
|
||
|
||
for (size_t i = 0; i < registry_count; i++) {
|
||
const char *name = registry[i] && registry[i]->name
|
||
? registry[i]->name : "?";
|
||
const char *sub = (registry[i] && registry[i]->sub && registry[i]->sub[0])
|
||
? registry[i]->sub : NULL;
|
||
const char *sep = (i == 0) ? "" : ", ";
|
||
int n;
|
||
if (sub) {
|
||
n = snprintf(buf + off, sizeof(buf) - (size_t)off,
|
||
"%s%s %s", sep, name, sub);
|
||
} else {
|
||
n = snprintf(buf + off, sizeof(buf) - (size_t)off,
|
||
"%s%s", sep, name);
|
||
}
|
||
if (n < 0 || n >= (int)(sizeof(buf) - (size_t)off)) {
|
||
if (off < (int)sizeof(buf) - 4) {
|
||
memcpy(buf + (sizeof(buf) - 4), "...", 4);
|
||
}
|
||
break;
|
||
}
|
||
off += n;
|
||
}
|
||
|
||
ESPILON_LOGI_PURPLE(TAG, "%s", buf);
|
||
}
|
||
|
||
/* =========================================================
|
||
* Helpers: deep-copy argv into one arena + argv[] pointers
|
||
* ========================================================= */
|
||
static bool deepcopy_argv(char *const *argv_in,
|
||
int argc,
|
||
char ***argv_out,
|
||
char **arena_out,
|
||
const char *req_id)
|
||
{
|
||
*argv_out = NULL;
|
||
*arena_out = NULL;
|
||
|
||
if (argc < 0) {
|
||
msg_error("cmd", "Invalid argc", req_id);
|
||
return false;
|
||
}
|
||
|
||
if (argc == 0) {
|
||
char **argv0 = (char **)calloc(1, sizeof(char *));
|
||
if (!argv0) {
|
||
msg_error("cmd", "OOM copying argv", req_id);
|
||
return false;
|
||
}
|
||
*argv_out = argv0;
|
||
*arena_out = NULL;
|
||
return true;
|
||
}
|
||
|
||
size_t total = 0;
|
||
for (int i = 0; i < argc; i++) {
|
||
const char *s = (argv_in && argv_in[i]) ? argv_in[i] : "";
|
||
size_t n = strnlen(s, COMMAND_MAX_ARG_LEN);
|
||
total += (n + 1);
|
||
}
|
||
|
||
char *arena = (char *)malloc(total ? total : 1);
|
||
char **argv_copy = (char **)malloc((size_t)argc * sizeof(char *));
|
||
if (!arena || !argv_copy) {
|
||
free(arena);
|
||
free(argv_copy);
|
||
msg_error("cmd", "OOM copying argv", req_id);
|
||
return false;
|
||
}
|
||
|
||
size_t off = 0;
|
||
for (int i = 0; i < argc; i++) {
|
||
const char *s = (argv_in && argv_in[i]) ? argv_in[i] : "";
|
||
size_t n = strnlen(s, COMMAND_MAX_ARG_LEN);
|
||
|
||
argv_copy[i] = &arena[off];
|
||
memcpy(&arena[off], s, n);
|
||
arena[off + n] = '\0';
|
||
off += (n + 1);
|
||
}
|
||
|
||
*argv_out = argv_copy;
|
||
*arena_out = arena;
|
||
return true;
|
||
}
|
||
|
||
/* =========================================================
|
||
* Dispatch nanopb command
|
||
* ========================================================= */
|
||
void command_process_pb(const c2_Command *cmd)
|
||
{
|
||
if (!cmd) return;
|
||
|
||
/* nanopb: tableaux fixes => jamais NULL */
|
||
const char *name = cmd->command_name;
|
||
const char *reqid = cmd->request_id;
|
||
const char *reqid_or_null = (reqid[0] ? reqid : NULL);
|
||
|
||
int argc = cmd->argv_count;
|
||
|
||
for (size_t i = 0; i < registry_count; i++) {
|
||
const command_t *c = registry[i];
|
||
|
||
if (strcmp(c->name, name) != 0)
|
||
continue;
|
||
|
||
/*
|
||
* Sub-command matching: if the command has a .sub field,
|
||
* argv[0] must match it. The sub is consumed (argv shifted
|
||
* by 1) before passing to the handler.
|
||
*/
|
||
int sub_offset = 0;
|
||
if (c->sub && c->sub[0]) {
|
||
if (argc < 1 || strcmp(cmd->argv[0], c->sub) != 0)
|
||
continue; /* not this sub-command, try next */
|
||
sub_offset = 1;
|
||
}
|
||
|
||
int effective_argc = argc - sub_offset;
|
||
|
||
if (effective_argc < c->min_args || effective_argc > c->max_args) {
|
||
msg_error("cmd", "Invalid argument count", reqid_or_null);
|
||
return;
|
||
}
|
||
|
||
if (c->sub && c->sub[0]) {
|
||
ESP_LOGI(TAG, "Execute: %s %s (argc=%d)", name, c->sub, effective_argc);
|
||
} else {
|
||
ESP_LOGI(TAG, "Execute: %s (argc=%d)", name, effective_argc);
|
||
}
|
||
|
||
if (c->async) {
|
||
command_async_enqueue(c, cmd, sub_offset);
|
||
return;
|
||
}
|
||
|
||
/* ================================
|
||
* SYNC PATH:
|
||
* Build argv_ptrs[] from cmd->argv, skipping sub_offset
|
||
* ================================ */
|
||
if (effective_argc > COMMAND_MAX_ARGS) {
|
||
msg_error("cmd", "Too many args", reqid_or_null);
|
||
return;
|
||
}
|
||
|
||
char *argv_ptrs[COMMAND_MAX_ARGS] = {0};
|
||
for (int a = 0; a < effective_argc; a++) {
|
||
argv_ptrs[a] = (char *)cmd->argv[a + sub_offset];
|
||
}
|
||
|
||
/* Deep-copy pour rendre sync aussi safe que async */
|
||
char **argv_copy = NULL;
|
||
char *arena = NULL;
|
||
|
||
if (!deepcopy_argv(argv_ptrs, effective_argc, &argv_copy, &arena, reqid_or_null))
|
||
return;
|
||
|
||
c->handler(effective_argc, argv_copy, reqid_or_null, c->ctx);
|
||
|
||
free(argv_copy);
|
||
free(arena);
|
||
return;
|
||
}
|
||
|
||
msg_error("cmd", "Unknown command", reqid_or_null);
|
||
}
|