diff options
author | kartofen <mladenovnasko0@gmail.com> | 2023-01-28 00:36:27 +0200 |
---|---|---|
committer | kartofen <mladenovnasko0@gmail.com> | 2023-01-28 00:36:27 +0200 |
commit | 9ac597d24336586421d994a8b1201aa3f0127827 (patch) | |
tree | c228f5902ba1c8d8a67b4ee7027806b4a8e2974e | |
parent | e0a039ad08fc16773cee43e68f92b632a4540bce (diff) |
now using libmagic and its optional
-rw-r--r-- | README.md | 15 | ||||
-rwxr-xr-x | build.sh | 69 | ||||
-rw-r--r-- | files/index.html | 4 | ||||
-rw-r--r-- | src/main.c | 121 |
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. @@ -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> @@ -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) |