espilon-source/espilon_bot/components/mod_honeypot/hp_tcp_services.c
Eun0us 6d45770d98 epsilon: merge command system into core + add 5 new modules
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.
2026-02-28 20:07:59 +01:00

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 */