Refactor code for consistency and readability
- Updated function parameter formatting to use consistent pointer notation. - Removed unnecessary whitespace and adjusted comments for clarity. - Improved variable declarations and initializations in websocket.c for better readability. - Ensured consistent use of types and formatting across server_config and websocket headers. - Enhanced overall code style to align with best practices.
This commit is contained in:
@@ -23,14 +23,11 @@ typedef enum
|
|||||||
CONFIG_SSL_CERT_PATH,
|
CONFIG_SSL_CERT_PATH,
|
||||||
CONFIG_SSL_KEY_PATH,
|
CONFIG_SSL_KEY_PATH,
|
||||||
CONFIG_UNKNOWN
|
CONFIG_UNKNOWN
|
||||||
|
|
||||||
} ConfigKey;
|
} ConfigKey;
|
||||||
|
|
||||||
// Trim whitespace from both ends of a string
|
// Trim whitespace from both ends of a string
|
||||||
static char *trim_whitespace(char *str)
|
static char* trim_whitespace(char* str)
|
||||||
{
|
{
|
||||||
char *end;
|
|
||||||
|
|
||||||
// Trim leading space
|
// Trim leading space
|
||||||
while (isspace((unsigned char)*str))
|
while (isspace((unsigned char)*str))
|
||||||
str++;
|
str++;
|
||||||
@@ -39,7 +36,7 @@ static char *trim_whitespace(char *str)
|
|||||||
return str;
|
return str;
|
||||||
|
|
||||||
// Trim trailing space
|
// Trim trailing space
|
||||||
end = str + strlen(str) - 1;
|
char* end = str + strlen(str) - 1;
|
||||||
while (end > str && isspace((unsigned char)*end))
|
while (end > str && isspace((unsigned char)*end))
|
||||||
end--;
|
end--;
|
||||||
|
|
||||||
@@ -48,7 +45,7 @@ static char *trim_whitespace(char *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse a boolean value (true/false, yes/no, on/off, 1/0)
|
// Parse a boolean value (true/false, yes/no, on/off, 1/0)
|
||||||
static bool parse_bool(const char *value)
|
static bool parse_bool(const char* value)
|
||||||
{
|
{
|
||||||
if (strcasecmp(value, "true") == 0 ||
|
if (strcasecmp(value, "true") == 0 ||
|
||||||
strcasecmp(value, "yes") == 0 ||
|
strcasecmp(value, "yes") == 0 ||
|
||||||
@@ -59,8 +56,9 @@ static bool parse_bool(const char *value)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse log mode value (off/classic/debug/advanced)
|
// Parse log mode value (off/classic/debug/advanced)
|
||||||
static LogMode parse_log_mode(const char *value)
|
static LogMode parse_log_mode(const char* value)
|
||||||
{
|
{
|
||||||
if (strcasecmp(value, "off") == 0 || strcmp(value, "0") == 0)
|
if (strcasecmp(value, "off") == 0 || strcmp(value, "0") == 0)
|
||||||
return LOG_MODE_OFF;
|
return LOG_MODE_OFF;
|
||||||
@@ -75,11 +73,11 @@ static LogMode parse_log_mode(const char *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map string to enum
|
// Map string to enum
|
||||||
static ConfigKey get_config_key(const char *key)
|
static ConfigKey get_config_key(const char* key)
|
||||||
{
|
{
|
||||||
static const struct
|
static const struct
|
||||||
{
|
{
|
||||||
const char *name;
|
const char* name;
|
||||||
ConfigKey key;
|
ConfigKey key;
|
||||||
} key_map[] = {
|
} key_map[] = {
|
||||||
{"port", CONFIG_PORT},
|
{"port", CONFIG_PORT},
|
||||||
@@ -109,7 +107,7 @@ static ConfigKey get_config_key(const char *key)
|
|||||||
return CONFIG_UNKNOWN;
|
return CONFIG_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
int load_config(const char *filename, ServerConfig *config)
|
int load_config(const char* filename, ServerConfig* config)
|
||||||
{
|
{
|
||||||
if (!filename || strlen(filename) > 4096)
|
if (!filename || strlen(filename) > 4096)
|
||||||
{
|
{
|
||||||
@@ -117,7 +115,7 @@ int load_config(const char *filename, ServerConfig *config)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *fp = fopen(filename, "r");
|
FILE* fp = fopen(filename, "r");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
{
|
{
|
||||||
perror("Error opening config file");
|
perror("Error opening config file");
|
||||||
@@ -135,7 +133,7 @@ int load_config(const char *filename, ServerConfig *config)
|
|||||||
line[strcspn(line, "\r\n")] = 0;
|
line[strcspn(line, "\r\n")] = 0;
|
||||||
|
|
||||||
// Trim whitespace
|
// Trim whitespace
|
||||||
char *trimmed = trim_whitespace(line);
|
char* trimmed = trim_whitespace(line);
|
||||||
|
|
||||||
// Skip empty lines and comments
|
// Skip empty lines and comments
|
||||||
if (trimmed[0] == '\0' || trimmed[0] == '#' || trimmed[0] == ';')
|
if (trimmed[0] == '\0' || trimmed[0] == '#' || trimmed[0] == ';')
|
||||||
@@ -144,7 +142,7 @@ int load_config(const char *filename, ServerConfig *config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the delimiter (= or space)
|
// Find the delimiter (= or space)
|
||||||
char *delim = strchr(trimmed, '=');
|
char* delim = strchr(trimmed, '=');
|
||||||
if (!delim)
|
if (!delim)
|
||||||
{
|
{
|
||||||
// Try space as delimiter
|
// Try space as delimiter
|
||||||
@@ -159,8 +157,8 @@ int load_config(const char *filename, ServerConfig *config)
|
|||||||
|
|
||||||
// Split into key and value
|
// Split into key and value
|
||||||
*delim = '\0';
|
*delim = '\0';
|
||||||
char *key = trim_whitespace(trimmed);
|
char* key = trim_whitespace(trimmed);
|
||||||
char *value = trim_whitespace(delim + 1);
|
char* value = trim_whitespace(delim + 1);
|
||||||
|
|
||||||
// Remove quotes from value if present
|
// Remove quotes from value if present
|
||||||
if ((value[0] == '"' || value[0] == '\'') &&
|
if ((value[0] == '"' || value[0] == '\'') &&
|
||||||
@@ -173,7 +171,7 @@ int load_config(const char *filename, ServerConfig *config)
|
|||||||
switch (get_config_key(key))
|
switch (get_config_key(key))
|
||||||
{
|
{
|
||||||
case CONFIG_PORT:
|
case CONFIG_PORT:
|
||||||
config->port = atoi(value);
|
config->port = strcoll(value, value);
|
||||||
printf("load_config: port = %d\n", config->port);
|
printf("load_config: port = %d\n", config->port);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -189,7 +187,7 @@ int load_config(const char *filename, ServerConfig *config)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CONFIG_MAX_THREADS:
|
case CONFIG_MAX_THREADS:
|
||||||
config->max_threads = atoi(value);
|
config->max_threads = strcoll(value, value);
|
||||||
printf("load_config: max_threads = %d\n", config->max_threads);
|
printf("load_config: max_threads = %d\n", config->max_threads);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -216,15 +214,22 @@ int load_config(const char *filename, ServerConfig *config)
|
|||||||
case CONFIG_LOG_MODE:
|
case CONFIG_LOG_MODE:
|
||||||
config->log_mode = parse_log_mode(value);
|
config->log_mode = parse_log_mode(value);
|
||||||
printf("load_config: log_mode = %s\n",
|
printf("load_config: log_mode = %s\n",
|
||||||
config->log_mode == LOG_MODE_OFF ? "off" :
|
config->log_mode == LOG_MODE_OFF
|
||||||
config->log_mode == LOG_MODE_DEBUG ? "debug" :
|
? "off"
|
||||||
config->log_mode == LOG_MODE_ADVANCED ? "advanced" : "classic");
|
: config->log_mode == LOG_MODE_DEBUG
|
||||||
|
? "debug"
|
||||||
|
: config->log_mode == LOG_MODE_ADVANCED
|
||||||
|
? "advanced"
|
||||||
|
: "classic");
|
||||||
break;
|
break;
|
||||||
case CONFIG_VERBOSE:
|
case CONFIG_VERBOSE:
|
||||||
// Backwards compatibility: map verbose boolean to log_mode
|
// Backwards compatibility: map verbose boolean to log_mode
|
||||||
if (parse_bool(value)) {
|
if (parse_bool(value))
|
||||||
|
{
|
||||||
config->log_mode = LOG_MODE_CLASSIC;
|
config->log_mode = LOG_MODE_CLASSIC;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
config->log_mode = LOG_MODE_OFF;
|
config->log_mode = LOG_MODE_OFF;
|
||||||
}
|
}
|
||||||
printf("load_config: verbose (legacy) -> log_mode = %s\n",
|
printf("load_config: verbose (legacy) -> log_mode = %s\n",
|
||||||
@@ -256,7 +261,7 @@ int load_config(const char *filename, ServerConfig *config)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CONFIG_MAX_CONNECTIONS:
|
case CONFIG_MAX_CONNECTIONS:
|
||||||
config->max_connections = atoi(value);
|
config->max_connections = strcoll(value, value);
|
||||||
printf("load_config: max_connections: = %d\n", config->max_connections);
|
printf("load_config: max_connections: = %d\n", config->max_connections);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
166
src/http2.c
166
src/http2.c
@@ -10,19 +10,19 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
extern ServerConfig config;
|
extern ServerConfig config;
|
||||||
extern void log_event(const char *message);
|
extern void log_event(const char* message);
|
||||||
extern char *get_mime_type(const char *filepath);
|
extern char* get_mime_type(const char* filepath);
|
||||||
extern char *sanitize_url(const char *url);
|
extern char* sanitize_url(const char* url);
|
||||||
|
|
||||||
// ALPN callback - select HTTP/2 protocol
|
// ALPN callback - select HTTP/2 protocol
|
||||||
int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
int alpn_select_proto_cb(SSL* ssl, const unsigned char** out,
|
||||||
unsigned char *outlen, const unsigned char *in,
|
unsigned char* outlen, const unsigned char* in,
|
||||||
unsigned int inlen, void *arg)
|
unsigned int inlen, void* arg)
|
||||||
{
|
{
|
||||||
(void)ssl;
|
(void)ssl;
|
||||||
(void)arg;
|
(void)arg;
|
||||||
|
|
||||||
int ret = nghttp2_select_next_protocol((unsigned char **)out, outlen,
|
int ret = nghttp2_select_next_protocol((unsigned char**)out, outlen,
|
||||||
in, inlen);
|
in, inlen);
|
||||||
|
|
||||||
if (ret == 1)
|
if (ret == 1)
|
||||||
@@ -37,17 +37,17 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No match, use HTTP/1.1 as fallback
|
// No match, use HTTP/1.1 as fallback
|
||||||
*out = (const unsigned char *)"http/1.1";
|
*out = (const unsigned char*)"http/1.1";
|
||||||
*outlen = 8;
|
*outlen = 8;
|
||||||
return SSL_TLSEXT_ERR_OK;
|
return SSL_TLSEXT_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data read callback for nghttp2
|
// Data read callback for nghttp2
|
||||||
static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
static ssize_t file_read_callback(nghttp2_session* session, int32_t stream_id,
|
||||||
uint8_t *buf, size_t length,
|
uint8_t* buf, size_t length,
|
||||||
uint32_t *data_flags,
|
uint32_t* data_flags,
|
||||||
nghttp2_data_source *source,
|
nghttp2_data_source* source,
|
||||||
void *user_data)
|
void* user_data)
|
||||||
{
|
{
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)stream_id;
|
(void)stream_id;
|
||||||
@@ -56,8 +56,7 @@ static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||||||
int fd = source->fd;
|
int fd = source->fd;
|
||||||
ssize_t nread;
|
ssize_t nread;
|
||||||
|
|
||||||
while ((nread = read(fd, buf, length)) == -1 && errno == EINTR)
|
while ((nread = read(fd, buf, length)) == -1 && errno == EINTR);
|
||||||
;
|
|
||||||
|
|
||||||
if (nread == -1)
|
if (nread == -1)
|
||||||
{
|
{
|
||||||
@@ -73,13 +72,13 @@ static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send callback for nghttp2
|
// Send callback for nghttp2
|
||||||
static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
|
static ssize_t send_callback(nghttp2_session* session, const uint8_t* data,
|
||||||
size_t length, int flags, void *user_data)
|
size_t length, int flags, void* user_data)
|
||||||
{
|
{
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)flags;
|
(void)flags;
|
||||||
|
|
||||||
http2_session_t *h2_session = (http2_session_t *)user_data;
|
http2_session_t* h2_session = (http2_session_t*)user_data;
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
|
||||||
if (h2_session->ssl)
|
if (h2_session->ssl)
|
||||||
@@ -112,13 +111,13 @@ static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Receive callback for nghttp2
|
// Receive callback for nghttp2
|
||||||
static ssize_t recv_callback(nghttp2_session *session, uint8_t *buf,
|
static ssize_t recv_callback(nghttp2_session* session, uint8_t* buf,
|
||||||
size_t length, int flags, void *user_data)
|
size_t length, int flags, void* user_data)
|
||||||
{
|
{
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)flags;
|
(void)flags;
|
||||||
|
|
||||||
http2_session_t *h2_session = (http2_session_t *)user_data;
|
http2_session_t* h2_session = (http2_session_t*)user_data;
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
|
|
||||||
if (h2_session->ssl)
|
if (h2_session->ssl)
|
||||||
@@ -159,9 +158,9 @@ static ssize_t recv_callback(nghttp2_session *session, uint8_t *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Frame receive callback
|
// Frame receive callback
|
||||||
static int on_frame_recv_callback(nghttp2_session *session,
|
static int on_frame_recv_callback(nghttp2_session* session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame* frame,
|
||||||
void *user_data)
|
void* user_data)
|
||||||
{
|
{
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
|
|
||||||
@@ -173,28 +172,29 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
log_event("HTTP/2: Received HEADERS frame (request)");
|
log_event("HTTP/2: Received HEADERS frame (request)");
|
||||||
|
|
||||||
// Get stream data
|
// Get stream data
|
||||||
http2_stream_data_t *stream_data =
|
http2_stream_data_t* stream_data =
|
||||||
(http2_stream_data_t *)nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
(http2_stream_data_t*)nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
|
|
||||||
if (stream_data && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM))
|
if (stream_data && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM))
|
||||||
{
|
{
|
||||||
// Request is complete, send response
|
// Request is complete, send response
|
||||||
char *path = stream_data->request_path;
|
char* path = stream_data->request_path;
|
||||||
if (strlen(path) == 0)
|
if (strlen(path) == 0)
|
||||||
{
|
{
|
||||||
strcpy(path, "/");
|
strcpy(path, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanitize URL
|
// Sanitize URL
|
||||||
char *sanitized = sanitize_url(path);
|
char* sanitized = sanitize_url(path);
|
||||||
if (!sanitized)
|
if (!sanitized)
|
||||||
{
|
{
|
||||||
log_event("HTTP/2: Blocked malicious URL");
|
log_event("HTTP/2: Blocked malicious URL");
|
||||||
|
|
||||||
// Send 403 error
|
// Send 403 error
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
{(uint8_t *)":status", (uint8_t *)"403", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)":status", (uint8_t*)"403", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}};
|
{(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}
|
||||||
|
};
|
||||||
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -213,8 +213,9 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
// Send 404 error
|
// Send 404 error
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
{(uint8_t *)":status", (uint8_t *)"404", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)":status", (uint8_t*)"404", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}};
|
{(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}
|
||||||
|
};
|
||||||
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -228,8 +229,9 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
// Send 500 error
|
// Send 500 error
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
{(uint8_t *)":status", (uint8_t *)"500", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)":status", (uint8_t*)"500", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}};
|
{(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}
|
||||||
|
};
|
||||||
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -241,14 +243,15 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
|
|
||||||
// Send 500 error
|
// Send 500 error
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
{(uint8_t *)":status", (uint8_t *)"500", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)":status", (uint8_t*)"500", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}};
|
{(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}
|
||||||
|
};
|
||||||
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get MIME type
|
// Get MIME type
|
||||||
char *mime_type = get_mime_type(filepath);
|
char* mime_type = get_mime_type(filepath);
|
||||||
if (!mime_type)
|
if (!mime_type)
|
||||||
{
|
{
|
||||||
mime_type = strdup("application/octet-stream");
|
mime_type = strdup("application/octet-stream");
|
||||||
@@ -260,7 +263,7 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
stream_data->mime_type = mime_type;
|
stream_data->mime_type = mime_type;
|
||||||
|
|
||||||
// Build response headers - allocate content length string
|
// Build response headers - allocate content length string
|
||||||
char *content_length = malloc(32);
|
char* content_length = malloc(32);
|
||||||
if (!content_length)
|
if (!content_length)
|
||||||
{
|
{
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -274,10 +277,14 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
stream_data->content_length = content_length;
|
stream_data->content_length = content_length;
|
||||||
|
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
{(uint8_t *)":status", (uint8_t *)"200", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)":status", (uint8_t*)"200", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"content-type", (uint8_t *)mime_type, 12, strlen(mime_type), NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)"content-type", (uint8_t*)mime_type, 12, strlen(mime_type), NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"content-length", (uint8_t *)content_length, 14, strlen(content_length), NGHTTP2_NV_FLAG_NONE},
|
{
|
||||||
{(uint8_t *)"server", (uint8_t *)"Carbon/2.0", 6, 10, NGHTTP2_NV_FLAG_NONE}};
|
(uint8_t*)"content-length", (uint8_t*)content_length, 14, strlen(content_length),
|
||||||
|
NGHTTP2_NV_FLAG_NONE
|
||||||
|
},
|
||||||
|
{(uint8_t*)"server", (uint8_t*)"Carbon/2.0", 6, 10, NGHTTP2_NV_FLAG_NONE}
|
||||||
|
};
|
||||||
|
|
||||||
// Submit response with file data provider
|
// Submit response with file data provider
|
||||||
nghttp2_data_provider data_prd;
|
nghttp2_data_provider data_prd;
|
||||||
@@ -307,15 +314,15 @@ static int on_frame_recv_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stream close callback
|
// Stream close callback
|
||||||
static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
static int on_stream_close_callback(nghttp2_session* session, int32_t stream_id,
|
||||||
uint32_t error_code, void *user_data)
|
uint32_t error_code, void* user_data)
|
||||||
{
|
{
|
||||||
(void)error_code;
|
(void)error_code;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
|
|
||||||
// Get stream data and clean up
|
// Get stream data and clean up
|
||||||
http2_stream_data_t *stream_data =
|
http2_stream_data_t* stream_data =
|
||||||
(http2_stream_data_t *)nghttp2_session_get_stream_user_data(session, stream_id);
|
(http2_stream_data_t*)nghttp2_session_get_stream_user_data(session, stream_id);
|
||||||
|
|
||||||
if (stream_data)
|
if (stream_data)
|
||||||
{
|
{
|
||||||
@@ -340,11 +347,11 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Header callback
|
// Header callback
|
||||||
static int on_header_callback(nghttp2_session *session,
|
static int on_header_callback(nghttp2_session* session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame* frame,
|
||||||
const uint8_t *name, size_t namelen,
|
const uint8_t* name, size_t namelen,
|
||||||
const uint8_t *value, size_t valuelen,
|
const uint8_t* value, size_t valuelen,
|
||||||
uint8_t flags, void *user_data)
|
uint8_t flags, void* user_data)
|
||||||
{
|
{
|
||||||
(void)flags;
|
(void)flags;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
@@ -356,8 +363,8 @@ static int on_header_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get stream data
|
// Get stream data
|
||||||
http2_stream_data_t *stream_data =
|
http2_stream_data_t* stream_data =
|
||||||
(http2_stream_data_t *)nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
(http2_stream_data_t*)nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||||
|
|
||||||
if (!stream_data)
|
if (!stream_data)
|
||||||
{
|
{
|
||||||
@@ -367,7 +374,9 @@ static int on_header_callback(nghttp2_session *session,
|
|||||||
// Process request headers
|
// Process request headers
|
||||||
if (namelen == 5 && memcmp(name, ":path", 5) == 0)
|
if (namelen == 5 && memcmp(name, ":path", 5) == 0)
|
||||||
{
|
{
|
||||||
size_t copy_len = valuelen < sizeof(stream_data->request_path) - 1 ? valuelen : sizeof(stream_data->request_path) - 1;
|
size_t copy_len = valuelen < sizeof(stream_data->request_path) - 1
|
||||||
|
? valuelen
|
||||||
|
: sizeof(stream_data->request_path) - 1;
|
||||||
memcpy(stream_data->request_path, value, copy_len);
|
memcpy(stream_data->request_path, value, copy_len);
|
||||||
stream_data->request_path[copy_len] = '\0';
|
stream_data->request_path[copy_len] = '\0';
|
||||||
|
|
||||||
@@ -380,9 +389,9 @@ static int on_header_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Begin headers callback
|
// Begin headers callback
|
||||||
static int on_begin_headers_callback(nghttp2_session *session,
|
static int on_begin_headers_callback(nghttp2_session* session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame* frame,
|
||||||
void *user_data)
|
void* user_data)
|
||||||
{
|
{
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
@@ -394,7 +403,7 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allocate stream data
|
// Allocate stream data
|
||||||
http2_stream_data_t *stream_data = calloc(1, sizeof(http2_stream_data_t));
|
http2_stream_data_t* stream_data = calloc(1, sizeof(http2_stream_data_t));
|
||||||
if (!stream_data)
|
if (!stream_data)
|
||||||
{
|
{
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
@@ -404,14 +413,14 @@ static int on_begin_headers_callback(nghttp2_session *session,
|
|||||||
stream_data->fd = -1;
|
stream_data->fd = -1;
|
||||||
|
|
||||||
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, stream_data);
|
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, stream_data);
|
||||||
|
free(stream_data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data chunk receive callback
|
// Data chunk receive callback
|
||||||
static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
static int on_data_chunk_recv_callback(nghttp2_session* session, uint8_t flags,
|
||||||
int32_t stream_id, const uint8_t *data,
|
int32_t stream_id, const uint8_t* data,
|
||||||
size_t len, void *user_data)
|
size_t len, void* user_data)
|
||||||
{
|
{
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)flags;
|
(void)flags;
|
||||||
@@ -425,14 +434,14 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize HTTP/2 session
|
// Initialize HTTP/2 session
|
||||||
int http2_session_init(http2_session_t *h2_session, int client_socket, SSL *ssl)
|
int http2_session_init(http2_session_t* h2_session, int client_socket, SSL* ssl)
|
||||||
{
|
{
|
||||||
h2_session->client_socket = client_socket;
|
h2_session->client_socket = client_socket;
|
||||||
h2_session->ssl = ssl;
|
h2_session->ssl = ssl;
|
||||||
h2_session->handshake_complete = false;
|
h2_session->handshake_complete = false;
|
||||||
|
|
||||||
// Setup callbacks
|
// Setup callbacks
|
||||||
nghttp2_session_callbacks *callbacks;
|
nghttp2_session_callbacks* callbacks;
|
||||||
nghttp2_session_callbacks_new(&callbacks);
|
nghttp2_session_callbacks_new(&callbacks);
|
||||||
|
|
||||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||||
@@ -456,7 +465,8 @@ int http2_session_init(http2_session_t *h2_session, int client_socket, SSL *ssl)
|
|||||||
// Send initial SETTINGS frame
|
// Send initial SETTINGS frame
|
||||||
nghttp2_settings_entry settings[] = {
|
nghttp2_settings_entry settings[] = {
|
||||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
|
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
|
||||||
{NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 65535}};
|
{NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 65535}
|
||||||
|
};
|
||||||
|
|
||||||
rv = nghttp2_submit_settings(h2_session->session, NGHTTP2_FLAG_NONE,
|
rv = nghttp2_submit_settings(h2_session->session, NGHTTP2_FLAG_NONE,
|
||||||
settings, sizeof(settings) / sizeof(settings[0]));
|
settings, sizeof(settings) / sizeof(settings[0]));
|
||||||
@@ -474,7 +484,7 @@ int http2_session_init(http2_session_t *h2_session, int client_socket, SSL *ssl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup HTTP/2 session
|
// Cleanup HTTP/2 session
|
||||||
void http2_session_cleanup(http2_session_t *h2_session)
|
void http2_session_cleanup(http2_session_t* h2_session)
|
||||||
{
|
{
|
||||||
if (h2_session->session)
|
if (h2_session->session)
|
||||||
{
|
{
|
||||||
@@ -484,8 +494,8 @@ void http2_session_cleanup(http2_session_t *h2_session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send HTTP/2 response
|
// Send HTTP/2 response
|
||||||
int http2_send_response(http2_session_t *h2_session, int32_t stream_id,
|
int http2_send_response(http2_session_t* h2_session, int32_t stream_id,
|
||||||
const char *data, size_t len, bool end_stream)
|
const char* data, size_t len, bool end_stream)
|
||||||
{
|
{
|
||||||
(void)data; // Unused in current implementation
|
(void)data; // Unused in current implementation
|
||||||
(void)len; // Unused in current implementation
|
(void)len; // Unused in current implementation
|
||||||
@@ -493,9 +503,10 @@ int http2_send_response(http2_session_t *h2_session, int32_t stream_id,
|
|||||||
|
|
||||||
// Send response headers
|
// Send response headers
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
{(uint8_t *)":status", (uint8_t *)"200", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)":status", (uint8_t*)"200", 7, 3, NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"content-type", (uint8_t *)"text/html", 12, 9, NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)"content-type", (uint8_t*)"text/html", 12, 9, NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"server", (uint8_t *)"Carbon/2.0", 6, 10, NGHTTP2_NV_FLAG_NONE}};
|
{(uint8_t*)"server", (uint8_t*)"Carbon/2.0", 6, 10, NGHTTP2_NV_FLAG_NONE}
|
||||||
|
};
|
||||||
|
|
||||||
int rv = nghttp2_submit_response(h2_session->session, stream_id, hdrs, 3, NULL);
|
int rv = nghttp2_submit_response(h2_session->session, stream_id, hdrs, 3, NULL);
|
||||||
if (rv != 0)
|
if (rv != 0)
|
||||||
@@ -507,15 +518,16 @@ int http2_send_response(http2_session_t *h2_session, int32_t stream_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send HTTP/2 error response
|
// Send HTTP/2 error response
|
||||||
int http2_send_error(http2_session_t *h2_session, int32_t stream_id,
|
int http2_send_error(http2_session_t* h2_session, int32_t stream_id,
|
||||||
int status_code, const char *message)
|
int status_code, const char* message)
|
||||||
{
|
{
|
||||||
char status_str[4];
|
char status_str[4];
|
||||||
snprintf(status_str, sizeof(status_str), "%d", status_code);
|
snprintf(status_str, sizeof(status_str), "%d", status_code);
|
||||||
|
|
||||||
nghttp2_nv hdrs[] = {
|
nghttp2_nv hdrs[] = {
|
||||||
{(uint8_t *)":status", (uint8_t *)status_str, 7, strlen(status_str), NGHTTP2_NV_FLAG_NONE},
|
{(uint8_t*)":status", (uint8_t*)status_str, 7, strlen(status_str), NGHTTP2_NV_FLAG_NONE},
|
||||||
{(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}};
|
{(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}
|
||||||
|
};
|
||||||
|
|
||||||
int rv = nghttp2_submit_response(h2_session->session, stream_id, hdrs, 2, NULL);
|
int rv = nghttp2_submit_response(h2_session->session, stream_id, hdrs, 2, NULL);
|
||||||
if (rv != 0)
|
if (rv != 0)
|
||||||
@@ -526,7 +538,7 @@ int http2_send_error(http2_session_t *h2_session, int32_t stream_id,
|
|||||||
if (message)
|
if (message)
|
||||||
{
|
{
|
||||||
nghttp2_data_provider prd;
|
nghttp2_data_provider prd;
|
||||||
prd.source.ptr = (void *)message;
|
prd.source.ptr = (void*)message;
|
||||||
prd.read_callback = NULL;
|
prd.read_callback = NULL;
|
||||||
|
|
||||||
nghttp2_submit_data(h2_session->session, NGHTTP2_FLAG_END_STREAM,
|
nghttp2_submit_data(h2_session->session, NGHTTP2_FLAG_END_STREAM,
|
||||||
@@ -537,7 +549,7 @@ int http2_send_error(http2_session_t *h2_session, int32_t stream_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle HTTP/2 connection
|
// Handle HTTP/2 connection
|
||||||
int http2_handle_connection(http2_session_t *h2_session)
|
int http2_handle_connection(http2_session_t* h2_session)
|
||||||
{
|
{
|
||||||
// Receive and process frames first
|
// Receive and process frames first
|
||||||
int rv = nghttp2_session_recv(h2_session->session);
|
int rv = nghttp2_session_recv(h2_session->session);
|
||||||
|
|||||||
30
src/http2.h
30
src/http2.h
@@ -8,8 +8,8 @@
|
|||||||
// HTTP/2 session context
|
// HTTP/2 session context
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
nghttp2_session *session;
|
nghttp2_session* session;
|
||||||
SSL *ssl;
|
SSL* ssl;
|
||||||
int client_socket;
|
int client_socket;
|
||||||
bool handshake_complete;
|
bool handshake_complete;
|
||||||
} http2_session_t;
|
} http2_session_t;
|
||||||
@@ -19,25 +19,25 @@ typedef struct
|
|||||||
{
|
{
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
char request_path[256];
|
char request_path[256];
|
||||||
char *request_method;
|
char* request_method;
|
||||||
int fd; // File descriptor for response
|
int fd; // File descriptor for response
|
||||||
size_t file_size;
|
size_t file_size;
|
||||||
char *mime_type;
|
char* mime_type;
|
||||||
char *content_length;
|
char* content_length;
|
||||||
} http2_stream_data_t;
|
} http2_stream_data_t;
|
||||||
|
|
||||||
// Function prototypes
|
// Function prototypes
|
||||||
int http2_session_init(http2_session_t *session, int client_socket, SSL *ssl);
|
int http2_session_init(http2_session_t* session, int client_socket, SSL* ssl);
|
||||||
void http2_session_cleanup(http2_session_t *session);
|
void http2_session_cleanup(http2_session_t* session);
|
||||||
int http2_handle_connection(http2_session_t *session);
|
int http2_handle_connection(http2_session_t* session);
|
||||||
int http2_send_response(http2_session_t *session, int32_t stream_id,
|
int http2_send_response(http2_session_t* session, int32_t stream_id,
|
||||||
const char *data, size_t len, bool end_stream);
|
const char* data, size_t len, bool end_stream);
|
||||||
int http2_send_error(http2_session_t *session, int32_t stream_id,
|
int http2_send_error(http2_session_t* session, int32_t stream_id,
|
||||||
int status_code, const char *message);
|
int status_code, const char* message);
|
||||||
|
|
||||||
// ALPN callback for protocol selection
|
// ALPN callback for protocol selection
|
||||||
int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
|
int alpn_select_proto_cb(SSL* ssl, const unsigned char** out,
|
||||||
unsigned char *outlen, const unsigned char *in,
|
unsigned char* outlen, const unsigned char* in,
|
||||||
unsigned int inlen, void *arg);
|
unsigned int inlen, void* arg);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
201
src/logging.c
201
src/logging.c
@@ -1,6 +1,5 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -9,6 +8,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
// ANSI color codes
|
// ANSI color codes
|
||||||
#define COLOR_RESET "\x1b[0m"
|
#define COLOR_RESET "\x1b[0m"
|
||||||
@@ -41,7 +41,8 @@ static pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|||||||
static bool g_log_initialized = false;
|
static bool g_log_initialized = false;
|
||||||
|
|
||||||
// Performance tracking
|
// Performance tracking
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
char operation[64];
|
char operation[64];
|
||||||
struct timeval start_time;
|
struct timeval start_time;
|
||||||
bool active;
|
bool active;
|
||||||
@@ -52,9 +53,10 @@ static PerfTracker g_perf_trackers[MAX_PERF_TRACKERS];
|
|||||||
static pthread_mutex_t g_perf_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t g_perf_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
// Get color for log level
|
// Get color for log level
|
||||||
static const char *get_level_color(LogLevel level)
|
static const char* get_level_color(LogLevel level)
|
||||||
{
|
{
|
||||||
switch (level) {
|
switch (level)
|
||||||
|
{
|
||||||
case LOG_LEVEL_ERROR: return COLOR_RED;
|
case LOG_LEVEL_ERROR: return COLOR_RED;
|
||||||
case LOG_LEVEL_WARN: return COLOR_YELLOW;
|
case LOG_LEVEL_WARN: return COLOR_YELLOW;
|
||||||
case LOG_LEVEL_INFO: return COLOR_GREEN;
|
case LOG_LEVEL_INFO: return COLOR_GREEN;
|
||||||
@@ -65,9 +67,10 @@ static const char *get_level_color(LogLevel level)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get level prefix
|
// Get level prefix
|
||||||
static const char *get_level_prefix(LogLevel level)
|
static const char* get_level_prefix(LogLevel level)
|
||||||
{
|
{
|
||||||
switch (level) {
|
switch (level)
|
||||||
|
{
|
||||||
case LOG_LEVEL_ERROR: return "ERROR";
|
case LOG_LEVEL_ERROR: return "ERROR";
|
||||||
case LOG_LEVEL_WARN: return "WARN ";
|
case LOG_LEVEL_WARN: return "WARN ";
|
||||||
case LOG_LEVEL_INFO: return "INFO ";
|
case LOG_LEVEL_INFO: return "INFO ";
|
||||||
@@ -78,9 +81,10 @@ static const char *get_level_prefix(LogLevel level)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get category name
|
// Get category name
|
||||||
static const char *get_category_name(LogCategory cat)
|
static const char* get_category_name(LogCategory cat)
|
||||||
{
|
{
|
||||||
switch (cat) {
|
switch (cat)
|
||||||
|
{
|
||||||
case LOG_CAT_GENERAL: return "GENERAL";
|
case LOG_CAT_GENERAL: return "GENERAL";
|
||||||
case LOG_CAT_SECURITY: return "SECURITY";
|
case LOG_CAT_SECURITY: return "SECURITY";
|
||||||
case LOG_CAT_NETWORK: return "NETWORK";
|
case LOG_CAT_NETWORK: return "NETWORK";
|
||||||
@@ -93,9 +97,10 @@ static const char *get_category_name(LogCategory cat)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *log_level_to_string(LogLevel level)
|
const char* log_level_to_string(LogLevel level)
|
||||||
{
|
{
|
||||||
switch (level) {
|
switch (level)
|
||||||
|
{
|
||||||
case LOG_LEVEL_OFF: return "off";
|
case LOG_LEVEL_OFF: return "off";
|
||||||
case LOG_LEVEL_ERROR: return "error";
|
case LOG_LEVEL_ERROR: return "error";
|
||||||
case LOG_LEVEL_WARN: return "warn";
|
case LOG_LEVEL_WARN: return "warn";
|
||||||
@@ -106,9 +111,10 @@ const char *log_level_to_string(LogLevel level)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *log_mode_to_string(LogLevel level)
|
const char* log_mode_to_string(LogLevel level)
|
||||||
{
|
{
|
||||||
switch (level) {
|
switch (level)
|
||||||
|
{
|
||||||
case LOG_LEVEL_OFF: return "off";
|
case LOG_LEVEL_OFF: return "off";
|
||||||
case LOG_LEVEL_ERROR:
|
case LOG_LEVEL_ERROR:
|
||||||
case LOG_LEVEL_WARN:
|
case LOG_LEVEL_WARN:
|
||||||
@@ -119,7 +125,7 @@ const char *log_mode_to_string(LogLevel level)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogLevel log_level_from_string(const char *str)
|
LogLevel log_level_from_string(const char* str)
|
||||||
{
|
{
|
||||||
if (!str) return LOG_LEVEL_INFO;
|
if (!str) return LOG_LEVEL_INFO;
|
||||||
|
|
||||||
@@ -157,17 +163,24 @@ static void rotate_logs(void)
|
|||||||
|
|
||||||
// Rotate existing backup files
|
// Rotate existing backup files
|
||||||
char old_path[512], new_path[512];
|
char old_path[512], new_path[512];
|
||||||
for (int i = g_log_config.max_backup_files - 1; i >= 0; i--) {
|
for (int i = g_log_config.max_backup_files - 1; i >= 0; i--)
|
||||||
if (i == 0) {
|
{
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
snprintf(old_path, sizeof(old_path), "%s", g_log_config.log_file);
|
snprintf(old_path, sizeof(old_path), "%s", g_log_config.log_file);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
snprintf(old_path, sizeof(old_path), "%s.%d", g_log_config.log_file, i);
|
snprintf(old_path, sizeof(old_path), "%s.%d", g_log_config.log_file, i);
|
||||||
}
|
}
|
||||||
snprintf(new_path, sizeof(new_path), "%s.%d", g_log_config.log_file, i + 1);
|
snprintf(new_path, sizeof(new_path), "%s.%d", g_log_config.log_file, i + 1);
|
||||||
|
|
||||||
if (i + 1 >= g_log_config.max_backup_files) {
|
if (i + 1 >= g_log_config.max_backup_files)
|
||||||
|
{
|
||||||
unlink(old_path);
|
unlink(old_path);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
rename(old_path, new_path);
|
rename(old_path, new_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,23 +193,26 @@ static void ensure_log_directory(void)
|
|||||||
strncpy(log_dir, g_log_config.log_file, sizeof(log_dir) - 1);
|
strncpy(log_dir, g_log_config.log_file, sizeof(log_dir) - 1);
|
||||||
log_dir[sizeof(log_dir) - 1] = '\0';
|
log_dir[sizeof(log_dir) - 1] = '\0';
|
||||||
|
|
||||||
char *dir_path = dirname(log_dir);
|
char* dir_path = dirname(log_dir);
|
||||||
if (!dir_path || strcmp(dir_path, ".") == 0)
|
if (!dir_path || strcmp(dir_path, ".") == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(dir_path, &st) != 0) {
|
if (stat(dir_path, &st) != 0)
|
||||||
if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) {
|
{
|
||||||
|
if (mkdir(dir_path, 0755) != 0 && errno != EEXIST)
|
||||||
|
{
|
||||||
fprintf(stderr, "Failed to create log directory: %s\n", strerror(errno));
|
fprintf(stderr, "Failed to create log directory: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_init(LogConfig *config)
|
void log_init(LogConfig* config)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&g_log_mutex);
|
pthread_mutex_lock(&g_log_mutex);
|
||||||
|
|
||||||
if (config) {
|
if (config)
|
||||||
|
{
|
||||||
memcpy(&g_log_config, config, sizeof(LogConfig));
|
memcpy(&g_log_config, config, sizeof(LogConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,8 +247,8 @@ void log_set_categories(LogCategory categories)
|
|||||||
pthread_mutex_unlock(&g_log_mutex);
|
pthread_mutex_unlock(&g_log_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_write(LogLevel level, LogCategory category, const char *file,
|
void log_write(LogLevel level, LogCategory category, const char* file,
|
||||||
int line, const char *func, const char *fmt, ...)
|
int line, const char* func, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
// Quick check without lock
|
// Quick check without lock
|
||||||
if (level == LOG_LEVEL_OFF || g_log_config.level == LOG_LEVEL_OFF)
|
if (level == LOG_LEVEL_OFF || g_log_config.level == LOG_LEVEL_OFF)
|
||||||
@@ -253,7 +269,8 @@ void log_write(LogLevel level, LogCategory category, const char *file,
|
|||||||
localtime_r(&tv.tv_sec, &tm);
|
localtime_r(&tv.tv_sec, &tm);
|
||||||
|
|
||||||
char timestamp[64] = "";
|
char timestamp[64] = "";
|
||||||
if (g_log_config.include_timestamp) {
|
if (g_log_config.include_timestamp)
|
||||||
|
{
|
||||||
snprintf(timestamp, sizeof(timestamp), "%04d-%02d-%02d %02d:%02d:%02d.%03ld",
|
snprintf(timestamp, sizeof(timestamp), "%04d-%02d-%02d %02d:%02d:%02d.%03ld",
|
||||||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec / 1000);
|
tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec / 1000);
|
||||||
@@ -261,14 +278,16 @@ void log_write(LogLevel level, LogCategory category, const char *file,
|
|||||||
|
|
||||||
// Get thread ID
|
// Get thread ID
|
||||||
char thread_id[32] = "";
|
char thread_id[32] = "";
|
||||||
if (g_log_config.include_thread_id) {
|
if (g_log_config.include_thread_id)
|
||||||
|
{
|
||||||
snprintf(thread_id, sizeof(thread_id), "%lu", (unsigned long)pthread_self());
|
snprintf(thread_id, sizeof(thread_id), "%lu", (unsigned long)pthread_self());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get source location
|
// Get source location
|
||||||
char source_loc[256] = "";
|
char source_loc[256] = "";
|
||||||
if (g_log_config.include_source_location && file && func) {
|
if (g_log_config.include_source_location && file && func)
|
||||||
const char *filename = strrchr(file, '/');
|
{
|
||||||
|
const char* filename = strrchr(file, '/');
|
||||||
filename = filename ? filename + 1 : file;
|
filename = filename ? filename + 1 : file;
|
||||||
snprintf(source_loc, sizeof(source_loc), "%s:%d:%s", filename, line, func);
|
snprintf(source_loc, sizeof(source_loc), "%s:%d:%s", filename, line, func);
|
||||||
}
|
}
|
||||||
@@ -283,26 +302,34 @@ void log_write(LogLevel level, LogCategory category, const char *file,
|
|||||||
// Build log entry based on format
|
// Build log entry based on format
|
||||||
char log_entry[8192];
|
char log_entry[8192];
|
||||||
|
|
||||||
if (g_log_config.format == LOG_FORMAT_JSON) {
|
if (g_log_config.format == LOG_FORMAT_JSON)
|
||||||
|
{
|
||||||
snprintf(log_entry, sizeof(log_entry),
|
snprintf(log_entry, sizeof(log_entry),
|
||||||
"{\"timestamp\":\"%s\",\"level\":\"%s\",\"category\":\"%s\","
|
"{\"timestamp\":\"%s\",\"level\":\"%s\",\"category\":\"%s\","
|
||||||
"\"pid\":%d,\"tid\":\"%s\",\"source\":\"%s\",\"message\":\"%s\"}\n",
|
"\"pid\":%d,\"tid\":\"%s\",\"source\":\"%s\",\"message\":\"%s\"}\n",
|
||||||
timestamp, get_level_prefix(level), get_category_name(category),
|
timestamp, get_level_prefix(level), get_category_name(category),
|
||||||
getpid(), thread_id, source_loc, message);
|
getpid(), thread_id, source_loc, message);
|
||||||
} else if (g_log_config.format == LOG_FORMAT_SYSLOG) {
|
}
|
||||||
|
else if (g_log_config.format == LOG_FORMAT_SYSLOG)
|
||||||
|
{
|
||||||
// Syslog-compatible format
|
// Syslog-compatible format
|
||||||
snprintf(log_entry, sizeof(log_entry),
|
snprintf(log_entry, sizeof(log_entry),
|
||||||
"<%d>%s %s[%d]: [%s] %s\n",
|
"<%d>%s %s[%d]: [%s] %s\n",
|
||||||
level, timestamp, "carbon", getpid(),
|
level, timestamp, "carbon", getpid(),
|
||||||
get_category_name(category), message);
|
get_category_name(category), message);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Plain text format
|
// Plain text format
|
||||||
if (g_log_config.include_source_location && source_loc[0]) {
|
if (g_log_config.include_source_location && source_loc[0])
|
||||||
|
{
|
||||||
snprintf(log_entry, sizeof(log_entry),
|
snprintf(log_entry, sizeof(log_entry),
|
||||||
"[%s] [%s] [PID:%d] [TID:%s] [%s] [%s] %s\n",
|
"[%s] [%s] [PID:%d] [TID:%s] [%s] [%s] %s\n",
|
||||||
timestamp, get_level_prefix(level), getpid(), thread_id,
|
timestamp, get_level_prefix(level), getpid(), thread_id,
|
||||||
get_category_name(category), source_loc, message);
|
get_category_name(category), source_loc, message);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
snprintf(log_entry, sizeof(log_entry),
|
snprintf(log_entry, sizeof(log_entry),
|
||||||
"[%s] [%s] [PID:%d] [TID:%s] [%s] %s\n",
|
"[%s] [%s] [PID:%d] [TID:%s] [%s] %s\n",
|
||||||
timestamp, get_level_prefix(level), getpid(), thread_id,
|
timestamp, get_level_prefix(level), getpid(), thread_id,
|
||||||
@@ -311,21 +338,27 @@ void log_write(LogLevel level, LogCategory category, const char *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write to console
|
// Write to console
|
||||||
if (g_log_config.console_output) {
|
if (g_log_config.console_output)
|
||||||
if (g_log_config.colorize_console && isatty(STDOUT_FILENO)) {
|
{
|
||||||
|
if (g_log_config.colorize_console && isatty(STDOUT_FILENO))
|
||||||
|
{
|
||||||
fprintf(stdout, "%s%s%s", get_level_color(level), log_entry, COLOR_RESET);
|
fprintf(stdout, "%s%s%s", get_level_color(level), log_entry, COLOR_RESET);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fputs(log_entry, stdout);
|
fputs(log_entry, stdout);
|
||||||
}
|
}
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to file
|
// Write to file
|
||||||
if (g_log_config.file_output && g_log_config.log_file[0]) {
|
if (g_log_config.file_output && g_log_config.log_file[0])
|
||||||
|
{
|
||||||
rotate_logs();
|
rotate_logs();
|
||||||
|
|
||||||
FILE *fp = fopen(g_log_config.log_file, "a");
|
FILE* fp = fopen(g_log_config.log_file, "a");
|
||||||
if (fp) {
|
if (fp)
|
||||||
|
{
|
||||||
fputs(log_entry, fp);
|
fputs(log_entry, fp);
|
||||||
fflush(fp);
|
fflush(fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@@ -336,14 +369,14 @@ void log_write(LogLevel level, LogCategory category, const char *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Backwards compatible log_event function
|
// Backwards compatible log_event function
|
||||||
void log_event(const char *message)
|
void log_event(const char* message)
|
||||||
{
|
{
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
log_write(LOG_LEVEL_INFO, LOG_CAT_GENERAL, NULL, 0, NULL, "%s", message);
|
log_write(LOG_LEVEL_INFO, LOG_CAT_GENERAL, NULL, 0, NULL, "%s", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secure logging - sanitizes potentially sensitive data
|
// Secure logging - sanitizes potentially sensitive data
|
||||||
void log_secure(LogLevel level, LogCategory category, const char *fmt, ...)
|
void log_secure(LogLevel level, LogCategory category, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
char message[4096];
|
char message[4096];
|
||||||
va_list args;
|
va_list args;
|
||||||
@@ -352,7 +385,7 @@ void log_secure(LogLevel level, LogCategory category, const char *fmt, ...)
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
// Sanitize common sensitive patterns
|
// Sanitize common sensitive patterns
|
||||||
char *patterns[] = {
|
char* patterns[] = {
|
||||||
"password", "passwd", "pwd", "secret", "token", "key", "auth",
|
"password", "passwd", "pwd", "secret", "token", "key", "auth",
|
||||||
"credential", "credit", "ssn", "api_key", "apikey", NULL
|
"credential", "credit", "ssn", "api_key", "apikey", NULL
|
||||||
};
|
};
|
||||||
@@ -363,36 +396,44 @@ void log_secure(LogLevel level, LogCategory category, const char *fmt, ...)
|
|||||||
|
|
||||||
// Convert to lowercase for pattern matching
|
// Convert to lowercase for pattern matching
|
||||||
char lower[4096];
|
char lower[4096];
|
||||||
for (size_t i = 0; i < strlen(sanitized) && i < sizeof(lower) - 1; i++) {
|
for (size_t i = 0; i < strlen(sanitized) && i < sizeof(lower) - 1; i++)
|
||||||
|
{
|
||||||
lower[i] = tolower((unsigned char)sanitized[i]);
|
lower[i] = tolower((unsigned char)sanitized[i]);
|
||||||
}
|
}
|
||||||
lower[strlen(sanitized)] = '\0';
|
lower[strlen(sanitized)] = '\0';
|
||||||
|
|
||||||
// Check for sensitive patterns
|
// Check for sensitive patterns
|
||||||
bool has_sensitive = false;
|
bool has_sensitive = false;
|
||||||
for (int i = 0; patterns[i]; i++) {
|
for (int i = 0; patterns[i]; i++)
|
||||||
if (strstr(lower, patterns[i])) {
|
{
|
||||||
|
if (strstr(lower, patterns[i]))
|
||||||
|
{
|
||||||
has_sensitive = true;
|
has_sensitive = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_sensitive) {
|
if (has_sensitive)
|
||||||
|
{
|
||||||
log_write(level, category, NULL, 0, NULL, "[REDACTED] Message contained sensitive data");
|
log_write(level, category, NULL, 0, NULL, "[REDACTED] Message contained sensitive data");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
log_write(level, category, NULL, 0, NULL, "%s", sanitized);
|
log_write(level, category, NULL, 0, NULL, "%s", sanitized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_perf_start(const char *operation)
|
void log_perf_start(const char* operation)
|
||||||
{
|
{
|
||||||
if (g_log_config.level < LOG_LEVEL_DEBUG)
|
if (g_log_config.level < LOG_LEVEL_DEBUG)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pthread_mutex_lock(&g_perf_mutex);
|
pthread_mutex_lock(&g_perf_mutex);
|
||||||
|
|
||||||
for (int i = 0; i < MAX_PERF_TRACKERS; i++) {
|
for (int i = 0; i < MAX_PERF_TRACKERS; i++)
|
||||||
if (!g_perf_trackers[i].active) {
|
{
|
||||||
|
if (!g_perf_trackers[i].active)
|
||||||
|
{
|
||||||
strncpy(g_perf_trackers[i].operation, operation, sizeof(g_perf_trackers[i].operation) - 1);
|
strncpy(g_perf_trackers[i].operation, operation, sizeof(g_perf_trackers[i].operation) - 1);
|
||||||
g_perf_trackers[i].operation[sizeof(g_perf_trackers[i].operation) - 1] = '\0';
|
g_perf_trackers[i].operation[sizeof(g_perf_trackers[i].operation) - 1] = '\0';
|
||||||
gettimeofday(&g_perf_trackers[i].start_time, NULL);
|
gettimeofday(&g_perf_trackers[i].start_time, NULL);
|
||||||
@@ -404,7 +445,7 @@ void log_perf_start(const char *operation)
|
|||||||
pthread_mutex_unlock(&g_perf_mutex);
|
pthread_mutex_unlock(&g_perf_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_perf_end(const char *operation)
|
void log_perf_end(const char* operation)
|
||||||
{
|
{
|
||||||
if (g_log_config.level < LOG_LEVEL_DEBUG)
|
if (g_log_config.level < LOG_LEVEL_DEBUG)
|
||||||
return;
|
return;
|
||||||
@@ -414,10 +455,11 @@ void log_perf_end(const char *operation)
|
|||||||
|
|
||||||
pthread_mutex_lock(&g_perf_mutex);
|
pthread_mutex_lock(&g_perf_mutex);
|
||||||
|
|
||||||
for (int i = 0; i < MAX_PERF_TRACKERS; i++) {
|
for (int i = 0; i < MAX_PERF_TRACKERS; i++)
|
||||||
|
{
|
||||||
if (g_perf_trackers[i].active &&
|
if (g_perf_trackers[i].active &&
|
||||||
strcmp(g_perf_trackers[i].operation, operation) == 0) {
|
strcmp(g_perf_trackers[i].operation, operation) == 0)
|
||||||
|
{
|
||||||
long elapsed_us = (end_time.tv_sec - g_perf_trackers[i].start_time.tv_sec) * 1000000 +
|
long elapsed_us = (end_time.tv_sec - g_perf_trackers[i].start_time.tv_sec) * 1000000 +
|
||||||
(end_time.tv_usec - g_perf_trackers[i].start_time.tv_usec);
|
(end_time.tv_usec - g_perf_trackers[i].start_time.tv_usec);
|
||||||
|
|
||||||
@@ -425,11 +467,16 @@ void log_perf_end(const char *operation)
|
|||||||
|
|
||||||
pthread_mutex_unlock(&g_perf_mutex);
|
pthread_mutex_unlock(&g_perf_mutex);
|
||||||
|
|
||||||
if (elapsed_us > 1000000) {
|
if (elapsed_us > 1000000)
|
||||||
|
{
|
||||||
LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %.2f s", operation, elapsed_us / 1000000.0);
|
LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %.2f s", operation, elapsed_us / 1000000.0);
|
||||||
} else if (elapsed_us > 1000) {
|
}
|
||||||
|
else if (elapsed_us > 1000)
|
||||||
|
{
|
||||||
LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %.2f ms", operation, elapsed_us / 1000.0);
|
LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %.2f ms", operation, elapsed_us / 1000.0);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %ld µs", operation, elapsed_us);
|
LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %ld µs", operation, elapsed_us);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -439,7 +486,7 @@ void log_perf_end(const char *operation)
|
|||||||
pthread_mutex_unlock(&g_perf_mutex);
|
pthread_mutex_unlock(&g_perf_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_hexdump(const char *label, const void *data, size_t len)
|
void log_hexdump(const char* label, const void* data, size_t len)
|
||||||
{
|
{
|
||||||
if (g_log_config.level < LOG_LEVEL_TRACE)
|
if (g_log_config.level < LOG_LEVEL_TRACE)
|
||||||
return;
|
return;
|
||||||
@@ -448,34 +495,46 @@ void log_hexdump(const char *label, const void *data, size_t len)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Limit output size
|
// Limit output size
|
||||||
if (len > 256) {
|
if (len > 256)
|
||||||
|
{
|
||||||
LOG_TRACE(LOG_CAT_GENERAL, "%s: [%zu bytes, showing first 256]", label, len);
|
LOG_TRACE(LOG_CAT_GENERAL, "%s: [%zu bytes, showing first 256]", label, len);
|
||||||
len = 256;
|
len = 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char *bytes = (const unsigned char *)data;
|
const unsigned char* bytes = (const unsigned char*)data;
|
||||||
char line[80];
|
char line[80];
|
||||||
char ascii[17];
|
char ascii[17];
|
||||||
|
|
||||||
for (size_t i = 0; i < len; i += 16) {
|
for (size_t i = 0; i < len; i += 16)
|
||||||
|
{
|
||||||
int pos = snprintf(line, sizeof(line), "%04zx: ", i);
|
int pos = snprintf(line, sizeof(line), "%04zx: ", i);
|
||||||
if (pos < 0) pos = 0;
|
if (pos < 0) pos = 0;
|
||||||
if ((size_t) pos>= sizeof(line)) pos = sizeof(line) -1;
|
if ((size_t)pos >= sizeof(line)) pos = sizeof(line) - 1;
|
||||||
|
|
||||||
for (size_t j = 0; j < 16; j++) {
|
for (size_t j = 0; j < 16; j++)
|
||||||
if (i + j < len) {
|
{
|
||||||
|
if (i + j < len)
|
||||||
|
{
|
||||||
int written = snprintf(line + pos, sizeof(line) - pos, "%02x ", bytes[i + j]);
|
int written = snprintf(line + pos, sizeof(line) - pos, "%02x ", bytes[i + j]);
|
||||||
if (written > 0 && (size_t)(pos + written) < sizeof(line)) {
|
if (written > 0 && (size_t)(pos + written) < sizeof(line))
|
||||||
|
{
|
||||||
pos += written;
|
pos += written;
|
||||||
} else {
|
}
|
||||||
pos = sizeof(line) -1;
|
else
|
||||||
|
{
|
||||||
|
pos = sizeof(line) - 1;
|
||||||
}
|
}
|
||||||
ascii[j] = isprint(bytes[i + j]) ? bytes[i + j] : '.';
|
ascii[j] = isprint(bytes[i + j]) ? bytes[i + j] : '.';
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
int written = snprintf(line + pos, sizeof(line) - pos, " ");
|
int written = snprintf(line + pos, sizeof(line) - pos, " ");
|
||||||
if (written > 0 && (size_t)(pos + written)) {
|
if (written > 0 && (size_t)(pos + written))
|
||||||
|
{
|
||||||
pos += written;
|
pos += written;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ascii[j] = ' ';
|
ascii[j] = ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
#define LOGGING_H
|
#define LOGGING_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
// Log levels
|
// Log levels
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
LOG_LEVEL_OFF = 0, // No logging
|
LOG_LEVEL_OFF = 0, // No logging
|
||||||
LOG_LEVEL_ERROR = 1, // Only errors
|
LOG_LEVEL_ERROR = 1, // Only errors
|
||||||
LOG_LEVEL_WARN = 2, // Errors + warnings
|
LOG_LEVEL_WARN = 2, // Errors + warnings
|
||||||
@@ -17,7 +16,8 @@ typedef enum {
|
|||||||
} LogLevel;
|
} LogLevel;
|
||||||
|
|
||||||
// Log categories for filtering
|
// Log categories for filtering
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
LOG_CAT_GENERAL = 0x01,
|
LOG_CAT_GENERAL = 0x01,
|
||||||
LOG_CAT_SECURITY = 0x02,
|
LOG_CAT_SECURITY = 0x02,
|
||||||
LOG_CAT_NETWORK = 0x04,
|
LOG_CAT_NETWORK = 0x04,
|
||||||
@@ -30,14 +30,16 @@ typedef enum {
|
|||||||
} LogCategory;
|
} LogCategory;
|
||||||
|
|
||||||
// Log output formats
|
// Log output formats
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
LOG_FORMAT_PLAIN = 0, // Simple text format
|
LOG_FORMAT_PLAIN = 0, // Simple text format
|
||||||
LOG_FORMAT_JSON = 1, // JSON structured format
|
LOG_FORMAT_JSON = 1, // JSON structured format
|
||||||
LOG_FORMAT_SYSLOG = 2 // Syslog compatible format
|
LOG_FORMAT_SYSLOG = 2 // Syslog compatible format
|
||||||
} LogFormat;
|
} LogFormat;
|
||||||
|
|
||||||
// Logger configuration
|
// Logger configuration
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
LogLevel level;
|
LogLevel level;
|
||||||
LogCategory categories;
|
LogCategory categories;
|
||||||
LogFormat format;
|
LogFormat format;
|
||||||
@@ -53,7 +55,7 @@ typedef struct {
|
|||||||
} LogConfig;
|
} LogConfig;
|
||||||
|
|
||||||
// Initialize the logging system
|
// Initialize the logging system
|
||||||
void log_init(LogConfig *config);
|
void log_init(LogConfig* config);
|
||||||
|
|
||||||
// Cleanup logging system
|
// Cleanup logging system
|
||||||
void log_cleanup(void);
|
void log_cleanup(void);
|
||||||
@@ -65,8 +67,8 @@ void log_set_level(LogLevel level);
|
|||||||
void log_set_categories(LogCategory categories);
|
void log_set_categories(LogCategory categories);
|
||||||
|
|
||||||
// Core logging functions
|
// Core logging functions
|
||||||
void log_write(LogLevel level, LogCategory category, const char *file,
|
void log_write(LogLevel level, LogCategory category, const char* file,
|
||||||
int line, const char *func, const char *fmt, ...);
|
int line, const char* func, const char* fmt, ...);
|
||||||
|
|
||||||
// Convenience macros with source location
|
// Convenience macros with source location
|
||||||
#define LOG_ERROR(cat, ...) \
|
#define LOG_ERROR(cat, ...) \
|
||||||
@@ -89,21 +91,21 @@ void log_write(LogLevel level, LogCategory category, const char *file,
|
|||||||
log_write(level, LOG_CAT_SECURITY, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
log_write(level, LOG_CAT_SECURITY, __FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||||
|
|
||||||
// Simple backwards-compatible log function
|
// Simple backwards-compatible log function
|
||||||
void log_event(const char *message);
|
void log_event(const char* message);
|
||||||
|
|
||||||
// Log mode string conversion
|
// Log mode string conversion
|
||||||
const char *log_level_to_string(LogLevel level);
|
const char* log_level_to_string(LogLevel level);
|
||||||
LogLevel log_level_from_string(const char *str);
|
LogLevel log_level_from_string(const char* str);
|
||||||
const char *log_mode_to_string(LogLevel level);
|
const char* log_mode_to_string(LogLevel level);
|
||||||
|
|
||||||
// Secure logging (sanitizes sensitive data)
|
// Secure logging (sanitizes sensitive data)
|
||||||
void log_secure(LogLevel level, LogCategory category, const char *fmt, ...);
|
void log_secure(LogLevel level, LogCategory category, const char* fmt, ...);
|
||||||
|
|
||||||
// Performance logging with timing
|
// Performance logging with timing
|
||||||
void log_perf_start(const char *operation);
|
void log_perf_start(const char* operation);
|
||||||
void log_perf_end(const char *operation);
|
void log_perf_end(const char* operation);
|
||||||
|
|
||||||
// Hex dump for debugging binary data (only in TRACE level)
|
// Hex dump for debugging binary data (only in TRACE level)
|
||||||
void log_hexdump(const char *label, const void *data, size_t len);
|
void log_hexdump(const char* label, const void* data, size_t len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define MAX_MMAP_CACHE_SIZE 100
|
#define MAX_MMAP_CACHE_SIZE 100
|
||||||
#define MAX_MMAP_FILE_SIZE (10 * 1024 * 1024) // 10MB
|
#define MAX_MMAP_FILE_SIZE (10 * 1024 * 1024) // 10MB
|
||||||
@@ -13,25 +12,26 @@
|
|||||||
#define DEFAULT_BUFFER_SIZE 32768
|
#define DEFAULT_BUFFER_SIZE 32768
|
||||||
|
|
||||||
// Global cache structures
|
// Global cache structures
|
||||||
static mmap_cache_entry_t *mmap_cache = NULL;
|
static mmap_cache_entry_t* mmap_cache = NULL;
|
||||||
static int mmap_cache_size = 0;
|
static int mmap_cache_size = 0;
|
||||||
static pthread_mutex_t mmap_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t mmap_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
static buffer_pool_t *buffer_pool = NULL;
|
static buffer_pool_t* buffer_pool = NULL;
|
||||||
static pthread_mutex_t buffer_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t buffer_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
// Pre-allocated response headers
|
// Pre-allocated response headers
|
||||||
const char *response_200_header = "HTTP/1.1 200 OK\r\n";
|
const char* response_200_header = "HTTP/1.1 200 OK\r\n";
|
||||||
const char *response_404_header = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found";
|
const char* response_404_header = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found";
|
||||||
const char *response_403_header = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied";
|
const char* response_403_header = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied";
|
||||||
const char *response_429_header = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded";
|
const char* response_429_header = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded";
|
||||||
const char *response_500_header = "HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error";
|
const char* response_500_header = "HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error";
|
||||||
const char *response_503_header = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded";
|
const char* response_503_header = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded";
|
||||||
|
|
||||||
// Common MIME types cache
|
// Common MIME types cache
|
||||||
typedef struct {
|
typedef struct
|
||||||
const char *ext;
|
{
|
||||||
const char *mime;
|
const char* ext;
|
||||||
|
const char* mime;
|
||||||
} mime_cache_t;
|
} mime_cache_t;
|
||||||
|
|
||||||
static const mime_cache_t mime_cache[] = {
|
static const mime_cache_t mime_cache[] = {
|
||||||
@@ -55,9 +55,12 @@ static const mime_cache_t mime_cache[] = {
|
|||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *get_mime_from_cache(const char *ext) {
|
const char* get_mime_from_cache(const char* ext)
|
||||||
for (int i = 0; mime_cache[i].ext != NULL; i++) {
|
{
|
||||||
if (strcasecmp(ext, mime_cache[i].ext) == 0) {
|
for (int i = 0; mime_cache[i].ext != NULL; i++)
|
||||||
|
{
|
||||||
|
if (strcasecmp(ext, mime_cache[i].ext) == 0)
|
||||||
|
{
|
||||||
return mime_cache[i].mime;
|
return mime_cache[i].mime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +68,7 @@ const char *get_mime_from_cache(const char *ext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Task queue implementation
|
// Task queue implementation
|
||||||
void init_task_queue(task_queue_t *queue)
|
void init_task_queue(task_queue_t* queue)
|
||||||
{
|
{
|
||||||
queue->head = NULL;
|
queue->head = NULL;
|
||||||
queue->tail = NULL;
|
queue->tail = NULL;
|
||||||
@@ -74,14 +77,14 @@ void init_task_queue(task_queue_t *queue)
|
|||||||
pthread_cond_init(&queue->cond, NULL);
|
pthread_cond_init(&queue->cond, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void enqueue_task(task_queue_t *queue, int socket_fd, SSL *ssl, bool is_https)
|
void enqueue_task(task_queue_t* queue, int socket_fd, SSL* ssl, bool is_https)
|
||||||
{
|
{
|
||||||
if (queue->count >= WORKER_QUEUE_SIZE - 1)
|
if (queue->count >= WORKER_QUEUE_SIZE - 1)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
connection_task_t *task = malloc(sizeof(connection_task_t));
|
connection_task_t* task = malloc(sizeof(connection_task_t));
|
||||||
if (!task)
|
if (!task)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -107,7 +110,7 @@ void enqueue_task(task_queue_t *queue, int socket_fd, SSL *ssl, bool is_https)
|
|||||||
pthread_mutex_unlock(&queue->mutex);
|
pthread_mutex_unlock(&queue->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
connection_task_t *dequeue_task(task_queue_t *queue)
|
connection_task_t* dequeue_task(task_queue_t* queue)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&queue->mutex);
|
pthread_mutex_lock(&queue->mutex);
|
||||||
|
|
||||||
@@ -116,7 +119,7 @@ connection_task_t *dequeue_task(task_queue_t *queue)
|
|||||||
pthread_cond_wait(&queue->cond, &queue->mutex);
|
pthread_cond_wait(&queue->cond, &queue->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
connection_task_t *task = queue->head;
|
connection_task_t* task = queue->head;
|
||||||
queue->head = task->next;
|
queue->head = task->next;
|
||||||
|
|
||||||
if (queue->head == NULL)
|
if (queue->head == NULL)
|
||||||
@@ -129,14 +132,14 @@ connection_task_t *dequeue_task(task_queue_t *queue)
|
|||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy_task_queue(task_queue_t *queue)
|
void destroy_task_queue(task_queue_t* queue)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&queue->mutex);
|
pthread_mutex_lock(&queue->mutex);
|
||||||
|
|
||||||
connection_task_t *current = queue->head;
|
connection_task_t* current = queue->head;
|
||||||
while (current)
|
while (current)
|
||||||
{
|
{
|
||||||
connection_task_t *next = current->next;
|
connection_task_t* next = current->next;
|
||||||
free(current);
|
free(current);
|
||||||
current = next;
|
current = next;
|
||||||
}
|
}
|
||||||
@@ -152,7 +155,7 @@ void init_mmap_cache(void)
|
|||||||
mmap_cache = calloc(MAX_MMAP_CACHE_SIZE, sizeof(mmap_cache_entry_t));
|
mmap_cache = calloc(MAX_MMAP_CACHE_SIZE, sizeof(mmap_cache_entry_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
mmap_cache_entry_t *get_cached_file(const char *path)
|
mmap_cache_entry_t* get_cached_file(const char* path)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&mmap_cache_mutex);
|
pthread_mutex_lock(&mmap_cache_mutex);
|
||||||
|
|
||||||
@@ -177,7 +180,7 @@ mmap_cache_entry_t *get_cached_file(const char *path)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cache_file_mmap(const char *path, size_t size, const char *mime_type)
|
void cache_file_mmap(const char* path, size_t size, const char* mime_type)
|
||||||
{
|
{
|
||||||
if (size > MAX_MMAP_FILE_SIZE)
|
if (size > MAX_MMAP_FILE_SIZE)
|
||||||
return;
|
return;
|
||||||
@@ -239,7 +242,7 @@ void cache_file_mmap(const char *path, size_t size, const char *mime_type)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
void* mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (mapped == MAP_FAILED)
|
if (mapped == MAP_FAILED)
|
||||||
@@ -263,7 +266,7 @@ void cache_file_mmap(const char *path, size_t size, const char *mime_type)
|
|||||||
pthread_mutex_unlock(&mmap_cache_mutex);
|
pthread_mutex_unlock(&mmap_cache_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void release_cached_file(mmap_cache_entry_t *entry)
|
void release_cached_file(mmap_cache_entry_t* entry)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&mmap_cache_mutex);
|
pthread_mutex_lock(&mmap_cache_mutex);
|
||||||
entry->ref_count--;
|
entry->ref_count--;
|
||||||
@@ -298,7 +301,7 @@ void init_buffer_pool(void)
|
|||||||
|
|
||||||
for (int i = 0; i < BUFFER_POOL_SIZE; i++)
|
for (int i = 0; i < BUFFER_POOL_SIZE; i++)
|
||||||
{
|
{
|
||||||
buffer_pool_t *buf = malloc(sizeof(buffer_pool_t));
|
buffer_pool_t* buf = malloc(sizeof(buffer_pool_t));
|
||||||
if (buf)
|
if (buf)
|
||||||
{
|
{
|
||||||
buf->buffer = malloc(DEFAULT_BUFFER_SIZE);
|
buf->buffer = malloc(DEFAULT_BUFFER_SIZE);
|
||||||
@@ -312,7 +315,7 @@ void init_buffer_pool(void)
|
|||||||
pthread_mutex_unlock(&buffer_pool_mutex);
|
pthread_mutex_unlock(&buffer_pool_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *get_buffer_from_pool(size_t min_size)
|
char* get_buffer_from_pool(size_t min_size)
|
||||||
{
|
{
|
||||||
if (min_size > DEFAULT_BUFFER_SIZE * 4)
|
if (min_size > DEFAULT_BUFFER_SIZE * 4)
|
||||||
{
|
{
|
||||||
@@ -322,8 +325,8 @@ char *get_buffer_from_pool(size_t min_size)
|
|||||||
|
|
||||||
pthread_mutex_lock(&buffer_pool_mutex);
|
pthread_mutex_lock(&buffer_pool_mutex);
|
||||||
|
|
||||||
buffer_pool_t *current = buffer_pool;
|
buffer_pool_t* current = buffer_pool;
|
||||||
buffer_pool_t *best_fit = NULL;
|
buffer_pool_t* best_fit = NULL;
|
||||||
|
|
||||||
// Find best fit buffer (smallest that fits)
|
// Find best fit buffer (smallest that fits)
|
||||||
while (current)
|
while (current)
|
||||||
@@ -350,11 +353,11 @@ char *get_buffer_from_pool(size_t min_size)
|
|||||||
return malloc(min_size);
|
return malloc(min_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void return_buffer_to_pool(char *buffer)
|
void return_buffer_to_pool(char* buffer)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&buffer_pool_mutex);
|
pthread_mutex_lock(&buffer_pool_mutex);
|
||||||
|
|
||||||
buffer_pool_t *current = buffer_pool;
|
buffer_pool_t* current = buffer_pool;
|
||||||
while (current)
|
while (current)
|
||||||
{
|
{
|
||||||
if (current->buffer == buffer)
|
if (current->buffer == buffer)
|
||||||
@@ -376,10 +379,10 @@ void cleanup_buffer_pool(void)
|
|||||||
{
|
{
|
||||||
pthread_mutex_lock(&buffer_pool_mutex);
|
pthread_mutex_lock(&buffer_pool_mutex);
|
||||||
|
|
||||||
buffer_pool_t *current = buffer_pool;
|
buffer_pool_t* current = buffer_pool;
|
||||||
while (current)
|
while (current)
|
||||||
{
|
{
|
||||||
buffer_pool_t *next = current->next;
|
buffer_pool_t* next = current->next;
|
||||||
free(current->buffer);
|
free(current->buffer);
|
||||||
free(current);
|
free(current);
|
||||||
current = next;
|
current = next;
|
||||||
|
|||||||
@@ -14,15 +14,15 @@
|
|||||||
typedef struct connection_task_t
|
typedef struct connection_task_t
|
||||||
{
|
{
|
||||||
int socket_fd;
|
int socket_fd;
|
||||||
SSL *ssl;
|
SSL* ssl;
|
||||||
bool is_https;
|
bool is_https;
|
||||||
struct connection_task_t *next;
|
struct connection_task_t* next;
|
||||||
} connection_task_t;
|
} connection_task_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
connection_task_t *head;
|
connection_task_t* head;
|
||||||
connection_task_t *tail;
|
connection_task_t* tail;
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
int count;
|
int count;
|
||||||
@@ -31,51 +31,51 @@ typedef struct
|
|||||||
// Memory-mapped file cache
|
// Memory-mapped file cache
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char *path;
|
char* path;
|
||||||
void *mmap_data;
|
void* mmap_data;
|
||||||
size_t size;
|
size_t size;
|
||||||
void *compressed_data;
|
void* compressed_data;
|
||||||
size_t compressed_size;
|
size_t compressed_size;
|
||||||
time_t last_access;
|
time_t last_access;
|
||||||
char *mime_type;
|
char* mime_type;
|
||||||
int ref_count;
|
int ref_count;
|
||||||
} mmap_cache_entry_t;
|
} mmap_cache_entry_t;
|
||||||
|
|
||||||
// Response buffer pool
|
// Response buffer pool
|
||||||
typedef struct buffer_pool_t
|
typedef struct buffer_pool_t
|
||||||
{
|
{
|
||||||
char *buffer;
|
char* buffer;
|
||||||
size_t size;
|
size_t size;
|
||||||
bool in_use;
|
bool in_use;
|
||||||
struct buffer_pool_t *next;
|
struct buffer_pool_t* next;
|
||||||
} buffer_pool_t;
|
} buffer_pool_t;
|
||||||
|
|
||||||
// Function declarations
|
// Function declarations
|
||||||
void init_task_queue(task_queue_t *queue);
|
void init_task_queue(task_queue_t* queue);
|
||||||
void enqueue_task(task_queue_t *queue, int socket_fd, SSL *ssl, bool is_https);
|
void enqueue_task(task_queue_t* queue, int socket_fd, SSL* ssl, bool is_https);
|
||||||
connection_task_t *dequeue_task(task_queue_t *queue);
|
connection_task_t* dequeue_task(task_queue_t* queue);
|
||||||
void destroy_task_queue(task_queue_t *queue);
|
void destroy_task_queue(task_queue_t* queue);
|
||||||
|
|
||||||
void init_mmap_cache(void);
|
void init_mmap_cache(void);
|
||||||
mmap_cache_entry_t *get_cached_file(const char *path);
|
mmap_cache_entry_t* get_cached_file(const char* path);
|
||||||
void cache_file_mmap(const char *path, size_t size, const char *mime_type);
|
void cache_file_mmap(const char* path, size_t size, const char* mime_type);
|
||||||
void release_cached_file(mmap_cache_entry_t *entry);
|
void release_cached_file(mmap_cache_entry_t* entry);
|
||||||
void cleanup_mmap_cache(void);
|
void cleanup_mmap_cache(void);
|
||||||
|
|
||||||
void init_buffer_pool(void);
|
void init_buffer_pool(void);
|
||||||
char *get_buffer_from_pool(size_t min_size);
|
char* get_buffer_from_pool(size_t min_size);
|
||||||
void return_buffer_to_pool(char *buffer);
|
void return_buffer_to_pool(char* buffer);
|
||||||
void cleanup_buffer_pool(void);
|
void cleanup_buffer_pool(void);
|
||||||
|
|
||||||
// Pre-allocated response headers
|
// Pre-allocated response headers
|
||||||
extern const char *response_200_header;
|
extern const char* response_200_header;
|
||||||
extern const char *response_404_header;
|
extern const char* response_404_header;
|
||||||
extern const char *response_403_header;
|
extern const char* response_403_header;
|
||||||
extern const char *response_429_header;
|
extern const char* response_429_header;
|
||||||
extern const char *response_500_header;
|
extern const char* response_500_header;
|
||||||
extern const char *response_503_header;
|
extern const char* response_503_header;
|
||||||
|
|
||||||
// MIME type cache
|
// MIME type cache
|
||||||
const char *get_mime_from_cache(const char *ext);
|
const char* get_mime_from_cache(const char* ext);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
293
src/server.c
293
src/server.c
@@ -11,7 +11,6 @@
|
|||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <libgen.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
@@ -19,7 +18,6 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
@@ -89,14 +87,14 @@ typedef struct
|
|||||||
int cpu_core;
|
int cpu_core;
|
||||||
} ThreadInfo;
|
} ThreadInfo;
|
||||||
|
|
||||||
ThreadInfo *thread_pool;
|
ThreadInfo* thread_pool;
|
||||||
int thread_pool_size = 0;
|
int thread_pool_size = 0;
|
||||||
pthread_mutex_t thread_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t thread_pool_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
pthread_cond_t thread_pool_cond = PTHREAD_COND_INITIALIZER;
|
pthread_cond_t thread_pool_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
|
||||||
// Worker thread queue
|
// Worker thread queue
|
||||||
task_queue_t worker_queue;
|
task_queue_t worker_queue;
|
||||||
pthread_t *worker_threads = NULL;
|
pthread_t* worker_threads = NULL;
|
||||||
int num_worker_threads = 0;
|
int num_worker_threads = 0;
|
||||||
volatile int workers_running = 1;
|
volatile int workers_running = 1;
|
||||||
|
|
||||||
@@ -109,55 +107,55 @@ typedef struct
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char *path;
|
char* path;
|
||||||
char *data;
|
char* data;
|
||||||
size_t size;
|
size_t size;
|
||||||
time_t last_access;
|
time_t last_access;
|
||||||
char *mime_type;
|
char* mime_type;
|
||||||
} CacheEntry;
|
} CacheEntry;
|
||||||
|
|
||||||
CacheEntry *file_cache = NULL;
|
CacheEntry* file_cache = NULL;
|
||||||
int cache_size = 0;
|
int cache_size = 0;
|
||||||
pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
ServerConfig config;
|
ServerConfig config;
|
||||||
char server_log[MAX_LOG_SIZE];
|
char server_log[MAX_LOG_SIZE];
|
||||||
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
pthread_t *client_threads = NULL;
|
pthread_t* client_threads = NULL;
|
||||||
int num_client_threads = 0;
|
int num_client_threads = 0;
|
||||||
pthread_mutex_t thread_count_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t thread_count_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
SSL_CTX *ssl_ctx = NULL;
|
SSL_CTX* ssl_ctx = NULL;
|
||||||
volatile sig_atomic_t server_running = 1;
|
volatile sig_atomic_t server_running = 1;
|
||||||
int http_socket = -1;
|
int http_socket = -1;
|
||||||
int https_socket = -1;
|
int https_socket = -1;
|
||||||
int epoll_fd;
|
int epoll_fd;
|
||||||
|
|
||||||
RateLimit *rate_limits = NULL;
|
RateLimit* rate_limits = NULL;
|
||||||
int rate_limit_count = 0;
|
int rate_limit_count = 0;
|
||||||
pthread_mutex_t rate_limit_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t rate_limit_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
void cleanup_thread_pool(void);
|
void cleanup_thread_pool(void);
|
||||||
void *handle_http_client(void *arg);
|
void* handle_http_client(void* arg);
|
||||||
void *handle_https_client(void *arg);
|
void* handle_https_client(void* arg);
|
||||||
void *worker_thread(void *arg);
|
void* worker_thread(void* arg);
|
||||||
void set_cpu_affinity(int thread_id);
|
void set_cpu_affinity(int thread_id);
|
||||||
void optimize_socket_for_send(int socket_fd);
|
void optimize_socket_for_send(int socket_fd);
|
||||||
void log_event(const char *message);
|
void log_event(const char* message);
|
||||||
void initialize_openssl();
|
void initialize_openssl();
|
||||||
void cleanup_openssl();
|
void cleanup_openssl();
|
||||||
SSL_CTX *create_ssl_context();
|
SSL_CTX* create_ssl_context();
|
||||||
void configure_ssl_context(SSL_CTX *ctx);
|
void configure_ssl_context(SSL_CTX * ctx);
|
||||||
void *start_http_server(void *arg);
|
void* start_http_server(void* arg);
|
||||||
void *start_https_server(void *arg);
|
void* start_https_server(void* arg);
|
||||||
void shutdown_server();
|
void shutdown_server();
|
||||||
int parse_request_line(char *request_buffer, char *method, char *url, char *protocol);
|
int parse_request_line(char* request_buffer, char* method, char* url, char* protocol);
|
||||||
char *get_mime_type(const char *filepath);
|
char* get_mime_type(const char* filepath);
|
||||||
char *sanitize_url(const char *url);
|
char* sanitize_url(const char* url);
|
||||||
int check_rate_limit(const char *ip);
|
int check_rate_limit(const char* ip);
|
||||||
int should_compress(const char *mime_type);
|
int should_compress(const char* mime_type);
|
||||||
unsigned char *gzip_compress(const unsigned char *data, size_t size, size_t *compressed_size);
|
unsigned char* gzip_compress(const unsigned char* data, size_t size, size_t* compressed_size);
|
||||||
char *stristr(const char *haystack, const char *needle);
|
char* stristr(const char* haystack, const char* needle);
|
||||||
char *extract_header_value(const char *request, const char *header_name);
|
char* extract_header_value(const char* request, const char* header_name);
|
||||||
int calculate_dynamic_rate_limit(void);
|
int calculate_dynamic_rate_limit(void);
|
||||||
|
|
||||||
void initialize_openssl()
|
void initialize_openssl()
|
||||||
@@ -183,10 +181,10 @@ void cleanup_openssl()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SSL_CTX *create_ssl_context()
|
SSL_CTX* create_ssl_context()
|
||||||
{
|
{
|
||||||
const SSL_METHOD *method = TLS_server_method();
|
const SSL_METHOD* method = TLS_server_method();
|
||||||
SSL_CTX *ctx = SSL_CTX_new(method);
|
SSL_CTX* ctx = SSL_CTX_new(method);
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
{
|
{
|
||||||
perror(BOLD RED "Unable to create SSL context" RESET);
|
perror(BOLD RED "Unable to create SSL context" RESET);
|
||||||
@@ -195,7 +193,7 @@ SSL_CTX *create_ssl_context()
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void configure_ssl_context(SSL_CTX *ctx)
|
void configure_ssl_context(SSL_CTX* ctx)
|
||||||
{
|
{
|
||||||
if (SSL_CTX_use_certificate_file(ctx, config.ssl_cert_path, SSL_FILETYPE_PEM) <= 0)
|
if (SSL_CTX_use_certificate_file(ctx, config.ssl_cert_path, SSL_FILETYPE_PEM) <= 0)
|
||||||
{
|
{
|
||||||
@@ -235,7 +233,7 @@ void configure_ssl_context(SSL_CTX *ctx)
|
|||||||
|
|
||||||
// Use secure ciphers only - TLS 1.3 and strong TLS 1.2 AEAD ciphers
|
// Use secure ciphers only - TLS 1.3 and strong TLS 1.2 AEAD ciphers
|
||||||
// Prioritize ChaCha20 for mobile devices, AES-GCM for servers with AES-NI
|
// Prioritize ChaCha20 for mobile devices, AES-GCM for servers with AES-NI
|
||||||
const char *cipher_list =
|
const char* cipher_list =
|
||||||
"TLS_AES_256_GCM_SHA384:"
|
"TLS_AES_256_GCM_SHA384:"
|
||||||
"TLS_CHACHA20_POLY1305_SHA256:"
|
"TLS_CHACHA20_POLY1305_SHA256:"
|
||||||
"TLS_AES_128_GCM_SHA256:" // TLS 1.3 ciphers
|
"TLS_AES_128_GCM_SHA256:" // TLS 1.3 ciphers
|
||||||
@@ -335,7 +333,7 @@ void set_socket_options(int socket_fd)
|
|||||||
setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf));
|
setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
void *start_http_server(void *arg)
|
void* start_http_server(void* arg)
|
||||||
{
|
{
|
||||||
(void)arg;
|
(void)arg;
|
||||||
http_socket = socket(AF_INET, SOCK_STREAM, 0);
|
http_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
@@ -352,7 +350,7 @@ void *start_http_server(void *arg)
|
|||||||
http_address.sin_addr.s_addr = INADDR_ANY;
|
http_address.sin_addr.s_addr = INADDR_ANY;
|
||||||
http_address.sin_port = htons(config.port);
|
http_address.sin_port = htons(config.port);
|
||||||
|
|
||||||
if (bind(http_socket, (struct sockaddr *)&http_address, sizeof(http_address)) < 0)
|
if (bind(http_socket, (struct sockaddr*)&http_address, sizeof(http_address)) < 0)
|
||||||
{
|
{
|
||||||
perror(BOLD RED "Error binding HTTP socket" RESET);
|
perror(BOLD RED "Error binding HTTP socket" RESET);
|
||||||
close(http_socket);
|
close(http_socket);
|
||||||
@@ -394,7 +392,8 @@ void *start_http_server(void *arg)
|
|||||||
if (nfds == -1)
|
if (nfds == -1)
|
||||||
{
|
{
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
{ // Ignore interrupts for shutdown
|
{
|
||||||
|
// Ignore interrupts for shutdown
|
||||||
perror("epoll_wait");
|
perror("epoll_wait");
|
||||||
break; // Exit loop on error
|
break; // Exit loop on error
|
||||||
}
|
}
|
||||||
@@ -408,7 +407,7 @@ void *start_http_server(void *arg)
|
|||||||
// New connection
|
// New connection
|
||||||
struct sockaddr_in client_addr;
|
struct sockaddr_in client_addr;
|
||||||
socklen_t addr_size = sizeof(client_addr);
|
socklen_t addr_size = sizeof(client_addr);
|
||||||
int client_socket = accept(http_socket, (struct sockaddr *)&client_addr, &addr_size);
|
int client_socket = accept(http_socket, (struct sockaddr*)&client_addr, &addr_size);
|
||||||
if (client_socket < 0)
|
if (client_socket < 0)
|
||||||
{
|
{
|
||||||
perror("accept");
|
perror("accept");
|
||||||
@@ -423,7 +422,7 @@ void *start_http_server(void *arg)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
log_event("Worker queue full, rejecting connection.");
|
log_event("Worker queue full, rejecting connection.");
|
||||||
const char *overload_response = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded";
|
const char* overload_response = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded";
|
||||||
send(client_socket, overload_response, strlen(overload_response), 0);
|
send(client_socket, overload_response, strlen(overload_response), 0);
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
}
|
}
|
||||||
@@ -438,7 +437,7 @@ void *start_http_server(void *arg)
|
|||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *start_https_server(void *arg)
|
void* start_https_server(void* arg)
|
||||||
{
|
{
|
||||||
(void)arg;
|
(void)arg;
|
||||||
https_socket = socket(AF_INET, SOCK_STREAM, 0);
|
https_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
@@ -456,7 +455,7 @@ void *start_https_server(void *arg)
|
|||||||
https_address.sin_addr.s_addr = INADDR_ANY;
|
https_address.sin_addr.s_addr = INADDR_ANY;
|
||||||
https_address.sin_port = htons(443);
|
https_address.sin_port = htons(443);
|
||||||
|
|
||||||
if (bind(https_socket, (struct sockaddr *)&https_address, sizeof(https_address)) < 0)
|
if (bind(https_socket, (struct sockaddr*)&https_address, sizeof(https_address)) < 0)
|
||||||
{
|
{
|
||||||
perror(BOLD RED "Error binding HTTPS socket" RESET);
|
perror(BOLD RED "Error binding HTTPS socket" RESET);
|
||||||
close(https_socket);
|
close(https_socket);
|
||||||
@@ -494,7 +493,7 @@ void *start_https_server(void *arg)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
log_event("Worker queue full (HTTPS), rejecting connection.");
|
log_event("Worker queue full (HTTPS), rejecting connection.");
|
||||||
const char *overload_response = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded";
|
const char* overload_response = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded";
|
||||||
send(client_socket, overload_response, strlen(overload_response), 0);
|
send(client_socket, overload_response, strlen(overload_response), 0);
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
}
|
}
|
||||||
@@ -505,14 +504,14 @@ void *start_https_server(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if request is a WebSocket upgrade request
|
// Check if request is a WebSocket upgrade request
|
||||||
static int is_websocket_upgrade(const char *request)
|
static int is_websocket_upgrade(const char* request)
|
||||||
{
|
{
|
||||||
// Make a lowercase copy for case-insensitive comparison
|
// Make a lowercase copy for case-insensitive comparison
|
||||||
char *request_lower = strdup(request);
|
char* request_lower = strdup(request);
|
||||||
if (!request_lower)
|
if (!request_lower)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (char *p = request_lower; *p; p++)
|
for (char* p = request_lower; *p; p++)
|
||||||
{
|
{
|
||||||
*p = tolower((unsigned char)*p);
|
*p = tolower((unsigned char)*p);
|
||||||
}
|
}
|
||||||
@@ -527,14 +526,10 @@ static int is_websocket_upgrade(const char *request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle WebSocket connection
|
// Handle WebSocket connection
|
||||||
static void *handle_websocket(void *arg)
|
static void* handle_websocket(void* arg)
|
||||||
{
|
{
|
||||||
ws_connection_t *conn = (ws_connection_t *)arg;
|
ws_connection_t* conn = (ws_connection_t*)arg;
|
||||||
|
|
||||||
if (!conn)
|
|
||||||
{
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove socket timeout for WebSocket (connections should stay open)
|
// Remove socket timeout for WebSocket (connections should stay open)
|
||||||
struct timeval ws_timeout;
|
struct timeval ws_timeout;
|
||||||
@@ -570,7 +565,7 @@ static void *handle_websocket(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ws_frame_header_t header;
|
ws_frame_header_t header;
|
||||||
uint8_t *payload = NULL;
|
uint8_t* payload = NULL;
|
||||||
int parsed = ws_parse_frame(buffer, bytes_received, &header, &payload);
|
int parsed = ws_parse_frame(buffer, bytes_received, &header, &payload);
|
||||||
|
|
||||||
if (parsed < 0)
|
if (parsed < 0)
|
||||||
@@ -627,9 +622,9 @@ static void *handle_websocket(void *arg)
|
|||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *handle_http_client(void *arg)
|
void* handle_http_client(void* arg)
|
||||||
{
|
{
|
||||||
int client_socket = *((int *)arg);
|
int client_socket = *((int*)arg);
|
||||||
free(arg);
|
free(arg);
|
||||||
|
|
||||||
if (!server_running)
|
if (!server_running)
|
||||||
@@ -663,7 +658,9 @@ void *handle_http_client(void *arg)
|
|||||||
|
|
||||||
// Check if client accepts gzip BEFORE parsing (parse modifies buffer!)
|
// Check if client accepts gzip BEFORE parsing (parse modifies buffer!)
|
||||||
int accepts_gzip = (stristr(request_buffer, "accept-encoding:") &&
|
int accepts_gzip = (stristr(request_buffer, "accept-encoding:") &&
|
||||||
stristr(request_buffer, "gzip")) ? 1 : 0;
|
stristr(request_buffer, "gzip"))
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
|
||||||
// Check for WebSocket upgrade request
|
// Check for WebSocket upgrade request
|
||||||
if (config.enable_websocket && is_websocket_upgrade(request_buffer))
|
if (config.enable_websocket && is_websocket_upgrade(request_buffer))
|
||||||
@@ -676,7 +673,7 @@ void *handle_http_client(void *arg)
|
|||||||
send(client_socket, response, strlen(response), 0);
|
send(client_socket, response, strlen(response), 0);
|
||||||
|
|
||||||
// Create WebSocket connection context
|
// Create WebSocket connection context
|
||||||
ws_connection_t *ws_conn = malloc(sizeof(ws_connection_t));
|
ws_connection_t* ws_conn = malloc(sizeof(ws_connection_t));
|
||||||
if (ws_conn)
|
if (ws_conn)
|
||||||
{
|
{
|
||||||
ws_conn->socket_fd = client_socket;
|
ws_conn->socket_fd = client_socket;
|
||||||
@@ -704,21 +701,22 @@ void *handle_http_client(void *arg)
|
|||||||
if (parse_request_line(request_buffer, method, url, protocol) != 0)
|
if (parse_request_line(request_buffer, method, url, protocol) != 0)
|
||||||
{
|
{
|
||||||
log_event("Invalid request line.");
|
log_event("Invalid request line.");
|
||||||
const char *bad_request_response = "HTTP/1.1 400 Bad Request\r\n\r\nInvalid Request";
|
const char* bad_request_response = "HTTP/1.1 400 Bad Request\r\n\r\nInvalid Request";
|
||||||
send(client_socket, bad_request_response, strlen(bad_request_response), 0);
|
send(client_socket, bad_request_response, strlen(bad_request_response), 0);
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.use_https)
|
if (config.use_https)
|
||||||
{ // Check if HTTPS is enabled
|
{
|
||||||
|
// Check if HTTPS is enabled
|
||||||
size_t needed = snprintf(NULL, 0,
|
size_t needed = snprintf(NULL, 0,
|
||||||
"HTTP/1.1 301 Moved Permanently\r\n"
|
"HTTP/1.1 301 Moved Permanently\r\n"
|
||||||
"Location: https://%s%s\r\n\r\n",
|
"Location: https://%s%s\r\n\r\n",
|
||||||
config.server_name, url) +
|
config.server_name, url) +
|
||||||
1;
|
1;
|
||||||
|
|
||||||
char *redirect_response = malloc(needed);
|
char* redirect_response = malloc(needed);
|
||||||
if (redirect_response)
|
if (redirect_response)
|
||||||
{
|
{
|
||||||
snprintf(redirect_response, needed,
|
snprintf(redirect_response, needed,
|
||||||
@@ -733,11 +731,11 @@ void *handle_http_client(void *arg)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *sanitized_url = sanitize_url(url);
|
char* sanitized_url = sanitize_url(url);
|
||||||
if (!sanitized_url)
|
if (!sanitized_url)
|
||||||
{
|
{
|
||||||
log_event("Blocked malicious URL");
|
log_event("Blocked malicious URL");
|
||||||
const char *forbidden_response = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied";
|
const char* forbidden_response = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied";
|
||||||
send(client_socket, forbidden_response, strlen(forbidden_response), 0);
|
send(client_socket, forbidden_response, strlen(forbidden_response), 0);
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
@@ -746,14 +744,14 @@ void *handle_http_client(void *arg)
|
|||||||
char client_ip[INET_ADDRSTRLEN];
|
char client_ip[INET_ADDRSTRLEN];
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
socklen_t addr_len = sizeof(addr);
|
socklen_t addr_len = sizeof(addr);
|
||||||
getpeername(client_socket, (struct sockaddr *)&addr, &addr_len);
|
getpeername(client_socket, (struct sockaddr*)&addr, &addr_len);
|
||||||
inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip));
|
inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip));
|
||||||
|
|
||||||
if (!check_rate_limit(client_ip))
|
if (!check_rate_limit(client_ip))
|
||||||
{
|
{
|
||||||
log_event("Rate limit exceeded for IP:");
|
log_event("Rate limit exceeded for IP:");
|
||||||
log_event(client_ip);
|
log_event(client_ip);
|
||||||
const char *rate_limit_response = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded";
|
const char* rate_limit_response = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded";
|
||||||
send(client_socket, rate_limit_response, strlen(rate_limit_response), 0);
|
send(client_socket, rate_limit_response, strlen(rate_limit_response), 0);
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -767,21 +765,21 @@ void *handle_http_client(void *arg)
|
|||||||
if (written < 0 || written >= (int)sizeof(filepath))
|
if (written < 0 || written >= (int)sizeof(filepath))
|
||||||
{
|
{
|
||||||
log_event("Path too long, potential buffer overflow attempt");
|
log_event("Path too long, potential buffer overflow attempt");
|
||||||
const char *error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n";
|
const char* error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n";
|
||||||
send(client_socket, error_response, strlen(error_response), 0);
|
send(client_socket, error_response, strlen(error_response), 0);
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get MIME type
|
// Get MIME type
|
||||||
char *mime_type = get_mime_type(filepath);
|
char* mime_type = get_mime_type(filepath);
|
||||||
|
|
||||||
// Check for conditional request headers
|
// Check for conditional request headers
|
||||||
char *if_none_match = extract_header_value(request_buffer, "If-None-Match");
|
char* if_none_match = extract_header_value(request_buffer, "If-None-Match");
|
||||||
char *if_modified_since = extract_header_value(request_buffer, "If-Modified-Since");
|
char* if_modified_since = extract_header_value(request_buffer, "If-Modified-Since");
|
||||||
|
|
||||||
// Try cache first
|
// Try cache first
|
||||||
mmap_cache_entry_t *cached = get_cached_file(filepath);
|
mmap_cache_entry_t* cached = get_cached_file(filepath);
|
||||||
|
|
||||||
if (cached)
|
if (cached)
|
||||||
{
|
{
|
||||||
@@ -794,10 +792,10 @@ void *handle_http_client(void *arg)
|
|||||||
if (if_none_match)
|
if (if_none_match)
|
||||||
{
|
{
|
||||||
// Strip quotes if present in the header value
|
// Strip quotes if present in the header value
|
||||||
char *etag_value = if_none_match;
|
char* etag_value = if_none_match;
|
||||||
if (*etag_value == '"')
|
if (*etag_value == '"')
|
||||||
etag_value++;
|
etag_value++;
|
||||||
char *end_quote = strchr(etag_value, '"');
|
char* end_quote = strchr(etag_value, '"');
|
||||||
if (end_quote)
|
if (end_quote)
|
||||||
*end_quote = '\0';
|
*end_quote = '\0';
|
||||||
|
|
||||||
@@ -833,7 +831,7 @@ void *handle_http_client(void *arg)
|
|||||||
free(if_modified_since);
|
free(if_modified_since);
|
||||||
|
|
||||||
// Check if we should compress
|
// Check if we should compress
|
||||||
unsigned char *compressed_data = NULL;
|
unsigned char* compressed_data = NULL;
|
||||||
size_t compressed_size = 0;
|
size_t compressed_size = 0;
|
||||||
int using_compression = 0;
|
int using_compression = 0;
|
||||||
int needs_free = 0;
|
int needs_free = 0;
|
||||||
@@ -852,7 +850,8 @@ void *handle_http_client(void *arg)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Compress on-the-fly and cache it
|
// Compress on-the-fly and cache it
|
||||||
compressed_data = gzip_compress((unsigned char *)cached->mmap_data, cached->size, &compressed_size);
|
compressed_data = gzip_compress((unsigned char*)cached->mmap_data, cached->size,
|
||||||
|
&compressed_size);
|
||||||
if (compressed_data && compressed_size < cached->size * 0.9)
|
if (compressed_data && compressed_size < cached->size * 0.9)
|
||||||
{
|
{
|
||||||
using_compression = 1;
|
using_compression = 1;
|
||||||
@@ -880,7 +879,7 @@ void *handle_http_client(void *arg)
|
|||||||
|
|
||||||
// Format Last-Modified time (RFC 7231 format)
|
// Format Last-Modified time (RFC 7231 format)
|
||||||
char last_modified[64];
|
char last_modified[64];
|
||||||
struct tm *tm_info = gmtime(&cached->last_access);
|
struct tm* tm_info = gmtime(&cached->last_access);
|
||||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
||||||
|
|
||||||
int header_len = snprintf(response_header, sizeof(response_header),
|
int header_len = snprintf(response_header, sizeof(response_header),
|
||||||
@@ -903,7 +902,7 @@ void *handle_http_client(void *arg)
|
|||||||
using_compression ? "Content-Encoding: gzip\r\n" : "",
|
using_compression ? "Content-Encoding: gzip\r\n" : "",
|
||||||
SECURITY_HEADERS);
|
SECURITY_HEADERS);
|
||||||
|
|
||||||
void *data_to_send = using_compression ? compressed_data : cached->mmap_data;
|
void* data_to_send = using_compression ? compressed_data : cached->mmap_data;
|
||||||
size_t size_to_send = using_compression ? compressed_size : cached->size;
|
size_t size_to_send = using_compression ? compressed_size : cached->size;
|
||||||
|
|
||||||
// Use writev to send header + content in one syscall (for small files)
|
// Use writev to send header + content in one syscall (for small files)
|
||||||
@@ -924,7 +923,7 @@ void *handle_http_client(void *arg)
|
|||||||
size_t total_sent = 0;
|
size_t total_sent = 0;
|
||||||
while (total_sent < size_to_send)
|
while (total_sent < size_to_send)
|
||||||
{
|
{
|
||||||
ssize_t sent = send(client_socket, (char *)data_to_send + total_sent,
|
ssize_t sent = send(client_socket, (char*)data_to_send + total_sent,
|
||||||
size_to_send - total_sent, 0);
|
size_to_send - total_sent, 0);
|
||||||
if (sent <= 0)
|
if (sent <= 0)
|
||||||
break;
|
break;
|
||||||
@@ -943,7 +942,7 @@ void *handle_http_client(void *arg)
|
|||||||
int fd = open(filepath, O_RDONLY);
|
int fd = open(filepath, O_RDONLY);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
{
|
{
|
||||||
const char *not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found";
|
const char* not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found";
|
||||||
send(client_socket, not_found_response, strlen(not_found_response), 0);
|
send(client_socket, not_found_response, strlen(not_found_response), 0);
|
||||||
free(mime_type);
|
free(mime_type);
|
||||||
if (if_none_match)
|
if (if_none_match)
|
||||||
@@ -958,7 +957,7 @@ void *handle_http_client(void *arg)
|
|||||||
if (fstat(fd, &st) == -1)
|
if (fstat(fd, &st) == -1)
|
||||||
{
|
{
|
||||||
log_event("Error getting file size.");
|
log_event("Error getting file size.");
|
||||||
const char *internal_server_error =
|
const char* internal_server_error =
|
||||||
"HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error";
|
"HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error";
|
||||||
send(client_socket, internal_server_error, strlen(internal_server_error), 0);
|
send(client_socket, internal_server_error, strlen(internal_server_error), 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -980,7 +979,7 @@ void *handle_http_client(void *arg)
|
|||||||
// ETag matches - return 304
|
// ETag matches - return 304
|
||||||
char response_304[512];
|
char response_304[512];
|
||||||
char last_modified[64];
|
char last_modified[64];
|
||||||
struct tm *tm_info = gmtime(&st.st_mtime);
|
struct tm* tm_info = gmtime(&st.st_mtime);
|
||||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
||||||
|
|
||||||
int header_len = snprintf(response_304, sizeof(response_304),
|
int header_len = snprintf(response_304, sizeof(response_304),
|
||||||
@@ -1017,7 +1016,7 @@ void *handle_http_client(void *arg)
|
|||||||
|
|
||||||
// Format Last-Modified time
|
// Format Last-Modified time
|
||||||
char last_modified[64];
|
char last_modified[64];
|
||||||
struct tm *tm_info = gmtime(&st.st_mtime);
|
struct tm* tm_info = gmtime(&st.st_mtime);
|
||||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
||||||
|
|
||||||
int header_len = snprintf(response_header, sizeof(response_header),
|
int header_len = snprintf(response_header, sizeof(response_header),
|
||||||
@@ -1061,10 +1060,6 @@ void *handle_http_client(void *arg)
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (bytes_received == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
@@ -1075,12 +1070,12 @@ cleanup:
|
|||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *handle_https_client(void *arg)
|
void* handle_https_client(void* arg)
|
||||||
{
|
{
|
||||||
int client_socket = *((int *)arg);
|
int client_socket = *((int*)arg);
|
||||||
free(arg);
|
free(arg);
|
||||||
|
|
||||||
SSL *ssl = SSL_new(ssl_ctx);
|
SSL* ssl = SSL_new(ssl_ctx);
|
||||||
if (!ssl)
|
if (!ssl)
|
||||||
{
|
{
|
||||||
log_event("SSL_new failed");
|
log_event("SSL_new failed");
|
||||||
@@ -1114,7 +1109,7 @@ void *handle_https_client(void *arg)
|
|||||||
// Check if HTTP/2 was negotiated via ALPN
|
// Check if HTTP/2 was negotiated via ALPN
|
||||||
if (config.enable_http2)
|
if (config.enable_http2)
|
||||||
{
|
{
|
||||||
const unsigned char *alpn_data = NULL;
|
const unsigned char* alpn_data = NULL;
|
||||||
unsigned int alpn_len = 0;
|
unsigned int alpn_len = 0;
|
||||||
|
|
||||||
SSL_get0_alpn_selected(ssl, &alpn_data, &alpn_len);
|
SSL_get0_alpn_selected(ssl, &alpn_data, &alpn_len);
|
||||||
@@ -1195,7 +1190,7 @@ void *handle_https_client(void *arg)
|
|||||||
SSL_write(ssl, response, strlen(response));
|
SSL_write(ssl, response, strlen(response));
|
||||||
|
|
||||||
// Create WebSocket connection context
|
// Create WebSocket connection context
|
||||||
ws_connection_t *ws_conn = malloc(sizeof(ws_connection_t));
|
ws_connection_t* ws_conn = malloc(sizeof(ws_connection_t));
|
||||||
if (ws_conn)
|
if (ws_conn)
|
||||||
{
|
{
|
||||||
ws_conn->socket_fd = client_socket;
|
ws_conn->socket_fd = client_socket;
|
||||||
@@ -1226,7 +1221,7 @@ void *handle_https_client(void *arg)
|
|||||||
if (parse_request_line(buffer, method, url, protocol) != 0)
|
if (parse_request_line(buffer, method, url, protocol) != 0)
|
||||||
{
|
{
|
||||||
log_event("Invalid request line.");
|
log_event("Invalid request line.");
|
||||||
const char *bad_request_response = "HTTP/1.1 400 Bad Request\r\n\r\nInvalid Request";
|
const char* bad_request_response = "HTTP/1.1 400 Bad Request\r\n\r\nInvalid Request";
|
||||||
SSL_write(ssl, bad_request_response, strlen(bad_request_response));
|
SSL_write(ssl, bad_request_response, strlen(bad_request_response));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -1240,15 +1235,11 @@ void *handle_https_client(void *arg)
|
|||||||
log_event(protocol);
|
log_event(protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if client accepts gzip (case-insensitive)
|
char* sanitized_url = sanitize_url(url);
|
||||||
int accepts_gzip = (stristr(buffer, "accept-encoding:") &&
|
|
||||||
stristr(buffer, "gzip")) ? 1 : 0;
|
|
||||||
|
|
||||||
char *sanitized_url = sanitize_url(url);
|
|
||||||
if (!sanitized_url)
|
if (!sanitized_url)
|
||||||
{
|
{
|
||||||
log_event("Blocked malicious URL");
|
log_event("Blocked malicious URL");
|
||||||
const char *forbidden_response = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied";
|
const char* forbidden_response = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied";
|
||||||
SSL_write(ssl, forbidden_response, strlen(forbidden_response));
|
SSL_write(ssl, forbidden_response, strlen(forbidden_response));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -1256,14 +1247,14 @@ void *handle_https_client(void *arg)
|
|||||||
char client_ip[INET_ADDRSTRLEN];
|
char client_ip[INET_ADDRSTRLEN];
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
socklen_t addr_len = sizeof(addr);
|
socklen_t addr_len = sizeof(addr);
|
||||||
getpeername(client_socket, (struct sockaddr *)&addr, &addr_len);
|
getpeername(client_socket, (struct sockaddr*)&addr, &addr_len);
|
||||||
inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip));
|
inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip));
|
||||||
|
|
||||||
if (!check_rate_limit(client_ip))
|
if (!check_rate_limit(client_ip))
|
||||||
{
|
{
|
||||||
log_event("Rate limit exceeded for IP:");
|
log_event("Rate limit exceeded for IP:");
|
||||||
log_event(client_ip);
|
log_event(client_ip);
|
||||||
const char *rate_limit_response = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded";
|
const char* rate_limit_response = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded";
|
||||||
SSL_write(ssl, rate_limit_response, strlen(rate_limit_response));
|
SSL_write(ssl, rate_limit_response, strlen(rate_limit_response));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -1276,7 +1267,7 @@ void *handle_https_client(void *arg)
|
|||||||
if (written < 0 || written >= (int)sizeof(filepath))
|
if (written < 0 || written >= (int)sizeof(filepath))
|
||||||
{
|
{
|
||||||
log_event("Path too long, potential buffer overflow attempt (HTTPS)");
|
log_event("Path too long, potential buffer overflow attempt (HTTPS)");
|
||||||
const char *error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n";
|
const char* error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n";
|
||||||
SSL_write(ssl, error_response, strlen(error_response));
|
SSL_write(ssl, error_response, strlen(error_response));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -1284,14 +1275,14 @@ void *handle_https_client(void *arg)
|
|||||||
log_event(filepath);
|
log_event(filepath);
|
||||||
|
|
||||||
// Get MIME type
|
// Get MIME type
|
||||||
char *mime_type = get_mime_type(filepath);
|
char* mime_type = get_mime_type(filepath);
|
||||||
|
|
||||||
// Check for conditional request headers
|
// Check for conditional request headers
|
||||||
char *if_none_match = extract_header_value(buffer, "If-None-Match");
|
char* if_none_match = extract_header_value(buffer, "If-None-Match");
|
||||||
char *if_modified_since = extract_header_value(buffer, "If-Modified-Since");
|
char* if_modified_since = extract_header_value(buffer, "If-Modified-Since");
|
||||||
|
|
||||||
// Try to get file from cache first
|
// Try to get file from cache first
|
||||||
mmap_cache_entry_t *cached = get_cached_file(filepath);
|
mmap_cache_entry_t* cached = get_cached_file(filepath);
|
||||||
|
|
||||||
if (cached)
|
if (cached)
|
||||||
{
|
{
|
||||||
@@ -1333,12 +1324,12 @@ void *handle_https_client(void *arg)
|
|||||||
free(if_modified_since);
|
free(if_modified_since);
|
||||||
|
|
||||||
// Check if we should compress
|
// Check if we should compress
|
||||||
unsigned char *compressed_data = NULL;
|
unsigned char* compressed_data = NULL;
|
||||||
size_t compressed_size = 0;
|
size_t compressed_size = 0;
|
||||||
int using_compression = 0;
|
int using_compression = 0;
|
||||||
int needs_free = 0;
|
int needs_free = 0;
|
||||||
|
|
||||||
if (accepts_gzip && should_compress(cached->mime_type) && cached->size > 1024)
|
if (should_compress(cached->mime_type) && cached->size > 1024)
|
||||||
{
|
{
|
||||||
// Check if we have cached compressed version
|
// Check if we have cached compressed version
|
||||||
if (cached->compressed_data && cached->compressed_size > 0)
|
if (cached->compressed_data && cached->compressed_size > 0)
|
||||||
@@ -1350,7 +1341,7 @@ void *handle_https_client(void *arg)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
compressed_data = gzip_compress((unsigned char *)cached->mmap_data, cached->size, &compressed_size);
|
compressed_data = gzip_compress((unsigned char*)cached->mmap_data, cached->size, &compressed_size);
|
||||||
if (compressed_data && compressed_size < cached->size * 0.9)
|
if (compressed_data && compressed_size < cached->size * 0.9)
|
||||||
{
|
{
|
||||||
using_compression = 1;
|
using_compression = 1;
|
||||||
@@ -1378,7 +1369,7 @@ void *handle_https_client(void *arg)
|
|||||||
|
|
||||||
// Format Last-Modified time
|
// Format Last-Modified time
|
||||||
char last_modified[64];
|
char last_modified[64];
|
||||||
struct tm *tm_info = gmtime(&cached->last_access);
|
struct tm* tm_info = gmtime(&cached->last_access);
|
||||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
||||||
|
|
||||||
int header_len = snprintf(response_header, sizeof(response_header),
|
int header_len = snprintf(response_header, sizeof(response_header),
|
||||||
@@ -1404,14 +1395,14 @@ void *handle_https_client(void *arg)
|
|||||||
SSL_write(ssl, response_header, header_len);
|
SSL_write(ssl, response_header, header_len);
|
||||||
|
|
||||||
// Send compressed or uncompressed data
|
// Send compressed or uncompressed data
|
||||||
void *data_to_send = using_compression ? compressed_data : cached->mmap_data;
|
void* data_to_send = using_compression ? compressed_data : cached->mmap_data;
|
||||||
size_t size_to_send = using_compression ? compressed_size : cached->size;
|
size_t size_to_send = using_compression ? compressed_size : cached->size;
|
||||||
|
|
||||||
size_t total_sent = 0;
|
size_t total_sent = 0;
|
||||||
while (total_sent < size_to_send)
|
while (total_sent < size_to_send)
|
||||||
{
|
{
|
||||||
int to_send = (size_to_send - total_sent > 65536) ? 65536 : (size_to_send - total_sent);
|
int to_send = (size_to_send - total_sent > 65536) ? 65536 : (size_to_send - total_sent);
|
||||||
int sent = SSL_write(ssl, (char *)data_to_send + total_sent, to_send);
|
int sent = SSL_write(ssl, (char*)data_to_send + total_sent, to_send);
|
||||||
if (sent <= 0)
|
if (sent <= 0)
|
||||||
break;
|
break;
|
||||||
total_sent += sent;
|
total_sent += sent;
|
||||||
@@ -1430,7 +1421,7 @@ void *handle_https_client(void *arg)
|
|||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
{
|
{
|
||||||
log_event("File open failed");
|
log_event("File open failed");
|
||||||
const char *not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found";
|
const char* not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found";
|
||||||
SSL_write(ssl, not_found_response, strlen(not_found_response));
|
SSL_write(ssl, not_found_response, strlen(not_found_response));
|
||||||
free(mime_type);
|
free(mime_type);
|
||||||
if (if_none_match)
|
if (if_none_match)
|
||||||
@@ -1444,7 +1435,7 @@ void *handle_https_client(void *arg)
|
|||||||
if (fstat(fd, &st) == -1)
|
if (fstat(fd, &st) == -1)
|
||||||
{
|
{
|
||||||
log_event("Error getting file size.");
|
log_event("Error getting file size.");
|
||||||
const char *internal_server_error =
|
const char* internal_server_error =
|
||||||
"HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error";
|
"HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error";
|
||||||
SSL_write(ssl, internal_server_error, strlen(internal_server_error));
|
SSL_write(ssl, internal_server_error, strlen(internal_server_error));
|
||||||
close(fd);
|
close(fd);
|
||||||
@@ -1466,7 +1457,7 @@ void *handle_https_client(void *arg)
|
|||||||
// ETag matches - return 304
|
// ETag matches - return 304
|
||||||
char response_304[512];
|
char response_304[512];
|
||||||
char last_modified[64];
|
char last_modified[64];
|
||||||
struct tm *tm_info = gmtime(&st.st_mtime);
|
struct tm* tm_info = gmtime(&st.st_mtime);
|
||||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
||||||
|
|
||||||
int header_len = snprintf(response_304, sizeof(response_304),
|
int header_len = snprintf(response_304, sizeof(response_304),
|
||||||
@@ -1502,7 +1493,7 @@ void *handle_https_client(void *arg)
|
|||||||
|
|
||||||
// Format Last-Modified time
|
// Format Last-Modified time
|
||||||
char last_modified[64];
|
char last_modified[64];
|
||||||
struct tm *tm_info = gmtime(&st.st_mtime);
|
struct tm* tm_info = gmtime(&st.st_mtime);
|
||||||
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info);
|
||||||
|
|
||||||
int header_len = snprintf(response_header, sizeof(response_header),
|
int header_len = snprintf(response_header, sizeof(response_header),
|
||||||
@@ -1528,7 +1519,7 @@ void *handle_https_client(void *arg)
|
|||||||
SSL_write(ssl, response_header, header_len);
|
SSL_write(ssl, response_header, header_len);
|
||||||
|
|
||||||
// Use larger buffer for better performance
|
// Use larger buffer for better performance
|
||||||
char *file_buffer = get_buffer_from_pool(16384);
|
char* file_buffer = get_buffer_from_pool(16384);
|
||||||
ssize_t bytes_read;
|
ssize_t bytes_read;
|
||||||
while ((bytes_read = read(fd, file_buffer, 16384)) > 0)
|
while ((bytes_read = read(fd, file_buffer, 16384)) > 0)
|
||||||
{
|
{
|
||||||
@@ -1543,11 +1534,9 @@ void *handle_https_client(void *arg)
|
|||||||
log_event("Served requested file successfully.");
|
log_event("Served requested file successfully.");
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (ssl)
|
|
||||||
{
|
|
||||||
SSL_shutdown(ssl);
|
SSL_shutdown(ssl);
|
||||||
SSL_free(ssl);
|
SSL_free(ssl);
|
||||||
}
|
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
@@ -1638,7 +1627,7 @@ void shutdown_server()
|
|||||||
log_event("Server shutdown completed.");
|
log_event("Server shutdown completed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_request_line(char *request_buffer, char *method, char *url, char *protocol)
|
int parse_request_line(char* request_buffer, char* method, char* url, char* protocol)
|
||||||
{
|
{
|
||||||
if (!request_buffer || !method || !url || !protocol)
|
if (!request_buffer || !method || !url || !protocol)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1648,12 +1637,12 @@ int parse_request_line(char *request_buffer, char *method, char *url, char *prot
|
|||||||
protocol[0] = '\0';
|
protocol[0] = '\0';
|
||||||
|
|
||||||
char *saveptr1, *saveptr2;
|
char *saveptr1, *saveptr2;
|
||||||
char *line = strtok_r(request_buffer, "\r\n", &saveptr1);
|
char* line = strtok_r(request_buffer, "\r\n", &saveptr1);
|
||||||
|
|
||||||
if (line == NULL || strlen(line) == 0)
|
if (line == NULL || strlen(line) == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
char *token = strtok_r(line, " ", &saveptr2);
|
char* token = strtok_r(line, " ", &saveptr2);
|
||||||
if (token == NULL || strlen(token) > 7)
|
if (token == NULL || strlen(token) > 7)
|
||||||
return -1;
|
return -1;
|
||||||
strncpy(method, token, 7);
|
strncpy(method, token, 7);
|
||||||
@@ -1731,9 +1720,9 @@ void set_cpu_affinity(int thread_id)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void *worker_thread(void *arg)
|
void* worker_thread(void* arg)
|
||||||
{
|
{
|
||||||
int thread_id = *((int *)arg);
|
int thread_id = *((int*)arg);
|
||||||
free(arg);
|
free(arg);
|
||||||
|
|
||||||
// Set CPU affinity for this worker thread
|
// Set CPU affinity for this worker thread
|
||||||
@@ -1746,7 +1735,7 @@ void *worker_thread(void *arg)
|
|||||||
|
|
||||||
while (workers_running)
|
while (workers_running)
|
||||||
{
|
{
|
||||||
connection_task_t *task = dequeue_task(&worker_queue);
|
connection_task_t* task = dequeue_task(&worker_queue);
|
||||||
|
|
||||||
if (!task || !workers_running)
|
if (!task || !workers_running)
|
||||||
{
|
{
|
||||||
@@ -1759,7 +1748,7 @@ void *worker_thread(void *arg)
|
|||||||
// Handle the connection based on type
|
// Handle the connection based on type
|
||||||
if (task->is_https)
|
if (task->is_https)
|
||||||
{
|
{
|
||||||
int *socket_ptr = malloc(sizeof(int));
|
int* socket_ptr = malloc(sizeof(int));
|
||||||
if (socket_ptr)
|
if (socket_ptr)
|
||||||
{
|
{
|
||||||
*socket_ptr = task->socket_fd;
|
*socket_ptr = task->socket_fd;
|
||||||
@@ -1768,7 +1757,7 @@ void *worker_thread(void *arg)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int *socket_ptr = malloc(sizeof(int));
|
int* socket_ptr = malloc(sizeof(int));
|
||||||
if (socket_ptr)
|
if (socket_ptr)
|
||||||
{
|
{
|
||||||
*socket_ptr = task->socket_fd;
|
*socket_ptr = task->socket_fd;
|
||||||
@@ -1811,7 +1800,7 @@ void initialize_thread_pool()
|
|||||||
|
|
||||||
for (int i = 0; i < num_worker_threads; i++)
|
for (int i = 0; i < num_worker_threads; i++)
|
||||||
{
|
{
|
||||||
int *thread_id = malloc(sizeof(int));
|
int* thread_id = malloc(sizeof(int));
|
||||||
if (thread_id)
|
if (thread_id)
|
||||||
{
|
{
|
||||||
*thread_id = i;
|
*thread_id = i;
|
||||||
@@ -1829,14 +1818,14 @@ void initialize_thread_pool()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Case-insensitive strstr
|
// Case-insensitive strstr
|
||||||
char *stristr(const char *haystack, const char *needle)
|
char* stristr(const char* haystack, const char* needle)
|
||||||
{
|
{
|
||||||
if (!haystack || !needle) return NULL;
|
if (!haystack || !needle) return NULL;
|
||||||
|
|
||||||
size_t needle_len = strlen(needle);
|
size_t needle_len = strlen(needle);
|
||||||
if (needle_len == 0) return (char *)haystack;
|
if (needle_len == 0) return (char*)haystack;
|
||||||
|
|
||||||
for (const char *p = haystack; *p; p++)
|
for (const char* p = haystack; *p; p++)
|
||||||
{
|
{
|
||||||
if (tolower(*p) == tolower(*needle))
|
if (tolower(*p) == tolower(*needle))
|
||||||
{
|
{
|
||||||
@@ -1847,14 +1836,14 @@ char *stristr(const char *haystack, const char *needle)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i == needle_len)
|
if (i == needle_len)
|
||||||
return (char *)p;
|
return (char*)p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if MIME type should be compressed
|
// Check if MIME type should be compressed
|
||||||
int should_compress(const char *mime_type)
|
int should_compress(const char* mime_type)
|
||||||
{
|
{
|
||||||
return (strstr(mime_type, "text/") != NULL ||
|
return (strstr(mime_type, "text/") != NULL ||
|
||||||
strstr(mime_type, "application/javascript") != NULL ||
|
strstr(mime_type, "application/javascript") != NULL ||
|
||||||
@@ -1863,7 +1852,7 @@ int should_compress(const char *mime_type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gzip compress data
|
// Gzip compress data
|
||||||
unsigned char *gzip_compress(const unsigned char *data, size_t size, size_t *compressed_size)
|
unsigned char* gzip_compress(const unsigned char* data, size_t size, size_t* compressed_size)
|
||||||
{
|
{
|
||||||
z_stream stream = {0};
|
z_stream stream = {0};
|
||||||
stream.zalloc = Z_NULL;
|
stream.zalloc = Z_NULL;
|
||||||
@@ -1876,7 +1865,7 @@ unsigned char *gzip_compress(const unsigned char *data, size_t size, size_t *com
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t max_compressed = deflateBound(&stream, size);
|
size_t max_compressed = deflateBound(&stream, size);
|
||||||
unsigned char *compressed = malloc(max_compressed);
|
unsigned char* compressed = malloc(max_compressed);
|
||||||
if (!compressed)
|
if (!compressed)
|
||||||
{
|
{
|
||||||
deflateEnd(&stream);
|
deflateEnd(&stream);
|
||||||
@@ -1884,7 +1873,7 @@ unsigned char *gzip_compress(const unsigned char *data, size_t size, size_t *com
|
|||||||
}
|
}
|
||||||
|
|
||||||
stream.avail_in = size;
|
stream.avail_in = size;
|
||||||
stream.next_in = (unsigned char *)data;
|
stream.next_in = (unsigned char*)data;
|
||||||
stream.avail_out = max_compressed;
|
stream.avail_out = max_compressed;
|
||||||
stream.next_out = compressed;
|
stream.next_out = compressed;
|
||||||
|
|
||||||
@@ -1915,10 +1904,13 @@ int main()
|
|||||||
|
|
||||||
// Initialize logging system based on config
|
// Initialize logging system based on config
|
||||||
LogConfig log_cfg = {
|
LogConfig log_cfg = {
|
||||||
.level = (config.log_mode == LOG_MODE_OFF) ? LOG_LEVEL_OFF :
|
.level = (config.log_mode == LOG_MODE_OFF)
|
||||||
(config.log_mode == LOG_MODE_DEBUG) ? LOG_LEVEL_DEBUG :
|
? LOG_LEVEL_OFF
|
||||||
(config.log_mode == LOG_MODE_ADVANCED) ? LOG_LEVEL_TRACE :
|
: (config.log_mode == LOG_MODE_DEBUG)
|
||||||
LOG_LEVEL_INFO,
|
? LOG_LEVEL_DEBUG
|
||||||
|
: (config.log_mode == LOG_MODE_ADVANCED)
|
||||||
|
? LOG_LEVEL_TRACE
|
||||||
|
: LOG_LEVEL_INFO,
|
||||||
.categories = LOG_CAT_ALL,
|
.categories = LOG_CAT_ALL,
|
||||||
.format = LOG_FORMAT_PLAIN,
|
.format = LOG_FORMAT_PLAIN,
|
||||||
.console_output = true,
|
.console_output = true,
|
||||||
@@ -2009,9 +2001,9 @@ int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char *get_mime_type(const char *filepath)
|
char* get_mime_type(const char* filepath)
|
||||||
{
|
{
|
||||||
const char *ext = strrchr(filepath, '.');
|
const char* ext = strrchr(filepath, '.');
|
||||||
if (!ext)
|
if (!ext)
|
||||||
return strdup("application/octet-stream");
|
return strdup("application/octet-stream");
|
||||||
|
|
||||||
@@ -2055,14 +2047,14 @@ char *get_mime_type(const char *filepath)
|
|||||||
return strdup("application/octet-stream");
|
return strdup("application/octet-stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *mime = magic_file(magic, filepath);
|
const char* mime = magic_file(magic, filepath);
|
||||||
char *result = mime ? strdup(mime) : strdup("application/octet-stream");
|
char* result = mime ? strdup(mime) : strdup("application/octet-stream");
|
||||||
|
|
||||||
magic_close(magic);
|
magic_close(magic);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *sanitize_url(const char *url)
|
char* sanitize_url(const char* url)
|
||||||
{
|
{
|
||||||
if (!url)
|
if (!url)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -2071,7 +2063,7 @@ char *sanitize_url(const char *url)
|
|||||||
if (url_len == 0 || url_len > 2048)
|
if (url_len == 0 || url_len > 2048)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
char *sanitized = calloc(1, url_len + 2);
|
char* sanitized = calloc(1, url_len + 2);
|
||||||
if (!sanitized)
|
if (!sanitized)
|
||||||
{
|
{
|
||||||
log_event("Memory allocation failed in sanitize_url");
|
log_event("Memory allocation failed in sanitize_url");
|
||||||
@@ -2123,7 +2115,8 @@ char *sanitize_url(const char *url)
|
|||||||
{
|
{
|
||||||
consecutive_dots++;
|
consecutive_dots++;
|
||||||
if (consecutive_dots > 2)
|
if (consecutive_dots > 2)
|
||||||
{ // Too many dots
|
{
|
||||||
|
// Too many dots
|
||||||
free(sanitized);
|
free(sanitized);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -2194,7 +2187,7 @@ char *sanitize_url(const char *url)
|
|||||||
return sanitized;
|
return sanitized;
|
||||||
}
|
}
|
||||||
|
|
||||||
int check_rate_limit(const char *ip)
|
int check_rate_limit(const char* ip)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&rate_limit_mutex);
|
pthread_mutex_lock(&rate_limit_mutex);
|
||||||
|
|
||||||
@@ -2240,7 +2233,7 @@ int check_rate_limit(const char *ip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add new entry
|
// Add new entry
|
||||||
RateLimit *new_limits = realloc(rate_limits, (rate_limit_count + 1) * sizeof(RateLimit));
|
RateLimit* new_limits = realloc(rate_limits, (rate_limit_count + 1) * sizeof(RateLimit));
|
||||||
if (!new_limits)
|
if (!new_limits)
|
||||||
{
|
{
|
||||||
pthread_mutex_unlock(&rate_limit_mutex);
|
pthread_mutex_unlock(&rate_limit_mutex);
|
||||||
@@ -2295,7 +2288,7 @@ void cleanup_thread_pool()
|
|||||||
|
|
||||||
pthread_mutex_lock(&thread_pool_mutex);
|
pthread_mutex_lock(&thread_pool_mutex);
|
||||||
|
|
||||||
ThreadInfo *temp = thread_pool;
|
ThreadInfo* temp = thread_pool;
|
||||||
thread_pool = NULL;
|
thread_pool = NULL;
|
||||||
thread_pool_size = 0;
|
thread_pool_size = 0;
|
||||||
|
|
||||||
@@ -2306,9 +2299,9 @@ void cleanup_thread_pool()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract header value from HTTP request
|
// Extract header value from HTTP request
|
||||||
char *extract_header_value(const char *request, const char *header_name)
|
char* extract_header_value(const char* request, const char* header_name)
|
||||||
{
|
{
|
||||||
char *header_start = stristr(request, header_name);
|
char* header_start = stristr(request, header_name);
|
||||||
if (!header_start)
|
if (!header_start)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -2316,7 +2309,7 @@ char *extract_header_value(const char *request, const char *header_name)
|
|||||||
while (*header_start == ' ' || *header_start == ':')
|
while (*header_start == ' ' || *header_start == ':')
|
||||||
header_start++;
|
header_start++;
|
||||||
|
|
||||||
char *header_end = strstr(header_start, "\r\n");
|
char* header_end = strstr(header_start, "\r\n");
|
||||||
if (!header_end)
|
if (!header_end)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -2324,7 +2317,7 @@ char *extract_header_value(const char *request, const char *header_name)
|
|||||||
if (value_len == 0 || value_len > 512)
|
if (value_len == 0 || value_len > 512)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
char *value = malloc(value_len + 1);
|
char* value = malloc(value_len + 1);
|
||||||
if (!value)
|
if (!value)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -2383,7 +2376,7 @@ int calculate_dynamic_rate_limit(void)
|
|||||||
return dynamic_limit;
|
return dynamic_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cache_file(const char *path, const char *data, size_t size, const char *mime_type)
|
void cache_file(const char* path, const char* data, size_t size, const char* mime_type)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&cache_mutex);
|
pthread_mutex_lock(&cache_mutex);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "server_config.h"
|
#include "server_config.h"
|
||||||
|
|
||||||
void init_config(ServerConfig *config)
|
void init_config(ServerConfig* config)
|
||||||
{
|
{
|
||||||
config->port = 8080;
|
config->port = 8080;
|
||||||
config->use_https = false;
|
config->use_https = false;
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// Log modes
|
// Log modes
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
LOG_MODE_OFF = 0,
|
LOG_MODE_OFF = 0,
|
||||||
LOG_MODE_CLASSIC = 1,
|
LOG_MODE_CLASSIC = 1,
|
||||||
LOG_MODE_DEBUG = 2,
|
LOG_MODE_DEBUG = 2,
|
||||||
@@ -29,8 +30,8 @@ typedef struct
|
|||||||
char ssl_key_path[256];
|
char ssl_key_path[256];
|
||||||
} ServerConfig;
|
} ServerConfig;
|
||||||
|
|
||||||
int load_config(const char *filename, ServerConfig *config);
|
int load_config(const char* filename, ServerConfig* config);
|
||||||
void init_config(ServerConfig *config);
|
void init_config(ServerConfig* config);
|
||||||
void log_event(const char *message);
|
void log_event(const char* message);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -13,21 +13,19 @@
|
|||||||
#define SHA1_DIGEST_LENGTH 20
|
#define SHA1_DIGEST_LENGTH 20
|
||||||
|
|
||||||
// Base64 encode function
|
// Base64 encode function
|
||||||
static char *base64_encode(const unsigned char *input, int length)
|
static char* base64_encode(const unsigned char* input, int length)
|
||||||
{
|
{
|
||||||
BIO *bmem, *b64;
|
BIO *bmem = BIO_new(BIO_s_mem()), *b64 = BIO_new(BIO_f_base64());;
|
||||||
BUF_MEM *bptr;
|
BUF_MEM* bptr;
|
||||||
|
|
||||||
b64 = BIO_new(BIO_f_base64());
|
|
||||||
bmem = BIO_new(BIO_s_mem());
|
|
||||||
b64 = BIO_push(b64, bmem);
|
b64 = BIO_push(b64, bmem);
|
||||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||||
BIO_write(b64, input, length);
|
BIO_write(b64, input, length);
|
||||||
BIO_flush(b64);
|
BIO_flush(b64);
|
||||||
BIO_get_mem_ptr(b64, &bptr);
|
BIO_get_mem_ptr(b64, &bptr);
|
||||||
|
|
||||||
char *buff = (char *)malloc(bptr->length + 1);
|
char* buff = (char*)malloc(bptr->length + 1);
|
||||||
if (!buff) {
|
if (!buff)
|
||||||
|
{
|
||||||
BIO_free_all(b64);
|
BIO_free_all(b64);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -40,9 +38,10 @@ static char *base64_encode(const unsigned char *input, int length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate WebSocket accept key from client key
|
// Generate WebSocket accept key from client key
|
||||||
char *ws_generate_accept_key(const char *client_key)
|
char* ws_generate_accept_key(const char* client_key)
|
||||||
{
|
{
|
||||||
if (!client_key || strlen(client_key) > 128) {
|
if (!client_key || strlen(client_key) > 128)
|
||||||
|
{
|
||||||
return NULL; // Security: validate input length
|
return NULL; // Security: validate input length
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,14 +56,16 @@ char *ws_generate_accept_key(const char *client_key)
|
|||||||
unsigned char hash[SHA1_DIGEST_LENGTH];
|
unsigned char hash[SHA1_DIGEST_LENGTH];
|
||||||
unsigned int hash_len = 0;
|
unsigned int hash_len = 0;
|
||||||
|
|
||||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
|
||||||
if (!ctx) {
|
if (!ctx)
|
||||||
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EVP_DigestInit_ex(ctx, EVP_sha1(), NULL) != 1 ||
|
if (EVP_DigestInit_ex(ctx, EVP_sha1(), NULL) != 1 ||
|
||||||
EVP_DigestUpdate(ctx, combined, strlen(combined)) != 1 ||
|
EVP_DigestUpdate(ctx, combined, strlen(combined)) != 1 ||
|
||||||
EVP_DigestFinal_ex(ctx, hash, &hash_len) != 1) {
|
EVP_DigestFinal_ex(ctx, hash, &hash_len) != 1)
|
||||||
|
{
|
||||||
EVP_MD_CTX_free(ctx);
|
EVP_MD_CTX_free(ctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -74,20 +75,20 @@ char *ws_generate_accept_key(const char *client_key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle WebSocket handshake
|
// Handle WebSocket handshake
|
||||||
int ws_handle_handshake(int client_socket, const char *request, char *response, size_t response_size)
|
int ws_handle_handshake(int client_socket, const char* request, char* response, size_t response_size)
|
||||||
{
|
{
|
||||||
(void)client_socket; // Unused in this implementation
|
(void)client_socket; // Unused in this implementation
|
||||||
|
|
||||||
// Extract Sec-WebSocket-Key from request
|
// Extract Sec-WebSocket-Key from request
|
||||||
const char *key_header = "Sec-WebSocket-Key: ";
|
const char* key_header = "Sec-WebSocket-Key: ";
|
||||||
char *key_start = strstr(request, key_header);
|
char* key_start = strstr(request, key_header);
|
||||||
if (!key_start)
|
if (!key_start)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
key_start += strlen(key_header);
|
key_start += strlen(key_header);
|
||||||
|
|
||||||
char *key_end = strstr(key_start, "\r\n");
|
char* key_end = strstr(key_start, "\r\n");
|
||||||
if (!key_end)
|
if (!key_end)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -95,7 +96,7 @@ int ws_handle_handshake(int client_socket, const char *request, char *response,
|
|||||||
|
|
||||||
char client_key[256];
|
char client_key[256];
|
||||||
size_t key_len = key_end - key_start;
|
size_t key_len = key_end - key_start;
|
||||||
if (key_len >= sizeof(client_key) || key_len == 0 || key_len > 1024)
|
if (key_len >= sizeof(client_key) || key_len == 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -103,7 +104,7 @@ int ws_handle_handshake(int client_socket, const char *request, char *response,
|
|||||||
client_key[key_len] = '\0';
|
client_key[key_len] = '\0';
|
||||||
|
|
||||||
// Generate accept key
|
// Generate accept key
|
||||||
char *accept_key = ws_generate_accept_key(client_key);
|
char* accept_key = ws_generate_accept_key(client_key);
|
||||||
if (!accept_key)
|
if (!accept_key)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -129,17 +130,17 @@ int ws_handle_handshake(int client_socket, const char *request, char *response,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle WebSocket handshake for SSL connections
|
// Handle WebSocket handshake for SSL connections
|
||||||
int ws_handle_handshake_ssl(SSL *ssl, const char *request, char *response, size_t response_size)
|
int ws_handle_handshake_ssl(SSL* ssl, const char* request, char* response, size_t response_size)
|
||||||
{
|
{
|
||||||
(void)ssl; // Use the same logic, just different transport
|
(void)ssl; // Use the same logic, just different transport
|
||||||
return ws_handle_handshake(0, request, response, response_size);
|
return ws_handle_handshake(0, request, response, response_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse WebSocket frame
|
// Parse WebSocket frame
|
||||||
int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, uint8_t **payload)
|
int ws_parse_frame(const uint8_t* data, size_t len, ws_frame_header_t* header, uint8_t** payload)
|
||||||
{
|
{
|
||||||
// Maximum allowed WebSocket payload size (10MB)
|
// Maximum allowed WebSocket payload size (10MB)
|
||||||
#define MAX_WEBSOCKET_PAYLOAD (10 * 1024 * 1024)
|
#define MAX_WEBSOCKET_PAYLOAD (10 * 1024 * 1024)
|
||||||
|
|
||||||
if (len < 2)
|
if (len < 2)
|
||||||
{
|
{
|
||||||
@@ -195,7 +196,7 @@ int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unmask payload if masked
|
// Unmask payload if masked
|
||||||
*payload = (uint8_t *)malloc(header->payload_length);
|
*payload = (uint8_t*)malloc(header->payload_length);
|
||||||
if (!*payload)
|
if (!*payload)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -217,7 +218,7 @@ int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create WebSocket frame
|
// Create WebSocket frame
|
||||||
int ws_create_frame(uint8_t *buffer, size_t buffer_size, uint8_t opcode, const uint8_t *payload, size_t payload_len)
|
int ws_create_frame(uint8_t* buffer, size_t buffer_size, uint8_t opcode, const uint8_t* payload, size_t payload_len)
|
||||||
{
|
{
|
||||||
size_t header_size;
|
size_t header_size;
|
||||||
|
|
||||||
@@ -277,7 +278,7 @@ int ws_create_frame(uint8_t *buffer, size_t buffer_size, uint8_t opcode, const u
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send WebSocket frame
|
// Send WebSocket frame
|
||||||
int ws_send_frame(ws_connection_t *conn, uint8_t opcode, const uint8_t *payload, size_t payload_len)
|
int ws_send_frame(ws_connection_t* conn, uint8_t opcode, const uint8_t* payload, size_t payload_len)
|
||||||
{
|
{
|
||||||
// Allocate buffer with enough space for header (max 10 bytes) + payload
|
// Allocate buffer with enough space for header (max 10 bytes) + payload
|
||||||
// Check for integer overflow
|
// Check for integer overflow
|
||||||
@@ -319,19 +320,19 @@ int ws_send_frame(ws_connection_t *conn, uint8_t opcode, const uint8_t *payload,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send text message
|
// Send text message
|
||||||
int ws_send_text(ws_connection_t *conn, const char *text)
|
int ws_send_text(ws_connection_t* conn, const char* text)
|
||||||
{
|
{
|
||||||
return ws_send_frame(conn, WS_OPCODE_TEXT, (const uint8_t *)text, strlen(text));
|
return ws_send_frame(conn, WS_OPCODE_TEXT, (const uint8_t*)text, strlen(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send pong response
|
// Send pong response
|
||||||
int ws_send_pong(ws_connection_t *conn, const uint8_t *payload, size_t payload_len)
|
int ws_send_pong(ws_connection_t* conn, const uint8_t* payload, size_t payload_len)
|
||||||
{
|
{
|
||||||
return ws_send_frame(conn, WS_OPCODE_PONG, payload, payload_len);
|
return ws_send_frame(conn, WS_OPCODE_PONG, payload, payload_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close WebSocket connection
|
// Close WebSocket connection
|
||||||
void ws_close_connection(ws_connection_t *conn, uint16_t status_code)
|
void ws_close_connection(ws_connection_t* conn, uint16_t status_code)
|
||||||
{
|
{
|
||||||
uint8_t close_payload[2];
|
uint8_t close_payload[2];
|
||||||
close_payload[0] = (status_code >> 8) & 0xFF;
|
close_payload[0] = (status_code >> 8) & 0xFF;
|
||||||
@@ -348,7 +349,7 @@ void ws_close_connection(ws_connection_t *conn, uint16_t status_code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate UTF-8 encoding
|
// Validate UTF-8 encoding
|
||||||
bool ws_is_valid_utf8(const uint8_t *data, size_t len)
|
bool ws_is_valid_utf8(const uint8_t* data, size_t len)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
while (i < len)
|
while (i < len)
|
||||||
@@ -371,7 +372,8 @@ bool ws_is_valid_utf8(const uint8_t *data, size_t len)
|
|||||||
}
|
}
|
||||||
else if ((data[i] & 0xF8) == 0xF0)
|
else if ((data[i] & 0xF8) == 0xF0)
|
||||||
{
|
{
|
||||||
if (i + 3 >= len || (data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != 0x80)
|
if (i + 3 >= len || (data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) !=
|
||||||
|
0x80)
|
||||||
return false;
|
return false;
|
||||||
i += 4;
|
i += 4;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,23 +27,23 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int socket_fd;
|
int socket_fd;
|
||||||
SSL *ssl;
|
SSL* ssl;
|
||||||
bool is_ssl;
|
bool is_ssl;
|
||||||
bool handshake_complete;
|
bool handshake_complete;
|
||||||
} ws_connection_t;
|
} ws_connection_t;
|
||||||
|
|
||||||
// Function prototypes
|
// Function prototypes
|
||||||
int ws_handle_handshake(int client_socket, const char *request, char *response, size_t response_size);
|
int ws_handle_handshake(int client_socket, const char* request, char* response, size_t response_size);
|
||||||
int ws_handle_handshake_ssl(SSL *ssl, const char *request, char *response, size_t response_size);
|
int ws_handle_handshake_ssl(SSL* ssl, const char* request, char* response, size_t response_size);
|
||||||
int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, uint8_t **payload);
|
int ws_parse_frame(const uint8_t* data, size_t len, ws_frame_header_t* header, uint8_t** payload);
|
||||||
int ws_create_frame(uint8_t *buffer, size_t buffer_size, uint8_t opcode, const uint8_t *payload, size_t payload_len);
|
int ws_create_frame(uint8_t* buffer, size_t buffer_size, uint8_t opcode, const uint8_t* payload, size_t payload_len);
|
||||||
int ws_send_frame(ws_connection_t *conn, uint8_t opcode, const uint8_t *payload, size_t payload_len);
|
int ws_send_frame(ws_connection_t* conn, uint8_t opcode, const uint8_t* payload, size_t payload_len);
|
||||||
int ws_send_text(ws_connection_t *conn, const char *text);
|
int ws_send_text(ws_connection_t* conn, const char* text);
|
||||||
int ws_send_pong(ws_connection_t *conn, const uint8_t *payload, size_t payload_len);
|
int ws_send_pong(ws_connection_t* conn, const uint8_t* payload, size_t payload_len);
|
||||||
void ws_close_connection(ws_connection_t *conn, uint16_t status_code);
|
void ws_close_connection(ws_connection_t* conn, uint16_t status_code);
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
char *ws_generate_accept_key(const char *client_key);
|
char* ws_generate_accept_key(const char* client_key);
|
||||||
bool ws_is_valid_utf8(const uint8_t *data, size_t len);
|
bool ws_is_valid_utf8(const uint8_t* data, size_t len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user