-
Notifications
You must be signed in to change notification settings - Fork 7.7k
ESP32-C3 Captive Portal with DNS Redirection and Auto Popup Implementation #11558
-
When I connect to the Wi-Fi Soft AP, it prints the received DNS requests as expected as you can see below. However, the webpage does not automatically pop up on my smartphone.
I believe it should work because the code sends a DNS response with the predefined IP address "192.168.4.1" using sendto(sock, response, pos, 0, (struct sockaddr *)&client_addr, addr_len);. The DNS request is caught and answered correctly, but the browser does not open the page automatically.
BTW, the web server and HTTP handlers seem to be functioning properly because when I manually enter "192.168.4.1" in the browser, the HTML page defined in the code is displayed.
I would really appreciate your help with this.
I (23669) dns_redirect: Received DNS request for: connectivitycheckgstaticcom
Response size: 512
Response position: 63
I (23679) dns_redirect: Received DNS request for: wwwgooglecom
Response size: 512
Response position: 48
I (23749) dns_redirect: Received DNS request for: mtalkgooglecom
Response size: 512
Response position: 50
#include <string.h> #include "esp_wifi.h" #include "esp_event.h" #include "esp_log.h" #include "nvs_flash.h" #include "esp_netif.h" #include "esp_http_server.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "lwip/sockets.h" #include "lwip/inet.h" #define EXAMPLE_WIFI_SSID "MyDevice_AP" #define EXAMPLE_WIFI_PASS "12345678" static const char *TAG = "dns_redirect"; #define DNS_PORT 53 #define DNS_IP_ADDR "192.168.4.1" #define DNS_BUF_SIZE 512 static const char *TAG_HTTP = "captive_portal"; typedef struct { uint16_t id; uint16_t flags; uint16_t qdcount; uint16_t ancount; uint16_t nscount; uint16_t arcount; } dns_header_t; static void dns_server_task(void *pvParameters) { int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { ESP_LOGE(TAG, "Failed to create socket"); vTaskDelete(NULL); return; } struct sockaddr_in server_addr = { .sin_family = AF_INET, .sin_port = htons(DNS_PORT), .sin_addr.s_addr = INADDR_ANY, }; if (bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { ESP_LOGE(TAG, "Socket bind failed"); close(sock); vTaskDelete(NULL); return; } ESP_LOGI(TAG, "DNS server started on port %d", DNS_PORT); uint8_t buf[DNS_BUF_SIZE]; struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); while (1) { int len = recvfrom(sock, buf, DNS_BUF_SIZE, 0, (struct sockaddr *)&client_addr, &addr_len); if (len < 0) continue; ESP_LOGI(TAG, "Received DNS request for: %s", &buf[12]); if (len < 12) continue; uint8_t response[DNS_BUF_SIZE]; memcpy(response, buf, len); response[2] = 0x81; // QR=1, Opcode=0, AA=0, TC=0, RD=1 response[3] = 0x80; // RA=1, Z=0, RCODE=0 // Answer count = 1 response[7] = 0x01; int pos = len; response[pos++] = 0xC0; response[pos++] = 0x0C; // Type A, Class IN response[pos++] = 0x00; response[pos++] = 0x01; response[pos++] = 0x00; response[pos++] = 0x01; // TTL response[pos++] = 0x00; response[pos++] = 0x00; response[pos++] = 0x00; response[pos++] = 0x3C; // Data length = 4 response[pos++] = 0x00; response[pos++] = 0x04; // IP Address struct in_addr ip_resp; inet_aton(DNS_IP_ADDR, &ip_resp); memcpy(&response[pos], &ip_resp.s_addr, 4); pos += 4; printf("Response size: %zu\n", sizeof(response)); printf("Response position: %d\n", pos); sendto(sock, response, pos, 0, (struct sockaddr *)&client_addr, addr_len); //sendto(sock, buff_snd, strlen( buff_snd)+1, 0, ( struct sockaddr*)&client_addr, sizeof( client_addr)); } } // Basic HTML Page static const char *html_page = "<!DOCTYPE html><html><head><title>Device Control</title></head>" "<body><h1>Hello, Captive Portal!</h1><p>Control interface coming soon.</p></body></html>"; // Root Handler esp_err_t root_get_handler(httpd_req_t *req) { ESP_LOGI(TAG_HTTP, "Request URI: %s", req->uri); httpd_resp_send(req, html_page, HTTPD_RESP_USE_STRLEN); return ESP_OK; } // Redirection Handler esp_err_t redirect_handler(httpd_req_t *req) { ESP_LOGI(TAG_HTTP, "Redirecting request"); httpd_resp_set_status(req, "302 Found"); httpd_resp_set_hdr(req, "Location", "http://192.168.4.1/"); httpd_resp_send(req, NULL, 0); return ESP_OK; } esp_err_t android_handler(httpd_req_t *req) { httpd_resp_set_status(req, "204 No Content"); httpd_resp_send(req, NULL, 0); return ESP_OK; } // URI Definitions httpd_uri_t root = { .uri = "/", .method = HTTP_GET, .handler = root_get_handler, }; httpd_uri_t redirect_android = { .uri = "/generate_204", .method = HTTP_GET, .handler = android_handler, }; httpd_uri_t redirect_apple = { .uri = "/hotspot-detect.html", .method = HTTP_GET, .handler = redirect_handler, }; httpd_uri_t redirect_msft = { .uri = "/ncsi.txt", .method = HTTP_GET, .handler = redirect_handler, }; httpd_uri_t uri_android_2 = { .uri = "/redirect", .method = HTTP_GET, .handler = android_handler, }; httpd_uri_t uri_android_3 = { .uri = "/mobile/status.php", .method = HTTP_GET, .handler = android_handler, }; httpd_uri_t catch_all = { .uri = "/*", .method = HTTP_GET, .handler = redirect_handler }; httpd_handle_t start_webserver(void) { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.max_uri_handlers = 20; httpd_handle_t server = NULL; config.uri_match_fn = httpd_uri_match_wildcard; if (httpd_start(&server, &config) == ESP_OK) { httpd_register_uri_handler(server, &root); httpd_register_uri_handler(server, &redirect_android); httpd_register_uri_handler(server, &redirect_apple); httpd_register_uri_handler(server, &redirect_msft); httpd_register_uri_handler(server, &uri_android_2); httpd_register_uri_handler(server, &uri_android_3); // httpd_register_uri_handler(server, &catch_all); } return server; } void wifi_init_softap(void) { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); wifi_config_t wifi_config = { .ap = { .ssid = EXAMPLE_WIFI_SSID, .ssid_len = strlen(EXAMPLE_WIFI_SSID), .password = EXAMPLE_WIFI_PASS, .channel = 1, .authmode = WIFI_AUTH_WPA_WPA2_PSK, .max_connection = 4, .beacon_interval = 100, }, }; if (strlen(EXAMPLE_WIFI_PASS) == 0) { wifi_config.ap.authmode = WIFI_AUTH_OPEN; } ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG_HTTP, "SoftAP started with SSID: %s", EXAMPLE_WIFI_SSID); } void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_ap(); wifi_init_softap(); start_webserver(); ESP_LOGI(TAG_HTTP, "Captive portal ready."); xTaskCreate(dns_server_task, "dns_server", 4096, NULL, 5, NULL); }
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1