#include #include #include #include #include #include #include #include #include #include #include #define PORT 8081 #define BUFFER_SIZE 512 #define MAX_PATH 512 #define ROOT_DIR "." typedef struct handle_request_data { SSL *ssl; int socket; char *req; } handle_request_data; static const struct { const char *ext; const char *type; } mime_types[] = { {".html", "text/html"}, {".css", "text/css"}, {".js", "application/javascript"}, {".jpg", "image/jpeg"}, {".jpeg", "image/jpeg"}, {".png", "image/png"}, {NULL, "application/octet-stream"} }; static const char *get_mime_type(const char *path) { const char *ext = strrchr(path, '.'); if (!ext) return mime_types[0].type; for (int i = 0; mime_types[i].ext; i++) { if (!strcmp(ext, mime_types[i].ext)) return mime_types[i].type; } return mime_types[0].type; } static void send_response(SSL *ssl, const char *status, const char *type, const char *body, size_t len) { char buf[BUFFER_SIZE]; int n = snprintf(buf, sizeof(buf), "HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %zu\r\n\r\n", status, type, len); SSL_write(ssl, buf, n); if (len && body) SSL_write(ssl, body, len); } static void write_full(SSL *ssl, const char *data, size_t len) { while (len > 0) { int sent = SSL_write(ssl, data, len); if (sent <= 0) break; data += sent; len -= sent; } } static void list_files(SSL *ssl, char *directory) { char buf[8192] = "File Server

Files

    "; size_t pos = strlen(buf); DIR *dir = opendir(directory); if (!dir) { send_response(ssl, "500 Internal Server Error", "text/plain", "Cannot open directory", 20); return; } struct dirent *entry; while ((entry = readdir(dir)) && pos < sizeof(buf) - 100) { if (!strcmp(entry->d_name, ".")) continue; if (!strcmp(entry->d_name, "..") && !strcmp(directory, ROOT_DIR)) continue; char path[MAX_PATH]; snprintf(path, sizeof(path), "%s/%s", directory, entry->d_name); pos += snprintf(buf + pos, sizeof(buf) - pos, "
  • %s
  • ", directory, entry->d_name, entry->d_name); } closedir(dir); pos += snprintf(buf + pos, sizeof(buf) - pos, "
"); send_response(ssl, "200 OK", "text/html", buf, pos); } void *handle_request(void *param) { handle_request_data *param_data = (handle_request_data*)param; int client_sock = param_data->socket; SSL *ssl = param_data->ssl; char *req = param_data->req; char method[8], path[MAX_PATH-2], proto[8]; if (sscanf(req, "%7s %127s %7s", method, path, proto) != 3) { send_response(ssl, "400 Bad Request", "text/plain", "Bad Request", 11); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } printf("%s %s %s\n", path, method, proto); if (strcmp(method, "GET")) { send_response(ssl, "405 Method Not Allowed", "text/plain", "Method Not Allowed", 18); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } if (strstr(path, "..")) { send_response(ssl, "403 Forbidden", "text/plain", "Forbidden", 9); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } if (!strcmp(path, "/favicon.ico")) { send_response(ssl, "204 No Content", "text/plain", NULL, 0); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } if (!strcmp(path, "/")) { list_files(ssl, ROOT_DIR); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } char file_path[MAX_PATH]; snprintf(file_path, sizeof(file_path), "%s/%s", ROOT_DIR, path[0] == '/' ? path + 1 : path); if (file_path[0] == '.' && file_path[1] == '/') { for (int i = 2; i < MAX_PATH -1; i++) { file_path[i-2] = file_path[i]; } } struct stat st; if (stat(file_path, &st) < 0 || S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) { list_files(ssl, file_path); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } send_response(ssl, st.st_mode ? "403 Forbidden" : "404 Not Found", "text/plain", st.st_mode ? "Directory listing not allowed" : "Not Found", st.st_mode ? 29 : 9); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } int fd = open(file_path, O_RDONLY); if (fd < 0) { send_response(ssl, "404 Not Found", "text/plain", "Not Found", 9); SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } char buf[BUFFER_SIZE]; int n = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %lld\r\n\r\n", get_mime_type(file_path), (long long)st.st_size); write_full(ssl, buf, n); char data[16384]; off_t total_sent = 0; ssize_t bytes; while ((bytes = read(fd, data, sizeof(data))) > 0) { write_full(ssl, data, bytes); total_sent += bytes; } close(fd); if (total_sent != st.st_size) { send_response(ssl, "500 Internal Server Error", "text/plain", "Incomplete transfer", 18); } SSL_shutdown(ssl); SSL_free(ssl); close(client_sock); pthread_exit(0); } int main() { struct stat st; if (stat(ROOT_DIR, &st) < 0 || !S_ISDIR(st.st_mode)) return 1; SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); const SSL_METHOD *method = TLS_server_method(); SSL_CTX *ctx = SSL_CTX_new(method); if (!ctx) return 1; if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) <= 0 || SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) { SSL_CTX_free(ctx); return 1; } int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { SSL_CTX_free(ctx); return 1; } int opt = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, .sin_port = htons(PORT) }; if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0 || listen(sock, 5) < 0) { close(sock); SSL_CTX_free(ctx); return 1; } printf("Server started on https://localhost:%d\n", PORT); char buf[BUFFER_SIZE]; struct sockaddr_in client; socklen_t len = sizeof(client); while (1) { int client_sock = accept(sock, (struct sockaddr *)&client, &len); if (client_sock < 0) continue; SSL *ssl = SSL_new(ctx); SSL_set_fd(ssl, client_sock); if (SSL_accept(ssl) <= 0) { SSL_free(ssl); close(client_sock); continue; } int n = SSL_read(ssl, buf, BUFFER_SIZE - 1); if (n > 0) { buf[n] = '\0'; handle_request_data data; data.ssl = ssl; data.socket = client_sock; data.req = buf; pthread_t tid; pthread_attr_t attr; pthread_attr_init(&attr); pthread_create(&tid, &attr, handle_request, &data); } } close(sock); SSL_CTX_free(ctx); return 0; }