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