commit f47dc33f3c5a77cb5a37bfa1ceafbeb52e485195 Author: benjilegeek Date: Sun Feb 22 00:33:44 2026 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d47ae27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Build directories +build/ +builds/ +out/ +cmake-build-*/ # CLion default + +# CMake generated files +CMakeCache.txt +CMakeFiles/ +CMakeScripts/ +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps/ + +# Compiled binaries and libraries +*.exe +*.out +*.app +*.dll +*.so +*.so.* +*.dylib +*.a +*.lib + +# Debug / profiling artifacts +*.dSYM/ +*.su +*.idb +*.pdb +*.gch +*.pch + +# Sanitizer / coverage output +*.gcno +*.gcda +*.gcov +default.profraw + +# IDE and editor files +.vscode/ +.cache/ +.idea/ +*.swp +*.swo +*~ +.DS_Store +Thumbs.db diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..27cd161 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.15) +project(Webserver) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + + +# Generate compile_commands.json (for clangd, IDEs, etc.) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Find OpenSSL (includes both libssl and libcrypto) +find_package(OpenSSL REQUIRED) + +# Source files +set(SOURCES + src/main.c +) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +# Include OpenSSL headers +target_include_directories(${PROJECT_NAME} PRIVATE + ${OPENSSL_INCLUDE_DIR} +) + +# Link against OpenSSL libraries +# OpenSSL::SSL → libssl +# OpenSSL::Crypto → libcrypto +target_link_libraries(${PROJECT_NAME} PRIVATE + OpenSSL::SSL + OpenSSL::Crypto +) + +# Compiler warnings + debug flags (GCC/Clang) +target_compile_options(${PROJECT_NAME} PRIVATE + $<$: + -Wall + -Wextra + -Wpedantic + -Wshadow + -Wformat=2 + -Wconversion + -Wsign-conversion + -Wnull-dereference + -Wdouble-promotion + -Wstrict-prototypes + -Wmissing-prototypes + > + $<$: + -fanalyzer # GCC static analyzer + > +) + +# Optional: print found versions for debugging +message(STATUS "OpenSSL version: ${OPENSSL_VERSION}") +message(STATUS "OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}") +message(STATUS "OpenSSL libraries: ${OPENSSL_LIBRARIES}") diff --git a/assets/cert.pem b/assets/cert.pem new file mode 100644 index 0000000..656aa88 --- /dev/null +++ b/assets/cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL +BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X +DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 +IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 +daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 +kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq +dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT +bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 +J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK +NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 +yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W +ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU +XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg ++MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 +Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 +fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr +AopvZ09uEQ== +-----END CERTIFICATE----- diff --git a/assets/key.pem b/assets/key.pem new file mode 100644 index 0000000..3de14eb --- /dev/null +++ b/assets/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD +BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS +tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw +RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l +YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t +HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV +W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB +12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI ++2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw +zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt +fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty +RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT +ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 +myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ +XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY +5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD +ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD +ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 +k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 +wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV +5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg +3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa +Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C +rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m +y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW +w37pCS7ykL+7gp7V0WShYsw= +-----END PRIVATE KEY----- diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d4191e0 --- /dev/null +++ b/src/main.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 8080 +#define BUFFER_SIZE 1024 + +int main(void) { + // http server + char buffer[BUFFER_SIZE]; + char resp[] = "HTTP/1.0 200 OK\r\n" + "Server: webserver-c\r\n" + "Content-type: text/html\r\n\r\n" + "hello, world\r\n"; + + // create socket + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == -1){ + perror("webserver (socket)"); + return 1; + } + printf("socket created succesfully\n"); + + // bind to port and address + struct sockaddr_in host_addr; + socklen_t host_addr_len = sizeof(host_addr); + host_addr.sin_family = AF_INET; + host_addr.sin_port = htons(PORT); + host_addr.sin_addr.s_addr = htonl(INADDR_ANY); + if(bind(sockfd, (struct sockaddr*)&host_addr, host_addr_len) != 0){ + perror("webserver (bind)"); + return 1; + } + printf("socket succesfully bound to address\n"); + + // TLS/SSL BLOCK + + // create SSL_CTX config object + SSL_CTX* ctx = SSL_CTX_new(TLS_server_method()); + if (ctx == NULL) { + perror("webserver (SSL_CTX)"); + return 1; + } + + // restrict allowed TLS version so 1.2 or above + if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) { + SSL_CTX_free(ctx); + perror("webserver (TLS_VERSION)") ; + } + + // configure option flags + uint16_t opts = SSL_OP_IGNORE_UNEXPECTED_EOF; // tolerate clients hanging without a TLS "shoutdown" + opts |= SSL_OP_IGNORE_UNEXPECTED_EOF; // block clients from frequent renegotiation + opts |= SSL_OP_SERVER_PREFERENCE; + SSL_CTX_set_options(ctx, opts); + + // load certificate + if (SSL_CTX_use_certificate_chain_file(ctx, "../assets/cert.pem") <= 0) { + SSL_CTX_free(ctx); + perror("webserver (SSL_CERT)"); + return 1; + } + + // load private key + if (SSL_CTX_use_PrivateKey_file(ctx, "../assets/key.pem", SSL_FILETYPE_PEM) <= 0) { + SSL_CTX_free(ctx); + perror("webserver (SSL_KEY)"); + return 1; + } + + // TODO enable session caching + + // no need to verify client by cert + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + + + + // listen on socket + if(listen(sockfd, SOMAXCONN) != 0){ + perror("webserver (listen)"); + return 1; + } + printf("socket listening on port %d\n", PORT); + + // create client address + struct sockaddr_in client_addr; + int client_addr_len = sizeof(host_addr); + + // accept connections + while (1) { + int newsockfd = accept(sockfd, (struct sockaddr*) &host_addr, (socklen_t *) &host_addr_len); + if (newsockfd < 0){ + perror("webserver (accept)"); + return 1; + } + printf("connection accepted\n"); + + // get client addr + int sockn = getsockname(newsockfd, (struct sockaddr*) &client_addr, (socklen_t *) &client_addr_len); + + if (sockn < 0){ + perror("webserver (getsockname)"); + return 1; + } + + // read from socket + ssize_t valread = read(newsockfd, buffer, BUFFER_SIZE); + if (valread < 0){ + perror("webserver (read)"); + } + + // read the request + char method[BUFFER_SIZE], uri[BUFFER_SIZE], version[BUFFER_SIZE]; + sscanf(buffer, "%s %s %s", method, uri, version); + + printf("[%s:%u] %s %s %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), method, version, uri); + + // write to the socket + ssize_t valwrite = write(newsockfd, resp, strlen(resp)); + if (valwrite < 0){ + perror("webserver (write)"); + } + + close(newsockfd); + } + + return 0; +}