diff options
| author | Your Name <you@example.com> | 2026-05-16 04:46:32 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-16 04:46:32 +0530 |
| commit | 50b5975ac8793d6d820c35b5999f8a909f64e71b (patch) | |
| tree | 2592f9e7a671af2aca56e46887e50b8ad8e418b6 /main/captive_portal.c | |
| parent | 3f46bb83cb1041889034c88adce1895dd330793f (diff) | |
Captive portal detection fix + Phase 2 tests 16-18,20 passing (17/17)
- Add DoT reject server on port 853 (TCP RST forces DNS-over-TLS fallback)
- DNS hijack returns NXDOMAIN for all non-A query types (no forwarding for unauthed)
- Shorter TTL on hijack responses (10s) for faster captive detection
- Explicit 302 redirect handlers for /generate_204, /hotspot-detect.html, etc.
- HTTP and DNS request logging for debugging captive detection
- Per-MAC tracking in firewall (find_by_mac, get_mac_for_ip with ARP fallback)
- Session MAC tracking (session_find_by_mac)
- Phase 2 test 18: add route through TollGate before ping test
- All 17 Phase 2 tests pass (15-21 + whoami + portal form)
Diffstat (limited to 'main/captive_portal.c')
| -rw-r--r-- | main/captive_portal.c | 50 |
1 files changed, 42 insertions, 8 deletions
diff --git a/main/captive_portal.c b/main/captive_portal.c index 17f672f..0ae46ab 100644 --- a/main/captive_portal.c +++ b/main/captive_portal.c | |||
| @@ -100,11 +100,17 @@ static bool is_captive_detection_uri(const char *uri) | |||
| 100 | strcmp(uri, "/ncsi.txt") == 0 || | 100 | strcmp(uri, "/ncsi.txt") == 0 || |
| 101 | strcmp(uri, "/connecttest.txt") == 0 || | 101 | strcmp(uri, "/connecttest.txt") == 0 || |
| 102 | strcmp(uri, "/wpad.dat") == 0 || | 102 | strcmp(uri, "/wpad.dat") == 0 || |
| 103 | strcmp(uri, "/redirect") == 0; | 103 | strcmp(uri, "/redirect") == 0 || |
| 104 | strcmp(uri, "/kindle-wifi/wifistub.html") == 0 || | ||
| 105 | strcmp(uri, "/fwlink") == 0 || | ||
| 106 | strcmp(uri, "/connectivity-check.html") == 0 || | ||
| 107 | strcmp(uri, "/generate_204/") == 0 || | ||
| 108 | strcmp(uri, "/hotspot-detect.html/") == 0; | ||
| 104 | } | 109 | } |
| 105 | 110 | ||
| 106 | static esp_err_t portal_handler(httpd_req_t *req) | 111 | static esp_err_t portal_handler(httpd_req_t *req) |
| 107 | { | 112 | { |
| 113 | ESP_LOGI(TAG, "GET %s from client", req->uri); | ||
| 108 | httpd_resp_set_type(req, "text/html"); | 114 | httpd_resp_set_type(req, "text/html"); |
| 109 | httpd_resp_send(req, PORTAL_HTML, strlen(PORTAL_HTML)); | 115 | httpd_resp_send(req, PORTAL_HTML, strlen(PORTAL_HTML)); |
| 110 | return ESP_OK; | 116 | return ESP_OK; |
| @@ -139,12 +145,17 @@ static esp_err_t status_handler(httpd_req_t *req) | |||
| 139 | static esp_err_t whoami_handler(httpd_req_t *req) | 145 | static esp_err_t whoami_handler(httpd_req_t *req) |
| 140 | { | 146 | { |
| 141 | uint32_t client_ip; | 147 | uint32_t client_ip; |
| 142 | char resp[64]; | 148 | char resp[96]; |
| 143 | if (get_client_ip(req, &client_ip) == ESP_OK) { | 149 | if (get_client_ip(req, &client_ip) == ESP_OK) { |
| 150 | char mac[18] = {0}; | ||
| 144 | esp_ip4_addr_t ip = { .addr = client_ip }; | 151 | esp_ip4_addr_t ip = { .addr = client_ip }; |
| 145 | snprintf(resp, sizeof(resp), "mac=" IPSTR, IP2STR(&ip)); | 152 | if (firewall_get_mac_for_ip(client_ip, mac, sizeof(mac)) == ESP_OK) { |
| 153 | snprintf(resp, sizeof(resp), "ip=" IPSTR " mac=%s", IP2STR(&ip), mac); | ||
| 154 | } else { | ||
| 155 | snprintf(resp, sizeof(resp), "ip=" IPSTR " mac=unknown", IP2STR(&ip)); | ||
| 156 | } | ||
| 146 | } else { | 157 | } else { |
| 147 | snprintf(resp, sizeof(resp), "mac=unknown"); | 158 | snprintf(resp, sizeof(resp), "ip=unknown mac=unknown"); |
| 148 | } | 159 | } |
| 149 | httpd_resp_set_type(req, "text/plain"); | 160 | httpd_resp_set_type(req, "text/plain"); |
| 150 | httpd_resp_send(req, resp, strlen(resp)); | 161 | httpd_resp_send(req, resp, strlen(resp)); |
| @@ -174,13 +185,22 @@ static esp_err_t reset_auth_handler(httpd_req_t *req) | |||
| 174 | return ESP_OK; | 185 | return ESP_OK; |
| 175 | } | 186 | } |
| 176 | 187 | ||
| 188 | static esp_err_t redirect_to_portal_handler(httpd_req_t *req) | ||
| 189 | { | ||
| 190 | ESP_LOGI(TAG, "Captive detect: GET %s → 302 → http://192.168.4.1/", req->uri); | ||
| 191 | httpd_resp_set_status(req, "302 Found"); | ||
| 192 | httpd_resp_set_hdr(req, "Location", "http://192.168.4.1/"); | ||
| 193 | httpd_resp_set_hdr(req, "Connection", "close"); | ||
| 194 | httpd_resp_send(req, NULL, 0); | ||
| 195 | return ESP_OK; | ||
| 196 | } | ||
| 197 | |||
| 177 | static esp_err_t catchall_handler(httpd_req_t *req) | 198 | static esp_err_t catchall_handler(httpd_req_t *req) |
| 178 | { | 199 | { |
| 179 | if (is_captive_detection_uri(req->uri)) { | 200 | ESP_LOGI(TAG, "Catchall: GET %s → 302 → http://192.168.4.1/", req->uri); |
| 180 | return portal_handler(req); | ||
| 181 | } | ||
| 182 | httpd_resp_set_status(req, "302 Found"); | 201 | httpd_resp_set_status(req, "302 Found"); |
| 183 | httpd_resp_set_hdr(req, "Location", "http://192.168.4.1/"); | 202 | httpd_resp_set_hdr(req, "Location", "http://192.168.4.1/"); |
| 203 | httpd_resp_set_hdr(req, "Connection", "close"); | ||
| 184 | httpd_resp_send(req, NULL, 0); | 204 | httpd_resp_send(req, NULL, 0); |
| 185 | return ESP_OK; | 205 | return ESP_OK; |
| 186 | } | 206 | } |
| @@ -191,6 +211,13 @@ static const httpd_uri_t uri_status = { .uri = "/api/status", .method = HTTP_GET | |||
| 191 | static const httpd_uri_t uri_whoami = { .uri = "/whoami", .method = HTTP_GET, .handler = whoami_handler }; | 211 | static const httpd_uri_t uri_whoami = { .uri = "/whoami", .method = HTTP_GET, .handler = whoami_handler }; |
| 192 | static const httpd_uri_t uri_usage = { .uri = "/usage", .method = HTTP_GET, .handler = usage_handler }; | 212 | static const httpd_uri_t uri_usage = { .uri = "/usage", .method = HTTP_GET, .handler = usage_handler }; |
| 193 | static const httpd_uri_t uri_reset = { .uri = "/reset_authentication", .method = HTTP_GET, .handler = reset_auth_handler }; | 213 | static const httpd_uri_t uri_reset = { .uri = "/reset_authentication", .method = HTTP_GET, .handler = reset_auth_handler }; |
| 214 | static const httpd_uri_t uri_gen204 = { .uri = "/generate_204", .method = HTTP_GET, .handler = redirect_to_portal_handler }; | ||
| 215 | static const httpd_uri_t uri_hotspot = { .uri = "/hotspot-detect.html", .method = HTTP_GET, .handler = redirect_to_portal_handler }; | ||
| 216 | static const httpd_uri_t uri_canonical = { .uri = "/canonical.html", .method = HTTP_GET, .handler = redirect_to_portal_handler }; | ||
| 217 | static const httpd_uri_t uri_success = { .uri = "/success.txt", .method = HTTP_GET, .handler = redirect_to_portal_handler }; | ||
| 218 | static const httpd_uri_t uri_ncsi = { .uri = "/ncsi.txt", .method = HTTP_GET, .handler = redirect_to_portal_handler }; | ||
| 219 | static const httpd_uri_t uri_connecttest = { .uri = "/connecttest.txt", .method = HTTP_GET, .handler = redirect_to_portal_handler }; | ||
| 220 | static const httpd_uri_t uri_wpad = { .uri = "/wpad.dat", .method = HTTP_GET, .handler = redirect_to_portal_handler }; | ||
| 194 | static const httpd_uri_t uri_catchall = { .uri = "/*", .method = HTTP_GET, .handler = catchall_handler }; | 221 | static const httpd_uri_t uri_catchall = { .uri = "/*", .method = HTTP_GET, .handler = catchall_handler }; |
| 195 | 222 | ||
| 196 | esp_err_t captive_portal_start(void) | 223 | esp_err_t captive_portal_start(void) |
| @@ -198,7 +225,7 @@ esp_err_t captive_portal_start(void) | |||
| 198 | if (s_server) return ESP_OK; | 225 | if (s_server) return ESP_OK; |
| 199 | 226 | ||
| 200 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); | 227 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); |
| 201 | config.max_uri_handlers = 10; | 228 | config.max_uri_handlers = 20; |
| 202 | config.uri_match_fn = httpd_uri_match_wildcard; | 229 | config.uri_match_fn = httpd_uri_match_wildcard; |
| 203 | 230 | ||
| 204 | esp_err_t ret = httpd_start(&s_server, &config); | 231 | esp_err_t ret = httpd_start(&s_server, &config); |
| @@ -213,6 +240,13 @@ esp_err_t captive_portal_start(void) | |||
| 213 | httpd_register_uri_handler(s_server, &uri_whoami); | 240 | httpd_register_uri_handler(s_server, &uri_whoami); |
| 214 | httpd_register_uri_handler(s_server, &uri_usage); | 241 | httpd_register_uri_handler(s_server, &uri_usage); |
| 215 | httpd_register_uri_handler(s_server, &uri_reset); | 242 | httpd_register_uri_handler(s_server, &uri_reset); |
| 243 | httpd_register_uri_handler(s_server, &uri_gen204); | ||
| 244 | httpd_register_uri_handler(s_server, &uri_hotspot); | ||
| 245 | httpd_register_uri_handler(s_server, &uri_canonical); | ||
| 246 | httpd_register_uri_handler(s_server, &uri_success); | ||
| 247 | httpd_register_uri_handler(s_server, &uri_ncsi); | ||
| 248 | httpd_register_uri_handler(s_server, &uri_connecttest); | ||
| 249 | httpd_register_uri_handler(s_server, &uri_wpad); | ||
| 216 | httpd_register_uri_handler(s_server, &uri_catchall); | 250 | httpd_register_uri_handler(s_server, &uri_catchall); |
| 217 | 251 | ||
| 218 | ESP_LOGI(TAG, "Captive portal started on port 80"); | 252 | ESP_LOGI(TAG, "Captive portal started on port 80"); |