diff --git a/main/http_server/http_server.c b/main/http_server/http_server.c index bc92a1a..b83a630 100644 --- a/main/http_server/http_server.c +++ b/main/http_server/http_server.c @@ -34,6 +34,7 @@ #include static const char * TAG = "http_server"; +static const char * CORS_TAG = "CORS"; static GlobalState * GLOBAL_STATE; static httpd_handle_t server = NULL; @@ -61,60 +62,85 @@ typedef struct rest_server_context #define CHECK_FILE_EXTENSION(filename, ext) (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0) -static esp_err_t ip_in_private_range(uint32_t ip){ - //Private IP ranges (little endian, 192.168.0.0 => 0.0.168.192) - //192.168.0.0 - uint32_t sixteen_bit_block = 0b00000000000000001010100011000000; - uint32_t sixteen_bit_mask = 0b00000000000000001111111111111111; +static esp_err_t ip_in_private_range(uint32_t address) { + uint32_t ip_address = ntohl(address); - if((ip & sixteen_bit_mask) == sixteen_bit_block){ - return ESP_OK; + // 10.0.0.0 - 10.255.255.255 (Class A) + if ((ip_address >= 0x0A000000) && (ip_address <= 0x0AFFFFFF)) { + return ESP_OK; } - //172.16.0.0 - uint32_t twenty_bit_block = 0b00000000000000000001000010101100; - uint32_t twenty_bit_mask = 0b00000000000000001111000011111111; - if((ip & twenty_bit_mask) == twenty_bit_block){ - return ESP_OK; + + // 172.16.0.0 - 172.31.255.255 (Class B) + if ((ip_address >= 0xAC100000) && (ip_address <= 0xAC1FFFFF)) { + return ESP_OK; } - //10.0.0.0 - uint32_t twenty_four_bit_block = 0b00000000000000000000000000001010; - uint32_t twenty_four_bit_mask = 0b00000000000000000000000011111111; - if((ip & twenty_four_bit_mask) == twenty_four_bit_block){ - return ESP_OK; + + // 192.168.0.0 - 192.168.255.255 (Class C) + if ((ip_address >= 0xC0A80000) && (ip_address <= 0xC0A8FFFF)) { + return ESP_OK; } return ESP_FAIL; } -static esp_err_t check_is_same_network(httpd_req_t * req){ - wifi_mode_t mode; - esp_err_t err = esp_wifi_get_mode(&mode); +static uint32_t extract_origin_ip_addr(httpd_req_t *req) +{ + char origin[128]; + char ip_str[16]; + uint32_t origin_ip_addr = 0; - if (err == ESP_OK) { - switch (mode) { - case WIFI_MODE_STA: - case WIFI_MODE_APSTA: - ESP_LOGI(TAG, "WiFi is in AP+/STA mode."); - return ESP_OK; - default: - break; + // Attempt to get the Origin header. + if (httpd_req_get_hdr_value_str(req, "Origin", origin, sizeof(origin)) != ESP_OK) { + ESP_LOGD(CORS_TAG, "No origin header found."); + return 0; + } + ESP_LOGD(CORS_TAG, "Origin header: %s", origin); + + // Find the start of the IP address in the Origin header + const char *prefix = "http://"; + char *ip_start = strstr(origin, prefix); + if (ip_start) { + ip_start += strlen(prefix); // Move past "http://" + + // Extract the IP address portion (up to the next '/') + char *ip_end = strchr(ip_start, '/'); + size_t ip_len = ip_end ? (size_t)(ip_end - ip_start) : strlen(ip_start); + if (ip_len < sizeof(ip_str)) { + strncpy(ip_str, ip_start, ip_len); + ip_str[ip_len] = '\0'; // Null-terminate the string + + // Convert the IP address string to uint32_t + origin_ip_addr = inet_addr(ip_str); + if (origin_ip_addr == INADDR_NONE) { + ESP_LOGW(CORS_TAG, "Invalid IP address: %s", ip_str); + } else { + ESP_LOGD(CORS_TAG, "Extracted IP address %lu", origin_ip_addr); + } + } else { + ESP_LOGW(CORS_TAG, "IP address string is too long: %s", ip_start); } - } else { - ESP_LOGE(TAG, "Failed to get WiFi mode: %s", esp_err_to_name(err)); - return ESP_FAIL; + } + + return origin_ip_addr; +} + +static esp_err_t is_network_allowed(httpd_req_t * req) +{ + if (GLOBAL_STATE->SYSTEM_MODULE.ap_enabled == true) { + ESP_LOGI(CORS_TAG, "Device in AP mode. Allowing CORS."); + return ESP_OK; } int sockfd = httpd_req_to_sockfd(req); char ipstr[INET6_ADDRSTRLEN]; struct sockaddr_in6 addr; // esp_http_server uses IPv6 addressing socklen_t addr_size = sizeof(addr); - + if (getpeername(sockfd, (struct sockaddr *)&addr, &addr_size) < 0) { - ESP_LOGE(TAG, "Error getting client IP"); + ESP_LOGE(CORS_TAG, "Error getting client IP"); return ESP_FAIL; } - uint32_t request_ip_addr = addr.sin6_addr.un.u32_addr[3]; // // Convert to IPv6 string @@ -123,55 +149,19 @@ static esp_err_t check_is_same_network(httpd_req_t * req){ // Convert to IPv4 string inet_ntop(AF_INET, &request_ip_addr, ipstr, sizeof(ipstr)); - - - char origin[128]; - char ip_str[16]; // Buffer to hold the extracted IP address string - uint32_t origin_ip_addr = 0; - // Attempt to get the Origin header - if (httpd_req_get_hdr_value_str(req, "Origin", origin, sizeof(origin)) == ESP_OK) { - ESP_LOGI("CORS", "Origin header: %s", origin); - - // Find the start of the IP address in the Origin header - const char *prefix = "http://"; - char *ip_start = strstr(origin, prefix); - if (ip_start) { - ip_start += strlen(prefix); // Move past "http://" - - // Extract the IP address portion (up to the next '/') - char *ip_end = strchr(ip_start, '/'); - size_t ip_len = ip_end ? (size_t)(ip_end - ip_start) : strlen(ip_start); - if (ip_len < sizeof(ip_str)) { - strncpy(ip_str, ip_start, ip_len); - ip_str[ip_len] = '\0'; // Null-terminate the string - - // Convert the IP address string to uint32_t - origin_ip_addr = inet_addr(ip_str); - if (origin_ip_addr == INADDR_NONE) { - ESP_LOGW("CORS", "Invalid IP address: %s", ip_str); - } else { - ESP_LOGI("CORS", "Extracted IP address %lu", origin_ip_addr); - } - } else { - ESP_LOGW("CORS", "IP address string is too long"); - } - } - }else { - // Origin is sent for CSRF sensitive requests, if there is no header it's not a concern. + uint32_t origin_ip_addr = extract_origin_ip_addr(req); + if (origin_ip_addr == 0) { origin_ip_addr = request_ip_addr; } - - if(ip_in_private_range(origin_ip_addr) == ESP_OK && ip_in_private_range(request_ip_addr) == ESP_OK){ + if (ip_in_private_range(origin_ip_addr) == ESP_OK && ip_in_private_range(request_ip_addr) == ESP_OK) { return ESP_OK; } - ESP_LOGI(TAG, "Client is NOT in the private ip ranges or same range as server."); + ESP_LOGI(CORS_TAG, "Client is NOT in the private ip ranges or same range as server."); return ESP_FAIL; - } - esp_err_t init_fs(void) { esp_vfs_spiffs_conf_t conf = {.base_path = "", .partition_label = NULL, .max_files = 5, .format_if_mount_failed = false}; @@ -253,7 +243,7 @@ static esp_err_t set_cors_headers(httpd_req_t * req) /* Recovery handler */ static esp_err_t rest_recovery_handler(httpd_req_t * req) { - if(check_is_same_network(req) != ESP_OK){ + if (is_network_allowed(req) != ESP_OK) { return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); } @@ -325,7 +315,7 @@ static esp_err_t rest_common_get_handler(httpd_req_t * req) static esp_err_t handle_options_request(httpd_req_t * req) { - if(check_is_same_network(req) != ESP_OK){ + if (is_network_allowed(req) != ESP_OK) { return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); } @@ -344,7 +334,7 @@ static esp_err_t handle_options_request(httpd_req_t * req) static esp_err_t PATCH_update_settings(httpd_req_t * req) { - if(check_is_same_network(req) != ESP_OK){ + if (is_network_allowed(req) != ESP_OK) { return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); } @@ -446,7 +436,7 @@ static esp_err_t PATCH_update_settings(httpd_req_t * req) static esp_err_t POST_restart(httpd_req_t * req) { - if(check_is_same_network(req) != ESP_OK){ + if (is_network_allowed(req) != ESP_OK) { return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); } @@ -475,7 +465,7 @@ static esp_err_t POST_restart(httpd_req_t * req) /* Simple handler for getting system handler */ static esp_err_t GET_system_info(httpd_req_t * req) { - if(check_is_same_network(req) != ESP_OK){ + if (is_network_allowed(req) != ESP_OK) { return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); } @@ -586,7 +576,7 @@ static esp_err_t GET_system_info(httpd_req_t * req) esp_err_t POST_WWW_update(httpd_req_t * req) { - if(check_is_same_network(req) != ESP_OK){ + if (is_network_allowed(req) != ESP_OK) { return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); } @@ -644,7 +634,7 @@ esp_err_t POST_WWW_update(httpd_req_t * req) */ esp_err_t POST_OTA_update(httpd_req_t * req) { - if(check_is_same_network(req) != ESP_OK){ + if (is_network_allowed(req) != ESP_OK) { return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); } @@ -767,7 +757,7 @@ void send_log_to_websocket(char *message) */ esp_err_t echo_handler(httpd_req_t * req) { - if(check_is_same_network(req) != ESP_OK){ + if (is_network_allowed(req) != ESP_OK) { return httpd_resp_send_err(req, HTTPD_401_UNAUTHORIZED, "Unauthorized"); }