From 2e10dadb341c879c373ca9c1a1fe3b9a1ac3bcfd Mon Sep 17 00:00:00 2001 From: Azreyo <58790873+Azreyo@users.noreply.github.com> Date: Sun, 9 Feb 2025 13:07:46 +0100 Subject: [PATCH] Update server.c Added support for basic HTTP and HTTP/s functionality Added support for Logging mechanism Added SSL/TLS support --- server.c | 529 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 328 insertions(+), 201 deletions(-) diff --git a/server.c b/server.c index 1bf2990..14956e8 100644 --- a/server.c +++ b/server.c @@ -10,184 +10,360 @@ #include #include #include +#include +#include #include "server_config.h" #define MAX_REQUEST_SIZE 8192 #define MAX_LOG_SIZE 2048 #define MAX_CLIENT_THREADS 100 -// Global variables +#define BOLD "\x1b[1m" +#define RED "\x1b[31m" +#define GREEN "\x1b[32m" +#define YELLOW "\x1b[33m" +#define BLUE "\x1b[34m" +#define RESET "\x1b[0m" + ServerConfig config; char server_log[MAX_LOG_SIZE]; pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_t client_threads[MAX_CLIENT_THREADS]; int num_client_threads = 0; +pthread_mutex_t thread_count_mutex = PTHREAD_MUTEX_INITIALIZER; +SSL_CTX *ssl_ctx = NULL; -// Function declarations -void *handle_client(void *arg); +void *handle_http_client(void *arg); +void *handle_https_client(void *arg); void log_event(const char *message); -void display_menu(); -void handle_menu_option(int option); +void initialize_openssl(); +void cleanup_openssl(); +SSL_CTX *create_ssl_context(); +void configure_ssl_context(SSL_CTX *ctx); +void *start_http_server(void *arg); +void *start_https_server(void *arg); +void shutdown_server(); -void shutdown_server() { - int server_socket = socket(AF_INET, SOCK_STREAM, 0); - config.running = 0; - close(server_socket); - for (int i = 0; i < num_client_threads; i++) { - pthread_cancel(client_threads[i]); - } +void initialize_openssl() { + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); } +void cleanup_openssl() { + if (ssl_ctx) { + SSL_CTX_free(ssl_ctx); + } + EVP_cleanup(); +} -int main() { - ServerConfig config; +SSL_CTX *create_ssl_context() { + const SSL_METHOD *method = TLS_server_method(); + SSL_CTX *ctx = SSL_CTX_new(method); + if (!ctx) { + perror(BOLD RED "Unable to create SSL context" RESET); + exit(EXIT_FAILURE); + } + return ctx; +} +void configure_ssl_context(SSL_CTX *ctx) { + if (SSL_CTX_use_certificate_file(ctx, "certs/cert.pem", SSL_FILETYPE_PEM) <= 0) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + if (SSL_CTX_use_PrivateKey_file(ctx, "certs/key.pem", SSL_FILETYPE_PEM) <= 0) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + if (SSL_CTX_set_cipher_list(ctx, "HIGH: !aNULL: !MD5") != 1) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } +} + +void *start_http_server(void *arg) { + int http_socket = socket(AF_INET, SOCK_STREAM, 0); + if (http_socket < 0) { + perror(BOLD RED "Error: "RESET"creating HTTP socket"); + pthread_exit(NULL); + } + + struct sockaddr_in http_address; + memset(&http_address, 0, sizeof(http_address)); + http_address.sin_family = AF_INET; + AF_INET; + http_address.sin_addr.s_addr = INADDR_ANY; + http_address.sin_port = htons(config.port); + + if (bind(http_socket, (struct sockaddr *)&http_address, sizeof(http_address)) < 0) { + perror(BOLD RED "Error: "RESET" binding HTTP socket"); + close(http_socket); + pthread_exit(NULL); + } + + if (listen(http_socket, 50) < 0) { + perror(BOLD RED"Error: "RESET"listening on HTTP socket"); + close(http_socket); + pthread_exit(NULL); + } + + log_event( "HTTP server started."); + + while (config.running) { + int client_socket = accept(http_socket, NULL, NULL); + if (client_socket < 0) { + perror(BOLD RED"Error: "RESET"accepting HTTP connection"); + continue; + } + + pthread_mutex_lock(&thread_count_mutex); + if (num_client_threads < MAX_CLIENT_THREADS) { + pthread_t client_thread; + int *client_socket_ptr = malloc(sizeof(int)); + *client_socket_ptr = client_socket; + + if (pthread_create(&client_thread, NULL, handle_http_client, client_socket_ptr) == 0) { + client_threads[num_client_threads++] = client_thread; + } else { + perror(BOLD RED "Error: " RESET "creating HTTP client thread"); + close(client_socket); + free(client_socket_ptr); + } + } else { + log_event("Max client threads reached, rejecting connection."); + close(client_socket); + } + pthread_mutex_unlock(&thread_count_mutex); + } + + close(http_socket); + pthread_exit(NULL); +} + +void *start_https_server(void *arg) { + int https_socket = socket(AF_INET, SOCK_STREAM, 0); + if (https_socket < 0) { + perror(BOLD RED"Error: "RESET"creating HTTPS socket"); + pthread_exit(NULL); + } + + struct sockaddr_in https_address; + memset(&https_address, 0, sizeof(https_address)); + https_address.sin_family = AF_INET; + https_address.sin_addr.s_addr = INADDR_ANY; + https_address.sin_port = htons(443); + + if (bind(https_socket, (struct sockaddr *)&https_address, sizeof(https_address)) < 0) { + perror(BOLD RED"Error: "RESET"binding HTTPS socket"); + close(https_socket); + pthread_exit(NULL); + } + + if (listen(https_socket, 50) < 0) { + perror(BOLD RED"Error: "RESET"listening on HTTPS socket"); + close(https_socket); + pthread_exit(NULL); + } + + log_event("HTTPS server started."); + + while (config.running) { + int client_socket = accept(https_socket, NULL, NULL); + if (client_socket < 0) { + perror("Error accepting HTTPS connection"); + continue; + } + + pthread_mutex_lock(&thread_count_mutex); + if (num_client_threads < MAX_CLIENT_THREADS) { + pthread_t client_thread; + int *client_socket_ptr = malloc(sizeof(int)); + *client_socket_ptr = client_socket; + + if (pthread_create(&client_thread, NULL, handle_https_client, client_socket_ptr) == 0) { + client_threads[num_client_threads++] = client_thread; + } else { + perror("Error creating HTTPS client thread"); + close(client_socket); + free(client_socket_ptr); + } + } else { + log_event("Max client threads reached, rejecting connection."); + close(client_socket); + } + pthread_mutex_unlock(&thread_count_mutex); + } + + close(https_socket); + pthread_exit(NULL); +} + +void *handle_http_client(void *arg) { + int client_socket = *((int *)arg); + free(arg); + + char buffer[MAX_REQUEST_SIZE]; + ssize_t bytes_received = recv(client_socket, buffer, MAX_REQUEST_SIZE - 1, 0); + if (bytes_received > 0) { + buffer[bytes_received] = '\0'; + log_event("Received HTTP request"); + send(client_socket, "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, HTTP!", 48, 0); + } + + close(client_socket); + pthread_exit(NULL); +} + +void *handle_https_client(void *arg) { + int client_socket = *((int *)arg); + free(arg); + + SSL *ssl = SSL_new(ssl_ctx); + SSL_set_fd(ssl, client_socket); + + if (SSL_accept(ssl) > 0) { + char filepath[256]; + snprintf(filepath, sizeof(filepath), "www/%s", "index.html"); + + int fd = open(filepath, O_RDONLY); + + if (strstr(filepath, "..")) { + const char *forbiden_access = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied"; + SSL_write(ssl, forbiden_access, strlen(forbiden_access)); + log_event("Potential directory traversal attempt detected."); + } + + if (fd == -1) { + 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)); + log_event("File not found, sent 404."); + } else { + struct stat st; + fstat(fd, &st); + off_t file_size = st.st_size; + + char response_header[256]; + snprintf(response_header, sizeof(response_header), + "HTTP/1.1 200 OK\r\n" + "Content-Length: %ld\r\n" + "Content-Type: text/html\r\n" + "\r\n", + file_size); + + + ssize_t total_sent = 0, bytes_sent; + size_t header_len = strlen(response_header); + + while (total_sent < header_len) { + bytes_sent = SSL_write(ssl, response_header + total_sent, header_len - total_sent); + if (bytes_sent <= 0) { + log_event("Failed to send HTTPS header."); + goto cleanup; + } + total_sent += bytes_sent; + } + + + char buffer[1024]; + ssize_t bytes_read; + total_sent = 0; + + while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { + size_t body_len = bytes_read; + while (total_sent < body_len) { + bytes_sent = SSL_write(ssl, buffer + total_sent, body_len - total_sent); + + if (bytes_sent <= 0) { + log_event("Failed to send HTTPS body."); + goto cleanup; + } + total_sent += bytes_sent; + } + + } + if (bytes_read < 0) { + log_event("Error reading from file."); + goto cleanup; + } + + + log_event("Sent HTTPS response successfully."); + close(fd); + } + } else { + log_event("SSL handshake failed."); + } + +cleanup: + SSL_shutdown(ssl); + SSL_free(ssl); + close(client_socket); + pthread_exit(NULL); +} + +void shutdown_server() { + config.running = 0; + + int dummy_socket = socket(AF_INET, SOCK_STREAM, 0); + if (dummy_socket >= 0) { + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + server_addr.sin_port = htons(config.port); + + if (connect(dummy_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { + perror("Failed to connect to the server"); + } + close(dummy_socket); + } else { + perror("Failed to create dummy socket"); + } + + pthread_mutex_lock(&thread_count_mutex); + for (int i = 0; i < num_client_threads; i++) { + if (client_threads[i] != 0) { + pthread_cancel(client_threads[i]); + pthread_join(client_threads[i], NULL); + } + } + num_client_threads = 0; + pthread_mutex_unlock(&thread_count_mutex); + + cleanup_openssl(); + log_event("Server shutdown completed."); +} + +int main() { if (load_config("server.json", &config) != 0) { printf("Using default configuration.\n"); } config.running = 1; - int server_socket = socket(AF_INET, SOCK_STREAM, 0); - if (server_socket < 0) { - perror("Error creating socket"); - exit(1); + if (config.use_https) { + initialize_openssl(); + ssl_ctx = create_ssl_context(); + configure_ssl_context(ssl_ctx); } - struct sockaddr_in server_address; - memset(&server_address, 0, sizeof(server_address)); - server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = INADDR_ANY; - server_address.sin_port = htons(config.port); - - if (bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) { - perror("Error binding socket"); - exit(1); + pthread_t http_thread, https_thread; + pthread_create(&http_thread, NULL, start_http_server, NULL); + if (config.use_https) { + pthread_create(&https_thread, NULL, start_https_server, NULL); } - if (listen(server_socket, 5) < 0) { - perror("Error listening for connections"); - exit(1); + pthread_join(http_thread, NULL); + if (config.use_https) { + pthread_join(https_thread, NULL); } - log_event("Server started."); + shutdown_server(); - - - - while (config.running) { - int client_socket; - struct sockaddr_in client_address; - socklen_t client_address_len = sizeof(client_address); - pthread_mutex_t client_count_mutex = PTHREAD_MUTEX_INITIALIZER; - - client_socket = accept(server_socket, (struct sockaddr *)&client_address, &client_address_len); - if (client_socket < 0) { - perror("Error accepting connection"); - continue; - } - log_event("Client connected."); - setbuf(stdin, NULL); - pthread_t client_thread; - int *client_socket_ptr = malloc(sizeof(int)); - *client_socket_ptr = client_socket; - - if (pthread_create(&client_thread, NULL, handle_client, (void *)client_socket_ptr) != 0) { - perror("Error creating thread"); - close(client_socket); - free(client_socket_ptr); - continue; - } - - pthread_mutex_lock(&client_count_mutex); - if (num_client_threads < MAX_CLIENT_THREADS) { - client_threads[num_client_threads++] = client_thread; - } else { - fprintf(stderr, "Maximum number of client threads reached.\n"); - close(client_socket); - free(client_socket_ptr); - } - pthread_mutex_unlock(&client_count_mutex); - } - - for(int i = 0; i < num_client_threads; i++) { - pthread_join(client_threads[i], NULL); - } - - close(server_socket); - pthread_mutex_destroy(&log_mutex); - log_event("Server stopped."); return 0; } - - -void cleanup_handler(void *arg) { - int client_socket = *((int *)arg); - close(client_socket); - printf("handle_client: Client socket closed.\n"); // debug -} - -void *handle_client(void *arg) { - int client_socket = *((int *)arg); - free(arg); - - printf("handle_client: Client connected. client_socket = %d\n", client_socket); // debug - - char request_buffer[MAX_REQUEST_SIZE]; - ssize_t bytes_received = recv(client_socket, request_buffer, MAX_REQUEST_SIZE - 1, 0); - - printf("handle_client: bytes_received = %ld\n", bytes_received); // debug - - // Push cleanup handler with the correct argument - pthread_cleanup_push(cleanup_handler, (void *)&client_socket); // Pass the address of client_socket - - if (bytes_received < 0) { - perror("handle_client: Error receiving data"); - log_event("Error receiving data"); // Log the error - } else if (bytes_received == 0) { - log_event("Client disconnected."); - } else { - request_buffer[bytes_received] = '\0'; - log_event("Received request:"); - log_event(request_buffer); - - char filepath[256]; - strcpy(filepath, "www/"); - strcat(filepath, "index.html"); // Default file - - int fd = open(filepath, O_RDONLY); - printf("handle_client: File descriptor (fd) = %d\n", fd); - - if (fd == -1) { - 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); - } else { - struct stat st; - fstat(fd, &st); - off_t file_size = st.st_size; - - printf("handle_client: File size = %ld\n", file_size); // debug - - char headers[512]; - snprintf(headers, sizeof(headers), "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n\r\n", file_size); - send(client_socket, headers, strlen(headers), 0); - - char buffer[1024]; - ssize_t bytes_read; - while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { - printf("handle_client: bytes_read = %ld\n", bytes_read); // debug - send(client_socket, buffer, bytes_read, 0); - } - - close(fd); - } - } - - pthread_cleanup_pop(1); // Pop and execute cleanup handler - pthread_exit(NULL); -} - void log_event(const char *message) { pthread_mutex_lock(&log_mutex); @@ -195,70 +371,21 @@ void log_event(const char *message) { struct tm tm = *localtime(&t); char timestamp[64]; - // Format timestamp strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &tm); - // Append new log entry safely size_t remaining_size = MAX_LOG_SIZE - strlen(server_log) - 2; if (remaining_size > 0) { snprintf(server_log + strlen(server_log), remaining_size, "%s: %s\n", timestamp, message); } + FILE *logfile = fopen(config.log_file, "a"); + if (logfile) { + fprintf(logfile, "%s: %s\n", timestamp, message); + fclose(logfile); + } else { + perror("Error opening log file"); + } + pthread_mutex_unlock(&log_mutex); } -void print_log() { - pthread_mutex_lock(&log_mutex); - printf("Server Log:\n%s", server_log); - pthread_mutex_unlock(&log_mutex); -} - - - -void display_menu() { - printf("\nServer Menu:\n"); - printf("1. Status\n"); - printf("2. Logging\n"); - printf("3. Config\n"); - printf("4. Switch HTTP/HTTPS\n"); - printf("5. Troubleshooting\n"); - printf("6. Exit\n"); - printf("Enter your choice: "); - - char input[10]; - if (fgets(input, sizeof(input), stdin) != NULL) { - int choice = atoi(input); - handle_menu_option(choice); - } -} - - -void handle_menu_option(int option) { - switch (option) { - case 1: - printf("Server Status:\n"); - printf("Running: %s\n", config.running ? "Yes" : "No"); - printf("Port: %d\n", config.port); - break; - case 2: - pthread_mutex_lock(&log_mutex); - printf("Server Log:\n%s\n", server_log); - pthread_mutex_unlock(&log_mutex); - break; - case 3: - printf("Config Options:\n"); - break; - case 4: - printf("Switching HTTP/HTTPS:\n"); - break; - case 5: - printf("Troubleshooting:\n"); - break; - case 6: - printf("Exiting...\n"); - shutdown_server(); - break; - default: - printf("Invalid option.\n"); - } -}