diff --git a/src/http_serv.c b/src/http_serv.c new file mode 100644 index 0000000..1f6c2a2 --- /dev/null +++ b/src/http_serv.c @@ -0,0 +1,88 @@ +#include +#include +#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"); + + + // 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; +} diff --git a/src/main.c b/src/main.c index d4191e0..3e3512c 100644 --- a/src/main.c +++ b/src/main.c @@ -1,57 +1,40 @@ #include +#include #include #include +#include +#include #include #include #include -#include +#include #include #include #define PORT 8080 +#define HOSTPORT "*:4040" #define BUFFER_SIZE 1024 +#define CHAIN_FILE "/home/vashqlf/Code/C/webserver/assets/cert.pem" +#define KEY_FILE "/home/vashqlf/Code/C/webserver/assets/key.pem" 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 + char buf[BUFFER_SIZE]; + size_t nread; + size_t nwritten; + size_t total = 0; // create SSL_CTX config object SSL_CTX* ctx = SSL_CTX_new(TLS_server_method()); if (ctx == NULL) { - perror("webserver (SSL_CTX)"); - return 1; + ERR_print_errors_fp(stderr); + errx(EXIT_FAILURE, "Failed to create server SSL_CTX"); } // 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)") ; + ERR_print_errors_fp(stderr); + errx(EXIT_FAILURE, "Failed to set minimum TLS version."); } // configure option flags @@ -61,17 +44,17 @@ int main(void) { SSL_CTX_set_options(ctx, opts); // load certificate - if (SSL_CTX_use_certificate_chain_file(ctx, "../assets/cert.pem") <= 0) { + if (SSL_CTX_use_certificate_chain_file(ctx, CHAIN_FILE) <= 0) { SSL_CTX_free(ctx); - perror("webserver (SSL_CERT)"); - return 1; + ERR_print_errors_fp(stderr); + errx(EXIT_FAILURE, "Failed to load the server certificate chain file."); } // load private key - if (SSL_CTX_use_PrivateKey_file(ctx, "../assets/key.pem", SSL_FILETYPE_PEM) <= 0) { + if (SSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, SSL_FILETYPE_PEM) <= 0) { SSL_CTX_free(ctx); - perror("webserver (SSL_KEY)"); - return 1; + ERR_print_errors_fp(stderr); + errx(EXIT_FAILURE, "Failed to load the server private key file."); } // TODO enable session caching @@ -79,56 +62,64 @@ int main(void) { // 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; + // create listener socket wrapped in a BIO + BIO* acceptor_bio = BIO_new_accept(HOSTPORT); + if (acceptor_bio == NULL) { + SSL_CTX_free(ctx); + ERR_print_errors_fp(stderr); + errx(EXIT_FAILURE, "Failed to create the acceptor bio."); } - printf("socket listening on port %d\n", PORT); - // create client address - struct sockaddr_in client_addr; - int client_addr_len = sizeof(host_addr); + // first call to do accept makes the socket listening + BIO_set_bind_mode(acceptor_bio, BIO_BIND_REUSEADDR); + if (BIO_do_accept(acceptor_bio) <= 0){ + SSL_CTX_free(ctx); + ERR_print_errors_fp(stderr); + errx(EXIT_FAILURE, "Failed setting up acceptor socket."); + } // 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; + ERR_clear_error(); + // wait for next client to connect + if (BIO_do_accept(acceptor_bio) <= 0) { + continue; } - // read from socket - ssize_t valread = read(newsockfd, buffer, BUFFER_SIZE); - if (valread < 0){ - perror("webserver (read)"); + BIO* client_bio = BIO_pop(acceptor_bio); + fprintf(stderr, "New client connection accepted\n"); + + // associate a new SSL handle with the new connection + SSL* ssl; + if ((ssl = SSL_new(ctx)) == NULL){ + ERR_print_errors_fp(stderr); + warnx("Error creating SSL handle for new connection"); + BIO_free(client_bio); + continue; + } + SSL_set_bio(ssl, client_bio, client_bio); + + // attempt SSL handshake + if (SSL_accept(ssl) <= 0) { + ERR_print_errors_fp(stderr); + warnx("Error performing SSL handshake with client"); + SSL_free(ssl); + continue; } - - // 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)"); + + // echoe client input back to client + while (SSL_read_ex(ssl, buf, sizeof(buf), &nread) > 0){ + if (SSL_write_ex(ssl, buf, nread, &nwritten) > 0 && nwritten == nread){ + total += nwritten; + continue; + } + warnx("Error echoing client input"); + break; } - - close(newsockfd); + fprintf(stderr, "Client connection closed, %zu bytes sent\n", total); + SSL_free(ssl); } - return 0; + SSL_CTX_free(ctx); + return EXIT_SUCCESS; }