diff --git a/404.html b/404.html new file mode 100644 index 0000000..6c51e5b --- /dev/null +++ b/404.html @@ -0,0 +1,10 @@ + + + + Not found + + +

Not found

+ The resource [path] was not found on server [host]. + + diff --git a/home/hello-world.sh b/home/hello-world.sh new file mode 100755 index 0000000..181e45a --- /dev/null +++ b/home/hello-world.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +statusContainer=$1 +eval "declare -A settings="${2#*=} +eval "declare -A headers="${3#*=} + +echo 200 > $statusContainer + +cat < + + + Hello World + + +

Hello World

+ + + +EOF diff --git a/home/info.sh b/home/info.sh new file mode 100755 index 0000000..5172c9f --- /dev/null +++ b/home/info.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +statusContainer=$1 +eval "declare -A settings="${2#*=} +eval "declare -A headers="${3#*=} + +echo 200 > $statusContainer + +cat < + + + Info + + +

Settings

+EOF +for key in "${!settings[@]}"; do + echo "$key -> ${settings[$key]}
" +done +cat <Headers +EOF +for key in "${!headers[@]}"; do + echo "$key -> ${headers[$key]}
" +done +cat << EOF + + + +EOF diff --git a/home/stuff/add.sh b/home/stuff/add.sh new file mode 100755 index 0000000..5360896 --- /dev/null +++ b/home/stuff/add.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +statusContainer=$1 +eval "declare -A settings="${2#*=} +eval "declare -A headers="${3#*=} + + +if test "${headers[query]}" == ""; then + echo 200 > $statusContainer + + cat< + + + Add to guestbook + + +

Add something to the guestbook

+
+
+
+ +
+ + +EOF + exit +fi + +name="" +text="" + +fields=$(echo ${headers[query]} | tr "&" "\n") +for field in $fields; do + key=$(echo $field | awk -F= '{ print $1 }') + value=$(echo $field | awk -F= '{for (i=2; i<=NF; i++) print $i}') + + if test "$key" = "name"; then + name=$value + elif test "$key" = "text"; then + text=$value + fi +done + +if test "$name" = "" -o "$text" = ""; then + echo 400 > $statusContainer + echo 400 - Bad Request + exit +fi + +cat >> ./guestbook.txt < $statusContainer +echo "Location: ./guestbook.txt" >> $statusContainer diff --git a/home/stuff/guestbook.txt b/home/stuff/guestbook.txt new file mode 100644 index 0000000..8b04d42 --- /dev/null +++ b/home/stuff/guestbook.txt @@ -0,0 +1,6 @@ + +=============================== +Sat Nov 18 01:20:41 CET 2017: overflow + +Welcome. : ) + diff --git a/home/test/cgi-test.c b/home/test/cgi-test.c new file mode 100644 index 0000000..4fbf4ef --- /dev/null +++ b/home/test/cgi-test.c @@ -0,0 +1,8 @@ +#include + +int main() { + + printf("Hello World"); + + return 0; +} diff --git a/home/test/cgi-test.cgi b/home/test/cgi-test.cgi new file mode 100755 index 0000000..f2e944b Binary files /dev/null and b/home/test/cgi-test.cgi differ diff --git a/home/test/non-executeable-script.sh b/home/test/non-executeable-script.sh new file mode 100644 index 0000000..a9f4e38 --- /dev/null +++ b/home/test/non-executeable-script.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo Hello World diff --git a/home/test/php-test.php b/home/test/php-test.php new file mode 100755 index 0000000..e542041 --- /dev/null +++ b/home/test/php-test.php @@ -0,0 +1,4 @@ +#!/usr/bin/php + $statusContainer +echo "Location: ../stuff/guestbook.txt" >> $statusContainer diff --git a/index.sh b/index.sh new file mode 100755 index 0000000..c6906aa --- /dev/null +++ b/index.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +server="$1" +host="$2" +rpath="$3" +path="$4" +statuscontainer=$5 + +echo 200 > $statuscontainer + +cat < + + + Index of $rpath + + +

Index of $rpath

+
+ + + + + + + +EOF + +for file in $(ls -a $path); do + if test "$file" = ".." -a "$rpath" = "/"; then + continue; + fi + cat < + + + + + +EOF +done + +cat < +
+ $host ($server) + + +EOF diff --git a/response.sh b/response.sh new file mode 100755 index 0000000..7fe4ab9 --- /dev/null +++ b/response.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +settingsfile=$1 +eval "$(cat $settingsfile)" # declare settings array + +declare -A headers +first=1 +while true; do + IFS=$'\r' read header + if test "$header" = ""; then + break + fi + if test $first = 1; then + headers[method]=$(echo "$header" | awk '{ print $1 }') + headers[http]=$(echo "$header" | awk '{ print $3 }' | awk -F/ '{ print $2} ') + headers[path]=$(echo "$header" | awk '{ print $2 }') + first=0 + continue + fi + headers[$(echo $header | awk -F: '{ print $1}')]="$(echo "$header" | awk '{for (i=2; i<=NF; i++) print $i}')" +done + +rpath="$(realpath -sm "${headers[path]}")" +headers[query]="$(echo "$rpath" | awk -F? '{for (i=2; i<=NF; i++) print $i}')" +rpath="$(echo "$rpath" | awk -F? '{ print $1 }')" + +path="${settings[home]}${rpath}" +path="$(realpath -sm "$path")" + +urlencode() { + python -c "import urllib, sys; print urllib.quote(sys.argv[1])" "$1" +} + +placeholder() { + declare -A tokens + tokens[path]="$rpath ($path)" + tokens[host]="${headers[Host]}" + tokens[server]="${settings[server]}" + + text="$(cat)" + + for key in "${!tokens[@]}"; do + key="$(echo "$key" | sed -e 's/[]\/$*.^|[]/\\&/g')" + value="$(echo "${tokens[$key]}" | sed -e 's/[]\/$*.^|[]/\\&/g')" + + text=$(echo "$text" | sed -e "s/\[$key\]/$value/g") + + done + + echo "$text" +} + +addStatusContainer() { + i=0 + container="/dev/shm/st-cont-$$-"; + while true; do + if test -f $container$i; then + i=$(($i+1)) + continue + fi + container=$container$i + break + done + touch $container + echo $container +} +removeStatusContainer() { + container=$1 + rm $container +} + +isExecutable() { + path=$1 + if test ! -x $path -o ! -f $path; then + return 1 + fi + ext="$(echo $path | awk -F. '{ print $NF }')" + for i in ${settings[executeable]}; do + if test "$ext" = "$i"; then + return 0 + fi + done + return 1 +} + +status=500 +content="" +declare -A responseHeaders +responseHeaders[Content-Type]="text/html" +responseHeaders[Server]="${settings[server]}" + + +if test ! -e "$path"; then + status=404 + + content="$(placeholder < ./404.html)" + +elif test ${settings[index]} = true -a -d "$path"; then + container=$(addStatusContainer) + content=$(./index.sh "${settings[server]}" "${headers[Host]}" "$rpath" "$path" "$container") + status=$(cat $container) + removeStatusContainer $container + +elif $(isExecutable $path); then + container=$(addStatusContainer) + pushd "$(dirname $path)" > /dev/null + content=$($path $container "$(declare -p settings)" "$(declare -p headers)") + popd > /dev/null + status=$(head -n 1 $container) + if test "$status" = ""; then + status=200 + fi + while read line; do + responseHeaders[$(echo $line | awk -F: '{ print $1 }')]="$(echo "$line" | awk '{for (i=2; i<=NF; i++) print $i}')" + done <<< $(tail -n 1 $container) + + removeStatusContainer $container +else + status=200 + responseHeaders['Content-Type']="$(file -b --mime-type $path)" + content=$(cat $path) +fi + +length=$(printf "%s" "$content" | wc -c) + +echo -en "HTTP/1.1 $status $(./statusString.sh $status)\r\n" +echo -en "Content-Length: $length\r\n" +for key in ${!responseHeaders[@]}; do + echo -en "$(urlencode "$key"): $(urlencode "${responseHeaders[$key]}")\r\n" +done +echo -en "\r\n" +printf "%s" "$content" + diff --git a/server.sh b/server.sh new file mode 100755 index 0000000..ba1096b --- /dev/null +++ b/server.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +EXIT_FAILURE=1 +EXIT_SUCCESS=0 + +port=-1 +progname="server" + +help() { + cat << EOF +usage: $progname --port=PORT + +Options: + -p, --port=PORT set port + -h, --home=HOME set home directory + -v, --verbose set to verbose mode + -q, --quiet don't output anything +EOF +} + +verboselevel=0 +echoOnVerbose() { + if test "$verboselevel" -ge $1; then + echo -n "$2" + fi +} + +progname="$0" + +OPTS=$(getopt -o "p:vqh:" -l "port:,verbose,quiet,home:" -- $@) +if test $? != 0; then + exit $EXIT_FAILURE +fi + +eval set -- "$OPTS" + +home="./home/" + +while true; do + case "$1" in + -p|--port) port=$2; shift 2;; + -v|--verbose) verboselevel=$(($verboselevel+1)); shift;; + -q|--quiet) verboselevel=-1; shift;; + -h|--home) home=$2; shift 2;; + --) shift; break;; + esac +done + +if test "$port" -lt 1; then + help + exit $EXIT_FAILURE +fi + +settingsfile="/dev/shm/wserver-$$" +declare -A settings +settings[home]=$home +settings[verbose]=$verboselevel +settings[executeable]="sh php py cgi" +settings[server]="server" +settings[index]="true" +declare -p settings > $settingsfile + +echo "Starting... " +socat $(echoOnVerbose 2 "-vv") tcp-listen:$port,reuseaddr,fork SYSTEM:"./response.sh $settingsfile" > /dev/null + +if test $? != 0; then + exit $EXIT_FAILURE +fi diff --git a/statusString.sh b/statusString.sh new file mode 100755 index 0000000..8ced6a3 --- /dev/null +++ b/statusString.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +status=$1 + +case $status in + 100) echo Continue ;; + 101) Switching Protocols ;; + 102) Processing ;; + + 200) echo OK ;; + 201) echo Created ;; + 202) echo Accepted ;; + 203) echo Non-Authoritative Information ;; + 204) echo No Content ;; + 205) echo Reset Content ;; + 206) echo Partial Content ;; + 207) echo Multi-Status ;; + 208) echo Already Reported ;; + 226) echo IM Used ;; + + 300) echo Multiple Choices ;; + 301) echo Moved Permanently ;; + 302) echo Found ;; + 303) echo See Other ;; + 304) echo Not Modified ;; + 305) echo Use Proxy ;; + 306) echo Switch Proxy ;; + 307) echo Temporary Redirect ;; + 308) echo Permanent Redirect ;; + + 400) echo Bad Request ;; + 401) echo Unauthorized ;; + 402) echo Payment Required ;; + 403) echo Forbidden ;; + 404) echo Not Found ;; + 405) echo Method Not Allowed ;; + 406) echo Not Acceptable ;; + 407) echo Proxy Authentication Required ;; + 408) echo Request Timeout ;; + 409) echo Conflict ;; + 410) echo Gone ;; + 411) echo Length Required ;; + 412) echo Precondition Failed ;; + 413) echo Payload Too Large ;; + 414) echo URI Too Long ;; + 415) echo Unsupported Media Type ;; + 416) echo Range Not Satisfiable ;; + 417) echo Expectation Failed ;; + 418) echo I\'m a teapot ;; + 421) echo Misdirected Request ;; + 422) echo Unprocessable Entity ;; + 423) echo Locked ;; + 424) echo Failed Dependency ;; + 426) echo Upgrade Required ;; + 428) echo Precondition Required ;; + 429) echo Too Many Requests ;; + 431) echo Request Header Fields Too Large ;; + 451) echo Unavailable For Legal Reasons ;; + + 500) echo Internal Server Error ;; + 501) echo Not Implemented ;; + 502) echo Bad Gateway ;; + 503) echo Service Unavailable ;; + 504) echo Gateway Timeout ;; + 505) echo HTTP Version Not Supported ;; + 506) echo Variant Also Negotiates ;; + 507) echo Insufficient Storage ;; + 508) echo Loop Detected ;; + 510) echo Not Extended ;; + 511) echo Network Authentication Required ;; + *) echo Unknown +esac diff --git a/test-response.sh b/test-response.sh new file mode 100755 index 0000000..90fc9ca --- /dev/null +++ b/test-response.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +echo "Start response.sh" 1>&2 + +settingsfile=$1 +eval "$(cat $settingsfile)" # declare settings array + +declare -A headers +first=1 +while true; do + IFS=$'\r' read header + if test "$header" = ""; then + break + fi + if test $first = 1; then + headers[method]=$(echo "$header" | awk '{ print $1 }') + headers[http]=$(echo "$header" | awk '{ print $3 }' | awk -F/ '{ print $2} ') + headers[path]=$(echo "$header" | awk '{ print $2 }') + first=0 + continue + fi + headers[$(echo $header | awk -F: '{ print $1}')]="$(echo "$header" | awk '{for (i=2; i<=NF; i++) print $i}')" +done + +content=" + + + Test + + +

Hallo Welt

+ $(for key in ${!headers[@]}; do echo "$key" "->" "${headers[$key]}" "
"; done) +
+
+ $(for key in ${!settings[@]}; do echo "$key" "->" "${settings[$key]}" "
"; done) + + +" + +length=$(printf "%s" "$content" | wc -c) + +echo "$length" 1>&2 +echo "$content" 1>&2 + +echo -en "HTTP/1.1 200 OK\r\n" +echo -en "Content-Type: text/html\r\n" +echo -en "Server: testserver\r\n" +echo -en "Content-Length: $length\r\n" +echo -en "\r\n" +printf "%s" "$content" +
+ Name + + Type + + Executeable + + Size +
+ $file + + $(file -b $path/$file) + + $(if test ! -d $path/$file; then if test -x $path/$file; then echo yes; else echo no; fi; fi) + + $(if test ! -d $path/$file; then du -kh $path/$file | cut -f1; fi) +