Skip to content

Commit

Permalink
Clean up CORS implemention (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
eandersson authored Jan 17, 2025
1 parent 40bf6b5 commit 06be7f5
Showing 1 changed file with 72 additions and 82 deletions.
154 changes: 72 additions & 82 deletions main/http_server/http_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <pthread.h>

static const char * TAG = "http_server";
static const char * CORS_TAG = "CORS";

static GlobalState * GLOBAL_STATE;
static httpd_handle_t server = NULL;
Expand Down Expand Up @@ -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
Expand All @@ -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};
Expand Down Expand Up @@ -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");
}

Expand Down Expand Up @@ -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");
}

Expand All @@ -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");
}

Expand Down Expand Up @@ -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");
}

Expand Down Expand Up @@ -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");
}

Expand Down Expand Up @@ -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");
}

Expand Down Expand Up @@ -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");
}

Expand Down Expand Up @@ -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");
}

Expand Down

0 comments on commit 06be7f5

Please sign in to comment.