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.
205 lines
5.8 KiB
C
205 lines
5.8 KiB
C
/*
|
|
* hp_tcp_services.c
|
|
* Generic TCP listener + public API for honeypot services.
|
|
* Service handlers live in services/svc_*.c
|
|
*/
|
|
#include "sdkconfig.h"
|
|
|
|
#ifdef CONFIG_MODULE_HONEYPOT
|
|
|
|
#include <errno.h>
|
|
#include "esp_log.h"
|
|
#include "services/svc_common.h"
|
|
#include "hp_tcp_services.h"
|
|
|
|
#define TAG "HP_SVC"
|
|
|
|
#define SVC_STACK_SIZE 4096
|
|
#define SVC_PRIORITY 4
|
|
#define SVC_CORE 1
|
|
#define ACCEPT_TIMEOUT_S 2
|
|
#define CLIENT_TIMEOUT_S 5
|
|
|
|
/* ============================================================
|
|
* Service descriptors
|
|
* ============================================================ */
|
|
static hp_svc_desc_t services[HP_SVC_COUNT] = {
|
|
[HP_SVC_SSH] = { .name = "ssh", .port = 22 },
|
|
[HP_SVC_TELNET] = { .name = "telnet", .port = 23 },
|
|
[HP_SVC_HTTP] = { .name = "http", .port = 80 },
|
|
[HP_SVC_FTP] = { .name = "ftp", .port = 21 },
|
|
};
|
|
|
|
static const hp_client_handler_t handlers[HP_SVC_COUNT] = {
|
|
[HP_SVC_SSH] = handle_ssh_client,
|
|
[HP_SVC_TELNET] = handle_telnet_client,
|
|
[HP_SVC_HTTP] = handle_http_client,
|
|
[HP_SVC_FTP] = handle_ftp_client,
|
|
};
|
|
|
|
/* ============================================================
|
|
* Name <-> ID mapping
|
|
* ============================================================ */
|
|
int hp_svc_name_to_id(const char *name)
|
|
{
|
|
for (int i = 0; i < HP_SVC_COUNT; i++) {
|
|
if (strcmp(services[i].name, name) == 0)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const char *hp_svc_id_to_name(hp_svc_id_t svc)
|
|
{
|
|
if (svc >= HP_SVC_COUNT) return "unknown";
|
|
return services[svc].name;
|
|
}
|
|
|
|
/* ============================================================
|
|
* Client IP helper
|
|
* ============================================================ */
|
|
static void sockaddr_to_str(const struct sockaddr_in *addr,
|
|
char *ip_buf, size_t ip_len,
|
|
uint16_t *port_out)
|
|
{
|
|
inet_ntoa_r(addr->sin_addr, ip_buf, ip_len);
|
|
*port_out = ntohs(addr->sin_port);
|
|
}
|
|
|
|
/* ============================================================
|
|
* Generic listener task
|
|
* ============================================================ */
|
|
static void listener_task(void *arg)
|
|
{
|
|
hp_svc_desc_t *svc = (hp_svc_desc_t *)arg;
|
|
int listen_fd = -1;
|
|
|
|
struct sockaddr_in addr = {
|
|
.sin_family = AF_INET,
|
|
.sin_port = htons(svc->port),
|
|
.sin_addr.s_addr = htonl(INADDR_ANY),
|
|
};
|
|
|
|
listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (listen_fd < 0) {
|
|
ESP_LOGE(TAG, "%s: socket() failed: %d", svc->name, errno);
|
|
goto done;
|
|
}
|
|
|
|
int opt = 1;
|
|
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
|
|
|
if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
ESP_LOGE(TAG, "%s: bind(%d) failed: %d", svc->name, svc->port, errno);
|
|
goto done;
|
|
}
|
|
|
|
if (listen(listen_fd, 2) < 0) {
|
|
ESP_LOGE(TAG, "%s: listen() failed: %d", svc->name, errno);
|
|
goto done;
|
|
}
|
|
|
|
struct timeval tv = { .tv_sec = ACCEPT_TIMEOUT_S, .tv_usec = 0 };
|
|
setsockopt(listen_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
|
|
ESP_LOGI(TAG, "%s listening on port %d", svc->name, svc->port);
|
|
svc->running = true;
|
|
|
|
while (!svc->stop_req) {
|
|
struct sockaddr_in client_addr;
|
|
socklen_t clen = sizeof(client_addr);
|
|
int client_fd = accept(listen_fd,
|
|
(struct sockaddr *)&client_addr, &clen);
|
|
|
|
if (client_fd < 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
|
continue;
|
|
ESP_LOGW(TAG, "%s: accept error: %d", svc->name, errno);
|
|
continue;
|
|
}
|
|
|
|
struct timeval ctv = { .tv_sec = CLIENT_TIMEOUT_S, .tv_usec = 0 };
|
|
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &ctv, sizeof(ctv));
|
|
|
|
char client_ip[16];
|
|
uint16_t client_port;
|
|
sockaddr_to_str(&client_addr, client_ip, sizeof(client_ip),
|
|
&client_port);
|
|
|
|
hp_svc_id_t id = (hp_svc_id_t)(svc - services);
|
|
if (id < HP_SVC_COUNT && handlers[id]) {
|
|
handlers[id](client_fd, client_ip, client_port, svc);
|
|
}
|
|
|
|
close(client_fd);
|
|
}
|
|
|
|
done:
|
|
if (listen_fd >= 0)
|
|
close(listen_fd);
|
|
svc->running = false;
|
|
svc->stop_req = false;
|
|
ESP_LOGI(TAG, "%s stopped", svc->name);
|
|
svc->task = NULL;
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
/* ============================================================
|
|
* Public API
|
|
* ============================================================ */
|
|
void hp_svc_start(hp_svc_id_t svc)
|
|
{
|
|
if (svc >= HP_SVC_COUNT) return;
|
|
hp_svc_desc_t *d = &services[svc];
|
|
if (d->running || d->task) {
|
|
ESP_LOGW(TAG, "%s already running", d->name);
|
|
return;
|
|
}
|
|
|
|
d->stop_req = false;
|
|
d->connections = 0;
|
|
d->auth_attempts = 0;
|
|
|
|
char name[16];
|
|
snprintf(name, sizeof(name), "hp_%s", d->name);
|
|
BaseType_t ret = xTaskCreatePinnedToCore(listener_task, name, SVC_STACK_SIZE,
|
|
d, SVC_PRIORITY, &d->task, SVC_CORE);
|
|
if (ret != pdPASS) {
|
|
ESP_LOGE(TAG, "Failed to create %s task", d->name);
|
|
d->task = NULL;
|
|
}
|
|
}
|
|
|
|
void hp_svc_stop(hp_svc_id_t svc)
|
|
{
|
|
if (svc >= HP_SVC_COUNT) return;
|
|
hp_svc_desc_t *d = &services[svc];
|
|
if (!d->running && !d->task) {
|
|
ESP_LOGW(TAG, "%s not running", d->name);
|
|
return;
|
|
}
|
|
d->stop_req = true;
|
|
ESP_LOGI(TAG, "%s stop requested", d->name);
|
|
}
|
|
|
|
bool hp_svc_running(hp_svc_id_t svc)
|
|
{
|
|
if (svc >= HP_SVC_COUNT) return false;
|
|
return services[svc].running;
|
|
}
|
|
|
|
int hp_svc_status(hp_svc_id_t svc, char *buf, size_t len)
|
|
{
|
|
if (svc >= HP_SVC_COUNT) return 0;
|
|
hp_svc_desc_t *d = &services[svc];
|
|
return snprintf(buf, len,
|
|
"service=%s running=%s port=%d connections=%lu auth_attempts=%lu",
|
|
d->name,
|
|
d->running ? "yes" : "no",
|
|
d->port,
|
|
(unsigned long)d->connections,
|
|
(unsigned long)d->auth_attempts);
|
|
}
|
|
|
|
#endif /* CONFIG_MODULE_HONEYPOT */
|