diff --git a/Dockerfile b/Dockerfile index e3bf864..9508d9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,27 @@ FROM ubuntu:latest -RUN apt-get update && \ - apt-get install -y openssl +ENV DEBIAN_FRONTEND=noninteractive -COPY ./bin/seaweb /bin/ -COPY ./certs/cert.pem /etc/ssl/certs/ -COPY ./certs/key.pem /etc/ssl/private/ +RUN apt-get update && \ + apt-get install \ + -y openssl libssl-dev make gcc pkg-config + +COPY Makefile /src/ +COPY src/ /src/src/ +COPY cmd/ /src/cmd/ +COPY include/ /src/include/ +COPY ca/ /src/ca/ + +COPY content/ /var/www/html + +RUN mkdir -p /var/log/seaweb + +WORKDIR /src/ + +RUN make && make install + +# Create Certificates +RUN printf "y\ny\n" | make genCerts EXPOSE 8080/tcp diff --git a/Makefile b/Makefile index ea245d4..2378588 100644 --- a/Makefile +++ b/Makefile @@ -5,13 +5,13 @@ OUTPUT = -o ${OUTPUT_DIR}/PROG INSTALL_OUTPUT = ${OUTPUT_DIR}/PROG build: output_dir - gcc -Wall ${LIBRARIES} ${SOURCES} ${OUTPUT:PROG=seaweb} + gcc -Wall ${SOURCES} ${OUTPUT:PROG=seaweb} ${LIBRARIES} debug: output_dir - gcc -Wall -g ${LIBRARIES} ${SOURCES} ${OUTPUT:PROG=seaweb} + gcc -Wall -g ${SOURCES} ${OUTPUT:PROG=seaweb} ${LIBRARIES} install: - mv ${INSTALL_OUTPUT:PROG=server} /usr/bin/ + mv ${INSTALL_OUTPUT:PROG=seaweb} /usr/bin/ output_dir: mkdir -p ${OUTPUT_DIR} @@ -36,3 +36,11 @@ genCerts: -rand_serial -config ./ca/ca.cnf \ -out ./certs/cert.pem -infiles ./certs/cert.csr +dockerBuild: output_dir + docker build . -t seaweb:latest + +dockerTestDeploy: dockerBuild + docker run -p8080:8080 --rm seaweb:latest seaweb & + +killTestDocker: + docker stop -t 0 `docker ps | grep "seaweb:latest" | tail -n 1 | tr -s " " | cut -d " " -f 1` diff --git a/cmd/server.c b/cmd/server.c index a760adb..e396bff 100644 --- a/cmd/server.c +++ b/cmd/server.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include // Local Includes #include "httpStruct.h" @@ -14,12 +16,15 @@ #include "server.h" #include "utils.h" +//#define WEB_ROOT "/var/www/html/" #define WEB_ROOT "content/" #define BUFF_READ 1024 static int verbose_flag = 0; bool enableHTTPS = 0; +static int timeout = 0; + int printDebug(char message[]) { if (verbose_flag == 1) { printf("[Debug] %s\n", message); @@ -43,13 +48,14 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) { // Checking for newline character OR end of string if (((!strcmp(temp, "\n")) && (i != 0)) || (i == strlen((char *)buffer))) { // Config Check - if (line == 0) { + if (line == 0) { // Grabbing first line for type parsing logLine = calloc(strlen((char *)checkLine), sizeof(char)); strcpy((char *)logLine, (char *)checkLine); token = strtok((char *)checkLine, " "); // HTTP Request Type if ((!strcmp(token, "GET")) || (!strcmp(token, "POST")) || - (!strcmp(token, "PUT")) || (!strcmp(token, "DELETE"))) { + (!strcmp(token, "PUT")) || (!strcmp(token, "DELETE")) || + (!strcmp(token, "CONNECT"))) { // Grabbing HTTP Request Type r->requestType = malloc(strlen(token)); strcpy(r->requestType, token); @@ -59,27 +65,24 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) { strcpy(r->requestDir, token); // Grabbing HTTP Request Version token = strtok(NULL, ""); + token[strlen(token) - 1] = '\0'; // Fixing version end char r->requestVersion = malloc(strlen(token)); strcpy(r->requestVersion, token); - } else { - char *errMessage = "Unsupported request type: "; - logLine = calloc(strlen(errMessage) + strlen(token), sizeof(unsigned char)); - sprintf((char *)logLine, "%s%s", errMessage, token); - PrintLog(logLine); + } else { // First line contains unsupported request free(logLine); free(checkLine); return 2; } // Log Request PrintLog(logLine); - } else { + } else { // Gather information on >first line if (contentCheck) { // Once content check is set to one everything after is part of the body r->requestBody = realloc(r->requestBody, (strlen((char *)checkLine) + strlen((char *)r->requestBody) + 1)); strcat((char *)r->requestBody, (char *)checkLine); // Adding newline to requestBody sprintf((char *)r->requestBody, "%s\n", r->requestBody); - } else { + } else { // Information parsing !content if (strlen((char *)checkLine) == 1) { // Looking for blank empty line to end header info contentCheck = 1; } @@ -127,7 +130,7 @@ int handleGetRequest(int socket, struct HTTPRequest *r, SSL *ssl) { } else { workingReqDir = r->requestDir; } - char *reqDir = calloc(strlen(WEB_ROOT) + strlen(workingReqDir), sizeof(char)); + char *reqDir = calloc((strlen(WEB_ROOT) + strlen(workingReqDir) + 1), sizeof(char)); sprintf(reqDir, "%s%s", WEB_ROOT, workingReqDir); @@ -176,15 +179,48 @@ int handlePUTRequest(int socket, struct HTTPRequest *r, SSL *ssl) { if (fp == NULL) { sprintf(errResponse, "Error opening file: %s", workingReqDir); printDebug(errResponse); - return404Request(socket, ssl); - return -1; + return return404Request(socket, ssl); } fprintf(fp, "%s", r->requestBody); free(reqDir); fclose(fp); - return201Request(socket, r->requestBody, ssl); + return return201Request(socket, r->requestBody, ssl); +} + +int handleDELETERequest(int socket, struct HTTPRequest *r, SSL *ssl) { + char *reqDir; + char * workingReqDir; + char logLine[256]; + + if (!strcmp(r->requestDir, "/")) { + workingReqDir = "index.html"; + } else { + workingReqDir = r->requestDir; + } + + reqDir = calloc(strlen(WEB_ROOT) + strlen(workingReqDir), sizeof(char)); + sprintf(reqDir, "%s%s", WEB_ROOT, workingReqDir); + + if (remove(reqDir) == 0) { + sprintf(logLine, "Deleted file"); + free(reqDir); + return return200Request(socket, NULL, ssl); + } else { + printDebug("Failed to delete file"); + free(reqDir); + if (errno == 1) { // Permission denided + return return403Request(socket, ssl); + } else { // Catch all (likely file != exist) + return return404Request(socket, ssl); + } + } + return 0; +} + +int handlePOSTRequest(int socket, struct HTTPRequest *r, SSL *ssl) { + return501Request(socket, ssl); return 0; } @@ -196,25 +232,33 @@ int handleRequest(unsigned char buffer[], int socket, SSL *ssl) { checkerr = parseHTTPRequest(buffer, &r); if (checkerr != 0) { // Checking for HTTP parsing error if (checkerr == -1) { - printDebug("Error reading request, returning empty 404"); + printDebug("Error reading request, returning empty 500"); + return500Request(socket, ssl); } else { - printDebug("Error parsing, returning 500"); + printDebug("Error parsing, returning 501"); return return501Request(socket, ssl); } - return return400Request(socket, ssl); + } else { + checkerr = checkHTTPVersion(r.requestVersion); + if (checkerr != 0) { + return return505Request(socket, ssl); + } } if (!strcmp(r.requestType, "GET")) { handleGetRequest(socket, &r, ssl); return 0; } else if (!strcmp(r.requestType, "POST")) { - return501Request(socket, ssl); + handlePOSTRequest(socket, &r, ssl); return 0; } else if (!strcmp(r.requestType, "PUT")) { handlePUTRequest(socket, &r, ssl); return 0; } else if (!strcmp(r.requestType, "DELETE")) { - return501Request(socket, ssl); + handleDELETERequest(socket, &r, ssl); + return 0; + } else if (!strcmp(r.requestType, "CONNECT")) { + return200Request(socket, NULL, ssl); return 0; } else { return500Request(socket, ssl); @@ -222,6 +266,10 @@ int handleRequest(unsigned char buffer[], int socket, SSL *ssl) { } } +void timeoutChild(int sig) { + timeout = 1; +} + int main(int argc, char **argv) { struct sockaddr_in address; int server_fd, new_socket; @@ -342,41 +390,75 @@ int main(int argc, char **argv) { perror("Accept connection error"); exit(EXIT_FAILURE); } - bufSize = BUFF_READ; - if ( enableHTTPS ) { - size_t buffCont = 1; - SSL *ssl; - ssl = SSL_new(ctx); - SSL_set_fd(ssl, new_socket); - SSL_accept(ssl); - do { - buffCont = SSL_read(ssl, buffer, BUFF_READ); - if (strlen((char *)buffer) == bufSize) { - bufSize *= 2; - buffer = realloc(buffer, bufSize); - } - } while(buffCont == 0); - if (strlen((char *)buffer) != 0) { - handleRequest(buffer, new_socket, ssl); - buffer = calloc(bufSize, sizeof(unsigned char)); + + // Forking process + pid_t pid; + pid = fork(); + + if (pid == 0) { + pid_t pid2; + pid2 = fork(); + + if (pid2 == 0) { + bufSize = BUFF_READ; + if ( enableHTTPS ) { + size_t buffCont = 1; + SSL *ssl; + ssl = SSL_new(ctx); + SSL_set_fd(ssl, new_socket); + SSL_accept(ssl); + do { + buffCont = SSL_read(ssl, buffer, BUFF_READ); + if (strlen((char *)buffer) == bufSize) { + bufSize *= 2; + buffer = realloc(buffer, bufSize); + } + } while(buffCont == 0); + if (strlen((char *)buffer) != 0) { + handleRequest(buffer, new_socket, ssl); + buffer = calloc(bufSize, sizeof(unsigned char)); + } else { + printDebug("Error reading from socket"); + } + } else { + size_t buffCont = 1; + do { + buffCont = read(new_socket, buffer, BUFF_READ); + if (strlen((char *)buffer) == bufSize) { + bufSize *= 2; + buffer = realloc(buffer, bufSize); + } + } while(buffCont == 0); + + handleRequest(buffer, new_socket, NULL); + buffer = calloc(bufSize, sizeof(unsigned char)); + } + exit(EXIT_SUCCESS); + } else if (pid2 < 0) { + printDebug("Error forking supervisor..."); } else { - printDebug("Error reading from socket"); + int status; + signal(SIGALRM, timeoutChild); + alarm(2); + pause(); + if (timeout) { + status = waitpid(pid, NULL, WNOHANG); + if (status == 0) { + printDebug("Killing child"); + kill(pid2, 9); + wait(NULL); + } + } + exit(EXIT_SUCCESS); } + close(new_socket); + } else if (pid < 0) { + printDebug("Error forking..."); } else { - size_t buffCont = 1; - do { - buffCont = read(new_socket, buffer, BUFF_READ); - if (strlen((char *)buffer) == bufSize) { - bufSize *= 2; - buffer = realloc(buffer, bufSize); - } - } while(buffCont == 0); - - handleRequest(buffer, new_socket, NULL); - buffer = calloc(bufSize, sizeof(unsigned char)); + signal(SIGCHLD, SIG_IGN); + close(new_socket); + continue; } - - close(new_socket); } free(privKeyFile); diff --git a/include/utils.h b/include/utils.h index bdf6f69..d21ab81 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,2 +1,3 @@ int PrintLog(unsigned char *message); +int checkHTTPVersion(char *version); diff --git a/src/returnRequest.c b/src/returnRequest.c index 0148a34..244302b 100644 --- a/src/returnRequest.c +++ b/src/returnRequest.c @@ -14,18 +14,24 @@ int returnRequest(int socket, char *message, int status, SSL *ssl) { } else { send(socket, message, strlen(message), 0); } + free(message); return 0; } int return200Request(int socket, unsigned char *content, SSL *ssl) { char *message = ""; - message = calloc(strlen((char *)content) + 128, sizeof(unsigned char)); - sprintf(message, "HTTP/1.1 200 OK\nContent-Length: %zu\nConnection: close\n\n%s\n", + if (content != NULL) { + message = calloc(strlen((char *)content) + 128, sizeof(char)); + sprintf(message, "HTTP/1.1 200 OK\nContent-Length: %zu\nConnection: close\n\n%s\n", strlen((char *)content), content); + } else { // Sending empty response + message = calloc(128, sizeof(char)); + sprintf(message, "HTTP/1.1 200 OK\nContent-Length: 0\nConnection: close\n\n"); + } return returnRequest(socket, message, 200, ssl); } -int return201Request(int socket, unsigned char *content, SSL *ssl) { +int return201Request(int socket, char *content, SSL *ssl) { char *message = ""; message = calloc(strlen((char *)content) + 128, sizeof(unsigned char)); sprintf(message, "HTTP/1.1 201 Created\nContent-Length: %zu\nConnection: close\n\n%s\n", @@ -33,22 +39,38 @@ int return201Request(int socket, unsigned char *content, SSL *ssl) { return returnRequest(socket, message, 201, ssl); } +int return403Request(int socket, SSL *ssl) { + char *message = calloc(128, sizeof(char)); + sprintf(message, "HTTP/1.1 403 Forbidden\nContent-Length: 0\nConnection: close\n\n"); + return returnRequest(socket, message, 403, ssl); +} + int return404Request(int socket, SSL *ssl) { - char *message = "HTTP/1.1 404 Not Found\nContent-Length: 12\nConnection: close\n\n404 Request\n"; + char *message = calloc(128, sizeof(char)); + sprintf(message, "HTTP/1.1 404 Not Found\nContent-Length: 12\nConnection: close\n\n404 Request\n\n"); return returnRequest(socket, message, 404, ssl); } int return400Request(int socket, SSL *ssl) { - char *message = "HTTP/1.1 400 HTTP Request Not Valid\nContent-Length: 0\nConnection: close\n\n"; + char *message = calloc(128, sizeof(char)); + sprintf(message, "HTTP/1.1 400 HTTP Request Not Valid\nContent-Length: 0\nConnection: close\n\n"); return returnRequest(socket, message, 400, ssl); } int return500Request(int socket, SSL *ssl) { - char *message = "HTTP/1.1 500 Internal Server Error\nContent-Length: 0\nConnection: close\n\n"; + char *message = calloc(128, sizeof(char)); + sprintf(message, "HTTP/1.1 500 Internal Server Error\nContent-Length: 0\nConnection: close\n\n"); return returnRequest(socket, message, 500, ssl); } int return501Request(int socket, SSL *ssl) { - char *message = "HTTP/1.1 501 Not Implemented\nContent-Length: 0\nConnection: close\n\n"; + char *message = calloc(128, sizeof(char)); + sprintf(message, "HTTP/1.1 501 Not Implemented\nContent-Length: 0\nConnection: close\n\n"); return returnRequest(socket, message, 501, ssl); } + +int return505Request(int socket, SSL *ssl) { + char *message = calloc(128, sizeof(char)); + sprintf(message, "HTTP/1.1 505 HTTP Version Not Supported\nContent-Length: 0\nConnection: close\n\n"); + return returnRequest(socket, message, 505, ssl); +} diff --git a/src/utils.c b/src/utils.c index 2c98c2b..ef2f906 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,5 +1,12 @@ #include #include +#include + +#define NUM_SUPPORTED_VERSIONS 2 +static char supportedHTTPVersions[NUM_SUPPORTED_VERSIONS][16] = { + "HTTP/1.1", + "HTTP/1.0" + }; int PrintLog(unsigned char *message) { time_t UTCTime; @@ -11,3 +18,16 @@ int PrintLog(unsigned char *message) { (now->tm_year + 1900), now->tm_hour, now->tm_min, now->tm_sec, message); return 0; } + +int checkHTTPVersion(char *version) { + int supported = -1; // Default fail state + char testVer[16]; + strcpy(testVer, version); + //testVer[strlen(version) - 1] = '\0'; // Removing + for (int i = 0; i < NUM_SUPPORTED_VERSIONS; i++) { + if (!strcmp(testVer, supportedHTTPVersions[i])) { + supported = 0; + } + } + return supported; +}