moved functions around

This commit is contained in:
Pin
2022-03-06 23:08:18 -05:00
parent fc4af96282
commit de1bac616b
12 changed files with 343 additions and 262 deletions

View File

@@ -16,6 +16,7 @@ COPY entrypoint.sh /
COPY content/ /var/www/html
RUN mkdir -p /var/log/seaweb
RUN touch /var/log/seaweb/log
WORKDIR /src/

View File

@@ -42,5 +42,8 @@ dockerBuild: output_dir
dockerTestDeploy: dockerBuild
docker run -p8080:8080 --rm -d seaweb:latest
dockerReleaseDeploy: dockerBuild
docker run -p8080:8080 --rm -d seaweb:latest --cert /src/certs/cert.pem --privkey /src/certs/key.pem
killTestDocker:
docker stop -t 0 `docker ps | grep "seaweb:latest" | tail -n 1 | tr -s " " | cut -d " " -f 1`

30
README.md Normal file
View File

@@ -0,0 +1,30 @@
# SeaWeb
## Running
It is recommended to run this application using Docker.
For a deployment running the application without HTTPS run `make dockerTestDeploy`, this will open a socket on port 8080.
For a deployment running the application with HTTPS run `make dockerReleaseDeploy`, this will also open a socket on port 8080.
Running `make killTestDocker` will kill the running instance of seaweb:latest running in Docker.
To view logs within the running container run `docker exec -it $(docker ps | grep "seaweb:latest" | grep -o '[^ ]*$') tail -f /var/log/seaweb/log`, or manually view the `/var/log/seaweb/log` file.
If you run this application without Docker please ensure the OpenSSL development headers are installed onto your system, this will be important if running with HTTPS.
To build this application outside of Docker run `make`, and the binary will be build into `./bin`.
Running `./bin/seaweb --help` or `./bin/seaweb -h` will show you the help message for the application showing all possible options.
By default the application is compiled to utilize `/var/www/html` as the WEB root, and will output logs to `/var/log/seaweb/log`; if running the application outside of Docker please ensure this is setup with correct permissions to run.
## Notes
- All new connections will spawn a child process which is used to deal with the request
- If the process takes longer than 5 seconds to resolve the request, the child process will be killed
- php-cgi seems to randomly return empty data from scripts without throwing an error message (these are caught and a HTTP 500 response is return)
- If the request is made again it will eventually return correct data (this seems to be an issue with php-cgi and not this application)
- Running `make genCerts` will generate a root authority and generate certificates to utilize for the web server
- Certificate authority related files start with `ca` while the certificates which should be utilized for the web server omit the `ca`
- Making a request to the web server which does not match the current protocol (e.g. making a https request when it is server http) will result in the server ignoring the request
- Returning an error here is not possible since if we have a none SSL socket created we cannot respond to a SSL request
- This will cause a fork of the process to spawn which will be killed in 5 seconds after the timeout is met
- If a request to the root directory is made, `/`, it is assumed that the user was trying to navigate to `/index.html` and the request will be treated as such

View File

@@ -15,6 +15,7 @@
#include "returnRequest.h"
#include "server.h"
#include "utils.h"
#include "requestHandlers.h"
#define WEB_ROOT "/var/www/html/"
//#define WEB_ROOT "content/"
@@ -22,6 +23,7 @@
static int verbose_flag = 0;
bool enableHTTPS = 0;
char *WEB_ROOT_DIR = NULL;
static int timeout = 0;
@@ -32,91 +34,6 @@ int printDebug(char message[]) {
return 0;
}
char *php_cgi(char *sPath, struct HTTPRequest *r) {
int phpPipe[2];
char *buf = NULL;
size_t bufLen = 1024;
buf = malloc(bufLen);
pipe(phpPipe);
pid_t pid;
int status = 0;
pid = fork();
if (pid == 0) { // Child fork
char script[500];
char *queryString = NULL;
close(phpPipe[0]);
dup2(phpPipe[1], STDOUT_FILENO);
strcpy(script, "SCRIPT_FILENAME=");
strcat(script, WEB_ROOT);
strcat(script, sPath);
putenv("GATEWAY_INTERFACE=CGI/1.1");
putenv(script);
putenv("REDIRECT_STATUS=true");
putenv("SERVER_PROTOCOL=HTTP/1.1");
putenv("REMOTE_HOST=127.0.0.1");
if (!strcmp(r->requestType, "POST")) {
putenv("REQUEST_METHOD=POST");
char conLenString[256];
sprintf(conLenString, "CONTENT_LENGTH=%zu", r->requestBodyLen);
putenv(conLenString);
putenv("CONTENT_TYPE=application/x-www-form-urlencoded");
queryString = malloc(r->requestBodyLen + 24);
sprintf(queryString, "QUERY_STRING=%s", r->requestBody);
putenv(queryString);
// Starting fork to pipe stdin into php-cgi
int phpPipe2[2];
pipe(phpPipe2);
pid_t pid2;
pid2 = fork();
if (pid2 == 0) { // Child fork
close(phpPipe2[0]);
dup2(phpPipe2[1], STDOUT_FILENO);
printf("%s", r->requestBody);
close(phpPipe2[1]);
exit(EXIT_SUCCESS);
} else if (pid < 0) { // Error forking
printDebug("Error in stdin php frok");
} else { // Parent fork
close(phpPipe[1]);
dup2(phpPipe2[0], STDIN_FILENO);
execlp("/usr/bin/php-cgi", "php-cgi", NULL);
}
} else {
queryString = malloc(strlen(r->requestVars) + 24);
sprintf(queryString, "QUERY_STRING=%s", r->requestVars);
putenv(queryString);
putenv("REQUEST_METHOD=GET");
execl("/usr/bin/php-cgi", "php-cgi", NULL);
}
} else if (pid < 0) { // Error forking
printDebug("Error forking php exec");
} else { // Parent fork
size_t buffCount;
close(phpPipe[1]);
do {
buffCount = read(phpPipe[0], buf, BUFF_READ);
if (strlen(buf) == bufLen) {
bufLen *= 2;
buf = realloc(buf, bufLen);
}
} while (buffCount == 0);
do {
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
close(phpPipe[0]);
if (status != 0) {
return NULL;
}
return buf;
}
int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
char temp[1]; // Used to check newlines
char *token = calloc(8, sizeof(char));
@@ -219,171 +136,6 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
return 0;
}
int handleGetRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
char errResponse[256];
char ch;
size_t size = 8;
int i = 0;
unsigned char *fileContent = calloc(size, sizeof(unsigned char));
FILE *fp;
char *workingReqDir;
char *phpReturn = NULL;
char phpEnd[5] = ".php";
bool endsWithPhp = false;
if (strlen(phpEnd) <= strlen(r->requestDir)) {
for (int i = 0; i < strlen(phpEnd); i++) {
if (r->requestDir[i + strlen(r->requestDir) - strlen(phpEnd)] != phpEnd[i]) {
endsWithPhp = false;
break;
}
endsWithPhp = true;
}
}
if (endsWithPhp) {
phpReturn = php_cgi(r->requestDir, r);
if (phpReturn != NULL) {
return returnPHPRequest(socket, phpReturn, ssl);
} else {
return return500Request(socket, ssl);
}
}
// If requesting root directory change to index.html
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
char *reqDir = calloc((strlen(WEB_ROOT) + strlen(workingReqDir) + 1), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT, workingReqDir);
fp = fopen(reqDir, "r");
if (fp == NULL) {
sprintf(errResponse, "Error opening file: %s", workingReqDir);
printDebug(errResponse);
return404Request(socket, ssl);
return -1;
}
while((ch = fgetc(fp)) != EOF) {
if (i == size) {
size *= 2;
fileContent = realloc(fileContent, size);
}
fileContent[i] = ch;
i++;
}
return200Request(socket, fileContent, ssl);
fclose(fp);
free(reqDir);
free(fileContent);
return 0;
}
int handlePUTRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
FILE *fp;
char *reqDir;
char * workingReqDir;
char errResponse[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);
fp = fopen(reqDir, "w");
if (fp == NULL) {
sprintf(errResponse, "Error opening file: %s", workingReqDir);
printDebug(errResponse);
return return404Request(socket, ssl);
}
fprintf(fp, "%s", r->requestBody);
free(reqDir);
fclose(fp);
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) {
char *phpReturn = NULL;
// Supported data types application/x-www-form-urlencoded application/json
if (r->requestConLen == NULL) { // Return 411 if length var not specified
printDebug("Content Length was not sent in POST request");
return return411Request(socket, ssl);
}
if (r->requestConType != NULL) {
if (!strcmp(r->requestConType, "application/x-www-form-utlencoded") == 0) {
printDebug("Application From Selected");
} else {
printDebug("Content will likely get parsed wrong");
}
}
char phpEnd[5] = ".php";
bool endsWithPhp = false;
if (strlen(phpEnd) <= strlen(r->requestDir)) {
for (int i = 0; i < strlen(phpEnd); i++) {
if (r->requestDir[i + strlen(r->requestDir) - strlen(phpEnd)] != phpEnd[i]) {
endsWithPhp = false;
break;
}
endsWithPhp = true;
}
}
if (endsWithPhp) {
phpReturn = php_cgi(r->requestDir, r);
if (phpReturn != NULL) {
return returnPHPRequest(socket, phpReturn, ssl);
}
} else {
return return200Request(socket, NULL, ssl);
}
return return500Request(socket, ssl);
}
int handleRequest(unsigned char buffer[], int socket, SSL *ssl) {
struct HTTPRequest r; // Holds relevant HTTP request information
r.requestConLen = malloc(sizeof(char));
@@ -453,6 +205,10 @@ int main(int argc, char **argv) {
size_t bufSize = BUFF_READ;
unsigned char *buffer = calloc(bufSize, sizeof(unsigned char));
// Copy WEB_ROOT into extern value
WEB_ROOT_DIR = malloc(strlen(WEB_ROOT));
strcpy(WEB_ROOT_DIR, WEB_ROOT);
// Setting up options
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
@@ -625,6 +381,7 @@ int main(int argc, char **argv) {
free(certFile);
free(listenAddr);
free(buffer);
free(WEB_ROOT_DIR);
close(server_fd);
exit(EXIT_SUCCESS);
}

View File

@@ -2,6 +2,4 @@
set -e
echo "$@"
exec seaweb $@
exec seaweb "$@"

View File

@@ -13,3 +13,4 @@ struct HTTPRequest {
unsigned char *requestBody;
size_t requestBodyLen;
};

View File

@@ -0,0 +1,6 @@
int handleGetRequest(int socket, struct HTTPRequest *r, SSL *ssl);
int handlePUTRequest(int socket, struct HTTPRequest *r, SSL *ssl);
int handleDELETERequest(int socket, struct HTTPRequest *r, SSL *ssl);
int handlePOSTRequest(int socket, struct HTTPRequest *r, SSL *ssl);

View File

@@ -1,4 +1,7 @@
#include <stdbool.h>
extern bool enableHTTPS;
int printDebug(char message[]);
extern bool enableHTTPS;
extern char *WEB_ROOT_DIR;

View File

@@ -1,3 +1,5 @@
int PrintLog(unsigned char *message);
int checkHTTPVersion(char *version);
char *php_cgi(char *sPath, struct HTTPRequest *r);

177
src/requestHandlers.c Normal file
View File

@@ -0,0 +1,177 @@
//#include <unistd.h>
//#include <stdio.h>
//#include <stdlib.h>
#include <string.h>
#include <openssl/ssl.h>
#include <errno.h>
#include "httpStruct.h"
#include "returnRequest.h"
#include "server.h"
#include "utils.h"
int handleGetRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
char errResponse[256];
char ch;
size_t size = 8;
int i = 0;
unsigned char *fileContent = calloc(size, sizeof(unsigned char));
FILE *fp;
char *workingReqDir;
char *phpReturn = NULL;
char phpEnd[5] = ".php";
bool endsWithPhp = false;
if (strlen(phpEnd) <= strlen(r->requestDir)) {
for (int i = 0; i < strlen(phpEnd); i++) {
if (r->requestDir[i + strlen(r->requestDir) - strlen(phpEnd)] != phpEnd[i]) {
endsWithPhp = false;
break;
}
endsWithPhp = true;
}
}
if (endsWithPhp) {
phpReturn = php_cgi(r->requestDir, r);
if (phpReturn != NULL) {
return returnPHPRequest(socket, phpReturn, ssl);
} else {
return return500Request(socket, ssl);
}
}
// If requesting root directory change to index.html
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
char *reqDir = calloc((strlen(WEB_ROOT_DIR) + strlen(workingReqDir) + 1), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT_DIR, workingReqDir);
fp = fopen(reqDir, "r");
if (fp == NULL) {
sprintf(errResponse, "Error opening file: %s", workingReqDir);
printDebug(errResponse);
return404Request(socket, ssl);
return -1;
}
while((ch = fgetc(fp)) != EOF) {
if (i == size) {
size *= 2;
fileContent = realloc(fileContent, size);
}
fileContent[i] = ch;
i++;
}
return200Request(socket, fileContent, ssl);
fclose(fp);
free(reqDir);
free(fileContent);
return 0;
}
int handlePUTRequest(int socket, struct HTTPRequest *r, SSL *ssl) {
FILE *fp;
char *reqDir;
char * workingReqDir;
char errResponse[256];
if (!strcmp(r->requestDir, "/")) {
workingReqDir = "index.html";
} else {
workingReqDir = r->requestDir;
}
reqDir = calloc(strlen(WEB_ROOT_DIR) + strlen(workingReqDir), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT_DIR, workingReqDir);
fp = fopen(reqDir, "w");
if (fp == NULL) {
sprintf(errResponse, "Error opening file: %s", workingReqDir);
printDebug(errResponse);
return return404Request(socket, ssl);
}
fprintf(fp, "%s", r->requestBody);
free(reqDir);
fclose(fp);
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_DIR) + strlen(workingReqDir), sizeof(char));
sprintf(reqDir, "%s%s", WEB_ROOT_DIR, 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) {
char *phpReturn = NULL;
// Supported data types application/x-www-form-urlencoded application/json
if (r->requestConLen == NULL) { // Return 411 if length var not specified
printDebug("Content Length was not sent in POST request");
return return411Request(socket, ssl);
}
if (r->requestConType != NULL) {
if (!strcmp(r->requestConType, "application/x-www-form-utlencoded") == 0) {
printDebug("Application From Selected");
} else {
printDebug("Content will likely get parsed wrong");
}
}
char phpEnd[5] = ".php";
bool endsWithPhp = false;
if (strlen(phpEnd) <= strlen(r->requestDir)) {
for (int i = 0; i < strlen(phpEnd); i++) {
if (r->requestDir[i + strlen(r->requestDir) - strlen(phpEnd)] != phpEnd[i]) {
endsWithPhp = false;
break;
}
endsWithPhp = true;
}
}
if (endsWithPhp) {
phpReturn = php_cgi(r->requestDir, r);
if (phpReturn != NULL) {
return returnPHPRequest(socket, phpReturn, ssl);
}
} else {
return return200Request(socket, NULL, ssl);
}
return return500Request(socket, ssl);
}

View File

@@ -39,6 +39,12 @@ int return201Request(int socket, char *content, SSL *ssl) {
return returnRequest(socket, message, 201, ssl);
}
int return400Request(int socket, SSL *ssl) {
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 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");
@@ -47,16 +53,10 @@ int return403Request(int socket, SSL *ssl) {
int return404Request(int socket, SSL *ssl) {
char *message = calloc(128, sizeof(char));
sprintf(message, "HTTP/1.1 404 Not Found\nContent-Length: 12\nConnection: close\n\n404 Request\n\n");
sprintf(message, "HTTP/1.1 404 Not Found\nContent-Length: 13\nConnection: close\n\n404 Request\n\n");
return returnRequest(socket, message, 404, ssl);
}
int return400Request(int socket, SSL *ssl) {
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 return411Request(int socket, SSL *ssl) {
char *message = calloc(128, sizeof(char));
sprintf(message, "HTTP/1.1 411 Length Required\nContent-Length: 0\nConnection: close\n\n");

View File

@@ -1,6 +1,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include "server.h"
#include "httpStruct.h"
#define BUFF_READ 1024
#define NUM_SUPPORTED_VERSIONS 2
static char supportedHTTPVersions[NUM_SUPPORTED_VERSIONS][16] = {
@@ -14,8 +22,17 @@ int PrintLog(unsigned char *message) {
time(&UTCTime);
struct tm *now = localtime(&UTCTime);
printf("[Log] %02d/%02d/%d %02d:%02d:%02d - %s\n", (now->tm_mon + 1), now->tm_mday,
if (!strcmp(WEB_ROOT_DIR, "/var/www/html/")) {
FILE *fp;
fp = fopen("/var/log/seaweb/log", "w");
fprintf(fp, "[Log] %02d/%02d/%d %02d:%02d:%02d - %s\n", (now->tm_mon + 1), now->tm_mday,
(now->tm_year + 1900), now->tm_hour, now->tm_min, now->tm_sec, message);
fclose(fp);
} else {
printf("[Log] %02d/%02d/%d %02d:%02d:%02d - %s\n", (now->tm_mon + 1), now->tm_mday,
(now->tm_year + 1900), now->tm_hour, now->tm_min, now->tm_sec, message);
}
return 0;
}
@@ -30,3 +47,89 @@ int checkHTTPVersion(char *version) {
}
return supported;
}
char *php_cgi(char *sPath, struct HTTPRequest *r) {
int phpPipe[2];
char *buf = NULL;
size_t bufLen = 1024;
buf = malloc(bufLen);
pipe(phpPipe);
pid_t pid;
int status = 0;
pid = fork();
if (pid == 0) { // Child fork
char script[500];
char *queryString = NULL;
close(phpPipe[0]);
dup2(phpPipe[1], STDOUT_FILENO);
strcpy(script, "SCRIPT_FILENAME=");
strcat(script, WEB_ROOT_DIR);
strcat(script, sPath);
putenv("GATEWAY_INTERFACE=CGI/1.1");
putenv(script);
putenv("REDIRECT_STATUS=true");
putenv("SERVER_PROTOCOL=HTTP/1.1");
putenv("REMOTE_HOST=127.0.0.1");
if (!strcmp(r->requestType, "POST")) {
putenv("REQUEST_METHOD=POST");
char conLenString[256];
sprintf(conLenString, "CONTENT_LENGTH=%zu", r->requestBodyLen);
putenv(conLenString);
putenv("CONTENT_TYPE=application/x-www-form-urlencoded");
queryString = malloc(r->requestBodyLen + 24);
sprintf(queryString, "QUERY_STRING=%s", r->requestBody);
putenv(queryString);
// Starting fork to pipe stdin into php-cgi
int phpPipe2[2];
pipe(phpPipe2);
pid_t pid2;
pid2 = fork();
if (pid2 == 0) { // Child fork
close(phpPipe2[0]);
dup2(phpPipe2[1], STDOUT_FILENO);
printf("%s", r->requestBody);
close(phpPipe2[1]);
exit(EXIT_SUCCESS);
} else if (pid < 0) { // Error forking
printDebug("Error in stdin php frok");
} else { // Parent fork
close(phpPipe[1]);
dup2(phpPipe2[0], STDIN_FILENO);
execlp("/usr/bin/php-cgi", "php-cgi", NULL);
}
} else {
queryString = malloc(strlen(r->requestVars) + 24);
sprintf(queryString, "QUERY_STRING=%s", r->requestVars);
putenv(queryString);
putenv("REQUEST_METHOD=GET");
execl("/usr/bin/php-cgi", "php-cgi", NULL);
}
} else if (pid < 0) { // Error forking
printDebug("Error forking php exec");
} else { // Parent fork
size_t buffCount;
close(phpPipe[1]);
do {
buffCount = read(phpPipe[0], buf, BUFF_READ);
if (strlen(buf) == bufLen) {
bufLen *= 2;
buf = realloc(buf, bufLen);
}
} while (buffCount == 0);
do {
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
close(phpPipe[0]);
if (status != 0) {
return NULL;
}
return buf;
}