Files
SeaWeb/cmd/server.c
2022-02-21 01:52:03 -05:00

299 lines
7.4 KiB
C

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <openssl/ssl.h>
// Local Includes
#include "httpStruct.h"
#include "socketHelp.h"
#include "returnRequest.h"
#include "server.h"
#define WEB_ROOT "content/"
static int verbose_flag = 0;
bool enableHTTPS = 0;
int printDebug(char message[]) {
if (verbose_flag == 1) {
printf("[Debug] %s\n", message);
}
return 0;
}
int parseHTTPRequest(unsigned char *buffer, struct HTTPRequest *r) {
char temp[1]; // Used to check newlines
char *token = calloc(8, sizeof(char));
int line = 0;
char *checkLine = calloc(1000, sizeof(char));
for (int i = 0; i < strlen((char *)buffer); i++) {
temp[0] = buffer[i];
if ((!strcmp(temp, "\n")) && (i != 0)) {
// Config Check
if (line == 0) {
token = strtok(checkLine, " ");
// HTTP Request Type
if (!strcmp(token, "GET")) {
// Grabbing HTTP Request Type
r->requestType = malloc(strlen(token));
strcpy(r->requestType, token);
// Grabbing HTTP Request Dir
token = strtok(NULL, " ");
r->requestDir = malloc(strlen(token));
strcpy(r->requestDir, token);
// Grabbing HTTP Request Version
token = strtok(NULL, "");
r->requestVersion = malloc(strlen(token));
strcpy(r->requestVersion, token);
} else if (!strcmp(token, "POST")) {
printDebug("POST RECEIVED");
return 2;
} else {
return 2;
}
} else {
token = strtok(checkLine, ":");
// Host Check
if (!strcmp(token, "Host")) {
token = strtok(NULL, "");
r->requestHost = malloc(strlen(token));
strcpy(r->requestHost, token);
}
// Reset checkline
}
if (strlen(checkLine) > 0) {
// Clear checkLine
memset(checkLine,0,strlen(checkLine));
}
line++;
} else {
strcat(checkLine, temp);
}
}
if (strlen(r->requestType) == 0) {
return -1;
}
free(checkLine);
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), 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 handleRequest(unsigned char buffer[], int socket, SSL *ssl) {
struct HTTPRequest r; // Holds relevant HTTP request information
int checkerr = 0;
printDebug("Received Request");
// Grabbing relevant information out of request
checkerr = parseHTTPRequest(buffer, &r);
if (checkerr != 0) { // Checking for HTTP parsing error
if (checkerr == -1) {
printDebug("Error reading request, returning empty 404");
} else {
printDebug("Error parsing, returning 500");
// return return500Request(socket, ssl);
}
return return400Request(socket, ssl);
}
if (!strcmp(r.requestType, "GET")) {
handleGetRequest(socket, &r, ssl);
return 0;
}
// Return response to socket
return200Request(socket, NULL ,ssl);
return 0;
}
int main(int argc, char **argv) {
struct sockaddr_in address;
int server_fd, new_socket;
int port = 0; // Define what port server listens on
int checkerr = 0; // Used for error checking
int addrlen = sizeof(address);
char *certFile = malloc(sizeof(char));
char *privKeyFile = malloc(sizeof(char));
certFile[0] = '\0';
privKeyFile[0] = '\0';
uint32_t listenAddrNum = -1;
char *listenAddr = malloc(sizeof(char));
listenAddr[0] = '\0';
SSL_CTX *ctx = NULL;
unsigned char *buffer = calloc(8096, sizeof(unsigned char));
// Setting up options
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"port", required_argument, NULL, 'p'},
{"listen", required_argument, NULL, 'l'},
{"cert", required_argument, NULL, 'c'},
{"privkey", required_argument, NULL, 'k'},
{"verbose", no_argument, &verbose_flag, 1},
{0, 0, 0, 0}
};
const char* usage =
"Usage: seaweb [options]\n\n"
" -h --help\t\t\tShows this message\n"
" -p --port\t\t\tStarts webserver on passed port\n"
" -l --listen\t\t\tDefines what addr to listen on (default 0.0.0.0)\n"
" -c --cert\t\t\tPath to certificate\n"
" -k --privkey\t\t\tPath to private key\n"
"\n"
"\n"
" --verbose\t\t\tPrints debug messages\n"
"\n";
int c;
while (1) {
int option_index = 0;
c = getopt_long(argc, argv, "hp:l:c:k:", long_options, &option_index);
if(c == -1) { // Break if no more options are present to parse
break;
}
switch(c) {
case 'h':
printf("%s", usage);
exit(EXIT_SUCCESS);
case 'p':
sscanf(optarg, "%d", &port);
break;
case 'l':
listenAddr = calloc(strlen(optarg), sizeof(char));
strcpy(listenAddr, optarg);
listenAddrNum = addr2sin_addr(listenAddr);
if (listenAddrNum == -1) {
exit(EXIT_FAILURE);
}
break;
case 'c':
certFile = calloc(strlen(optarg), sizeof(char));
strcpy(certFile, optarg);
printf("Cert: %s\n", certFile);
break;
case 'k':
privKeyFile = calloc(strlen(optarg), sizeof(char));
strcpy(privKeyFile, optarg);
printf("Priv: %s\n", privKeyFile);
break;
}
}
// Argument checks
if (port == 0) { // Setting default port if none is passed
port = 8080;
}
if (!((strlen(certFile) > 0) == (strlen(privKeyFile) > 0))) { // XNOR of string lengths
// Checking what flag was not passed
if (strlen(certFile)) { // Privkey not passed
printf("If certificate file is specified, a private key files needs to be specified\n");
} else if (strlen(privKeyFile)) { // Cert not passed
printf("If privkey file is specified, a certificate files needs to be specified\n");
}
printf("Exiting...\n");
exit(EXIT_FAILURE);
} else if ( (strlen(certFile) > 0) && (strlen(privKeyFile) > 0) ) { // Enabling HTTPS
enableHTTPS = 1;
}
if ( enableHTTPS == 1 ) {
printf("Opening secure socket on port: %d\n", port);
checkerr = createSecureSocket(port, &server_fd, &address, &addrlen, listenAddrNum,
&ctx, certFile, privKeyFile);
if ( ctx == NULL ) {
printf("Error creating ctx\n");
}
} else {
printf("Opening socket on port: %d\n", port);
checkerr = createSocket(port, &server_fd, &address, &addrlen, listenAddrNum);
}
if (checkerr != 0) {
perror("Error creating socket");
exit(EXIT_FAILURE);
}
// Handle incoming requests
while(1) {
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t *)&addrlen))<0) {
perror("Accept connection error");
exit(EXIT_FAILURE);
}
if ( enableHTTPS ) {
SSL *ssl;
ssl = SSL_new(ctx);
SSL_set_fd(ssl, new_socket);
SSL_accept(ssl);
SSL_read(ssl, buffer, 8096);
handleRequest(buffer, new_socket, ssl);
} else {
read(new_socket, buffer, 1024);
handleRequest(buffer, new_socket, NULL);
}
close(new_socket);
}
free(privKeyFile);
free(certFile);
free(listenAddr);
free(buffer);
close(server_fd);
exit(EXIT_SUCCESS);
}