aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkartofen <mladenovnasko0@gmail.com>2023-01-28 00:36:27 +0200
committerkartofen <mladenovnasko0@gmail.com>2023-01-28 00:36:27 +0200
commit9ac597d24336586421d994a8b1201aa3f0127827 (patch)
treec228f5902ba1c8d8a67b4ee7027806b4a8e2974e
parente0a039ad08fc16773cee43e68f92b632a4540bce (diff)
now using libmagic and its optional
-rw-r--r--README.md15
-rwxr-xr-xbuild.sh69
-rw-r--r--files/index.html4
-rw-r--r--src/main.c121
4 files changed, 150 insertions, 59 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b2438cc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+### Web Server
+
+This is a simple http web server written in C with minimal dependencies.
+It uses the unix socket api and optionally libmagic for file information.
+
+### Build
+
+I don't like Makefile, so I wrote a shell script that compiles the program.
+Use `./build.sh help` to learn more about the build options and `./build.sh run` just to run the program
+
+### Limitation
+
+I have implemented only the GET request method, which just send the requested
+file from the `files` directory. There also isn't a hash table for the headers
+because I am lazy and it will make the project more unnessairly complex.
diff --git a/build.sh b/build.sh
index 370c378..6b2713c 100755
--- a/build.sh
+++ b/build.sh
@@ -3,20 +3,53 @@
cd ${0%/*} # go to project root
NAME="web-server"
-FLAGS="-std=c99 -Wall -Wextra -g -pedantic -D_POSIX_C_SOURCE=200112L"
+FLAGS="-std=c99 -Wall -Wextra -g -pedantic"
+
SRCD="src"
ODIR="obj"
BIN="bin"
FILES="files"
RUN=0
+LIBS=("magic")
+INC_LIBS=
+INC_MACROS="-D_POSIX_C_SOURCE=200112L -DFILES=\"$FILES\" "
+
+function add_libs {
+ for lib in ${LIBS[@]}; do
+ INC_LIBS+="-l$lib "
+ INC_MACROS+="-DUSE_LIB${lib^^} "
+ done
+}
+
+function __help__ {
+ echo "Usage:"
+ echo " ./build.sh [command | option] ..."
+ echo ""
+ echo "Options:"
+ echo " -(library) add the libarary into the list of libraries"
+ echo ""
+ echo "Commands:"
+ echo " help print this text"
+ echo " run will run the program after all commands are interpreted"
+ echo " valgrind use valgrind when running"
+ echo " -, nolib clear the list of libraries that are going to be loaded"
+ echo " clean will clean the temporary folders like 'obj' or 'bin'"
+ echo ""
+ echo "Note: Each command and option is interpreted in order"
+ exit 0
+}
+
+function __nolib__ {
+ LIBS=()
+}
+
function __run__ {
RUN=1
}
-function __leak__ {
+function __valgrind__ {
VALGRND="valgrind --leak-check=full --show-leak-kinds=all -s"
- RUN=1
}
function __clean__ {
@@ -25,23 +58,37 @@ function __clean__ {
exit 0
}
+
+if ! [[ $# -eq 0 ]]
+then
+ for cmd in "$@"; do
+ if [ $(cut -c1-1 <<< $cmd) == "-" ]; then
+ if [ $(expr length $cmd) == 1 ]; then
+ __nolib__
+ else
+ LIBS+=("$(cut -c2- <<< $cmd)")
+ fi
+ else
+ __"$cmd"__
+ fi
+ done
+fi
+
set -xe
mkdir -p $BIN
mkdir -p $ODIR
mkdir -p $FILES
-if ! { [[ $# -eq 0 ]]; } 2> /dev/null
-then
- __$1__
-fi
+{ add_libs; } 2> /dev/null
-gcc -c $SRCD/server.c -o $ODIR/server.o $FLAGS
-gcc -c $SRCD/main.c -o $ODIR/main.o $FLAGS -DFILES=\"$FILES\"
-
-gcc -o $BIN/$NAME $ODIR/main.o $ODIR/server.o $FLAGS
+gcc -c $SRCD/server.c -o $ODIR/server.o $FLAGS $INC_MACROS
+gcc -c $SRCD/main.c -o $ODIR/main.o $FLAGS $INC_MACROS
+gcc -o $BIN/$NAME $ODIR/main.o $ODIR/server.o $FLAGS $INC_MACROS $INC_LIBS
if ! { [[ $RUN -eq 0 ]]; } 2> /dev/null
then
$VALGRND $BIN/$NAME
fi
+
+exit 0
diff --git a/files/index.html b/files/index.html
index b94ee21..5a0ce18 100644
--- a/files/index.html
+++ b/files/index.html
@@ -5,8 +5,8 @@
<link rel="icon" href="/favicon.png">
</head>
<body>
- <h1>Server</h1>
- <p>This is a paragraph</p>
+ <h1>Web Server Written in C</h1>
+ <p>Please don't hack</p>
<a href="/test/test1.html">Test 1</a>
</body>
</html>
diff --git a/src/main.c b/src/main.c
index 9420c47..8817b83 100644
--- a/src/main.c
+++ b/src/main.c
@@ -13,7 +13,7 @@
#define PORT "8079"
#define RM_LF(str) do { \
- signed long len = strlen(str)-1; \
+ signed long len = strlen(str)-1; \
if(len >= 0 && str[len] == '\n') str[len] = '\0'; \
} while(0)
@@ -35,58 +35,60 @@
SEND_BUF_ADD("\r\n"); \
} while(0)
-#define LOAD_FILE() do { \
- if(is_binary) { \
- fseek(fp, 0, SEEK_END); \
- size_t file_sz = ftell(fp); \
- rewind(fp); \
- \
- fread(&send_buf[*send_buf_sz], sizeof(char), file_sz, fp); \
- *send_buf_sz += file_sz; \
- } else { \
- char line[BUF_CAP]; \
- while(fgets(line, sizeof(line), fp) != NULL) \
- { \
- RM_LF(line); \
- SEND_BUF_ADD_LINE(line); \
- } \
- } \
+#define SEND_BUF_ADD_LINE_LONG(lon) do { \
+ char str[COMMON_CAP]; \
+ sprintf(str, "%ld", lon); \
+ SEND_BUF_ADD_LINE(str); \
} while(0)
+#ifdef USE_LIBMAGIC
+#include <magic.h>
static int get_content_type(char *file_path, char *content_type)
{
- char cmd[COMMON_CAP + 64];
- sprintf(cmd, "/usr/bin/file -ib %s", file_path);
+ int ret = 1;
+ magic_t magic;
- FILE *fp = popen(cmd, "r");
- if(!fp) {
- err("popen: cmd %s: %s", cmd, strerror(errno));
- return 1;
+ if((magic = magic_open(MAGIC_MIME)) == NULL) {
+ err("magic_open: %s", magic_error(magic));
+ return ret;
}
- if(fgets(content_type, COMMON_CAP, fp) == NULL) {
- err("get_mime_type: no output from fgets");
- return 1;
+ if(magic_load(magic, NULL) != 0) {
+ err("magic_load: %s", magic_error(magic));
+ goto close;
}
- pclose(fp);
- RM_LF(content_type);
-
- char ct_cpy[COMMON_CAP];
- strcpy(ct_cpy, content_type);
- strtok(ct_cpy, "=");
-
- // the only thing after the '=' should be the either binary or something else (text)
- char *charset = strtok(NULL, "=");
- if(charset == NULL || strcmp(charset, "binary") != 0) {
- return 0;
+ const char *mime = magic_file(magic, file_path);
+ if(mime == NULL) {
+ err("magic_file: %s", magic_error(magic));
+ goto close;
}
- return -1;
+ memcpy(content_type, mime, strlen(mime));
+
+ ret = 0;
+close:
+ magic_close(magic);
+ return ret;
+}
+#else
+static int get_content_type(char *file_path, char *content_type)
+{
+ (void)file_path;
+ (void)content_type;
+ return 0;
}
+#endif
static int try_file(char *req_path, FILE **fp, char *content_type)
{
+ for(size_t i = 1; i < strlen(req_path); i++) {
+ if(req_path[i-1] == '.' && req_path[i] == '.') {
+ err("try_file: the requested path %s includes ..", req_path);
+ return 1;
+ }
+ }
+
char file_path[COMMON_CAP];
sprintf(file_path, "%s%s", FILES,
(strlen(req_path) == 1) ? "/index.html" : req_path);
@@ -96,24 +98,31 @@ static int try_file(char *req_path, FILE **fp, char *content_type)
err("fopen: file %s: %s", file_path, strerror(errno));
return 1;
}
+
info("fopen: file %s was opened", file_path);
- return get_content_type(file_path, content_type);
+ if(get_content_type(file_path, content_type) != 0) {
+ err("get_content_type: failed");
+ fclose(*fp);
+ return 1;
+ }
+
+ return 0;
}
static int on_get(char *send_buf, ssize_t *send_buf_sz, char *req_path)
{
- char content_type[COMMON_CAP];
- int is_binary; // 0 - no; -1 - yes; 1 - error
+ int ret = 1;
+ char content_type[COMMON_CAP] = {0};
FILE *fp;
- if((is_binary = try_file(req_path, &fp, content_type)) == 1) {
+ if(try_file(req_path, &fp, content_type) != 0) {
if(errno == 2) { // 2 is 'No such file or directory'
SEND_BUF_ADD_LINE("404");
- if((is_binary = try_file("/404.html", &fp, content_type)) != 0) return 1;
+ if(try_file("/404.html", &fp, content_type) != 0) return ret;
} else {
SEND_BUF_ADD_LINE("500");
- if((is_binary = try_file("/500.html", &fp, content_type)) != 0) return 1;
+ if(try_file("/500.html", &fp, content_type) != 0) return ret;
}
} else {
SEND_BUF_ADD_LINE("200");
@@ -122,11 +131,31 @@ static int on_get(char *send_buf, ssize_t *send_buf_sz, char *req_path)
SEND_BUF_ADD_LINE("Server: potato");
SEND_BUF_ADD("Content-Type: ");
SEND_BUF_ADD_LINE(content_type);
+
+ if(fseek(fp, 0, SEEK_END) != 0) {
+ err("fseek: %s", strerror(errno));
+ goto close;
+ }
+
+ size_t file_sz = ftell(fp);
+ if(file_sz == 0) {
+ err("ftell: %s", strerror(errno));
+ goto close;
+ }
+
+ rewind(fp);
+
+ SEND_BUF_ADD("Content-Lenght: ");
+ SEND_BUF_ADD_LINE_LONG(file_sz);
SEND_BUF_ADD("\r\n");
- LOAD_FILE();
+ fread(&send_buf[*send_buf_sz], sizeof(char), file_sz, fp);
+ *send_buf_sz += file_sz;
+
+ ret = 0;
+close:
fclose(fp);
- return 0;
+ return ret;
}
static int handle_connection(sock_t *conn)