diff --git a/404.html b/404.html index 6c51e5b..326ac61 100644 --- a/404.html +++ b/404.html @@ -5,6 +5,8 @@

Not found

- The resource [path] was not found on server [host]. + The resource [path] was not found on the server. +
+ [signature] diff --git a/README.md b/README.md index 484a341..147dc96 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Requirements - an up-to-date version of bash - socat (for networking) - python (for url-encoding) +- dig (for reverse lookup) - some other basic tools, like sed, grep, awk, getopt Usage diff --git a/home/stuff/add.sh b/home/stuff/add.sh index 81c87c6..76a926a 100755 --- a/home/stuff/add.sh +++ b/home/stuff/add.sh @@ -2,7 +2,7 @@ . $1 -if test "${server[query]}" == ""; then +if test "${server[queryString]}" == ""; then setStatusCode 200 cat< +#include +#include + +int main() { + + if (strlen(getenv("QUERY_STRING")) == 0) { + printf("Status: 302 Found\n"); + printf("Location: ?foo=bar\n\n"); + } else { + printf("Content-Type: text/plain\n\n"); + + printf("This Server is running %s.\n", getenv("SERVER_SOFTWARE")); + printf("The query string is %s.\n", getenv("QUERY_STRING")); + } + + return 0; +} diff --git a/home/test/cgi-test-c.cgi b/home/test/cgi-test-c.cgi new file mode 100755 index 0000000..767b701 Binary files /dev/null and b/home/test/cgi-test-c.cgi differ diff --git a/home/test/cgi-test-shell.cgi b/home/test/cgi-test-shell.cgi new file mode 100755 index 0000000..16ed420 --- /dev/null +++ b/home/test/cgi-test-shell.cgi @@ -0,0 +1,5 @@ +#!/bin/bash + +printf "%s\n\n" "Content-Type: text/plain" + +env diff --git a/home/test/cgi-test.c b/home/test/cgi-test.c deleted file mode 100644 index 4fbf4ef..0000000 --- a/home/test/cgi-test.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int main() { - - printf("Hello World"); - - return 0; -} diff --git a/home/test/cgi-test.cgi b/home/test/cgi-test.cgi deleted file mode 100755 index f2e944b..0000000 Binary files a/home/test/cgi-test.cgi and /dev/null differ diff --git a/index.sh b/index.sh index cf0f5a1..7b3eec9 100755 --- a/index.sh +++ b/index.sh @@ -8,10 +8,10 @@ cat < - Index of ${server[path]} + Index of ${server[scriptName]} -

Index of ${server[path]}

+

Index of ${server[scriptName]}


@@ -30,23 +30,23 @@ cat < EOF -for file in $(ls -a "${server[real_path]}"); do - if test "$file" = ".." -a "${server[path]}" = "/"; then +for file in $(ls -a "${server[scriptFilename]}"); do + if test "$file" = ".." -a "${server[scriptName]}" = "/"; then continue; fi cat < EOF @@ -55,7 +55,7 @@ done cat <
- ${headers[Host]} (${settings[server]}) + ${server[serverSignature]} EOF diff --git a/response.sh b/response.sh index 57458d6..6282597 100755 --- a/response.sh +++ b/response.sh @@ -8,8 +8,16 @@ eval "$(cat $settingsfile)" # declare settings array declare -A server server[remoteAddress]="$SOCAT_PEERADDR" server[remotePort]="$SOCAT_PEERPORT" -server[localAddress]="$SOCAT_SOCKADDR" -server[localPort]="$SOCAT_SOCKPORT" +server[remoteHost]="$(dig +noall +answer -x $SOCAT_PEERADDR | awk '{ print $5 }')" +server[serverAddress]="$SOCAT_SOCKADDR" +server[serverPort]="$SOCAT_SOCKPORT" +server[serverName]="${settings[name]}" +server[serverAdmin]="${settings[admin]}" +server[serverSoftware]="${settings[server]}" +server[documentRoot]="$(realpath "${settings[home]}")" +server[requestTime]="$(date +%s)" +server[requestTimeFloat]="$(($(date +%s%N)/1000))" +server[requestTimeReadable]="$(date --rfc-3339=ns)" declare -A headers first=1 @@ -19,13 +27,15 @@ while true; do break fi if test $first = 1; then - server[method]="$(echo "$header" | awk '{ print $1 }')" + server[requestMethod]="$(echo "$header" | awk '{ print $1 }')" server[http]="$(echo "$header" | awk '{ print $3 }' | awk -F/ '{ print $2} ')" - server[user_path]=$(echo "$header" | awk '{ print $2 }') - server[path]="$(realpath -sm "${server[user_path]}")" - server[query]="$(echo "${server[path]}" | awk -F? '{for (i=2; i<=NF; i++) print $i}')" - server[path]="$(echo "${server[path]}" | awk -F? '{ print $1 }')" - server[real_path]="$(realpath -sm "${settings[home]}${server[path]}")" + server[https]="off" + server[serverProtocol]="$(echo "$header" | awk '{ print $3 }')" + server[request_unchecked]=$(echo "$header" | awk '{ print $2 }') + server[requestURI]="$(realpath -sm "${server[request_unchecked]}")" + server[queryString]="$(echo "${server[requestURI]}" | awk -F? '{for (i=2; i<=NF; i++) print $i}')" + server[scriptName]="$(echo "${server[requestURI]}" | awk -F? '{ print $1 }')" + server[scriptFilename]="$(realpath -sm "${settings[home]}${server[scriptName]}")" first=0 continue fi @@ -38,11 +48,30 @@ if test "$first" = 1; then exit 1 fi +server[httpAccept]="${headers[Accept]}" +server[httpAcceptCharset]="${headers[Accept-Charset]}" +server[httpAcceptEncoding]="${headers[Accept-Encoding]}" +server[httpAcceptLanguage]="${headers[Accept-Language]}" +server[httpConnection]="${headers[Accept-Connection]}" +server[httpHost]="${headers[Host]}" +server[httpReferer]="${headers[Referer]}" +server[httpUserAgent]="${headers[User-Agent]}" + +server[remoteUser]="" # TODO not implemented +server[redirectRemoteUser]="" # TODO not implemented +server[authType]="" # TODO not implemented + +server[serverSignature]="" +if true; then # TODO add condition setting + server[serverSignature]="${server[httpHost]} (${server[serverSoftware]})" +fi + placeholder() { declare -A tokens - tokens[path]="${server[path]}" - tokens[host]="${headers[Host]}" - tokens[server]="${settings[server]}" + tokens[path]="${server[scriptName]}" + tokens[host]="${server[httpHost]}" + tokens[server]="${server[serverSoftware]}" + tokens[signature]="${server[serverSignature]}" text="$(cat)" @@ -76,13 +105,41 @@ removeStatusContainer() { rm "$container" } -isExecutable() { +isExecutableShell() { 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 + for i in ${settings[shellExec]}; do + if test "$ext" = "$i"; then + return 0 + fi + done + return 1 +} + +isExecutableCGI() { + path="$1" + if test ! -x "$path" -o ! -f "$path"; then + return 1 + fi + ext="$(echo "$path" | awk -F. '{ print $NF }')" + for i in ${settings[cgiExec]}; do + if test "$ext" = "$i"; then + return 0 + fi + done + return 1 +} + +isExecutablePHP() { + path="$1" + if test ! -x "$path" -o ! -f "$path"; then + return 1 + fi + ext="$(echo "$path" | awk -F. '{ print $NF }')" + for i in ${settings[phpExec]}; do if test "$ext" = "$i"; then return 0 fi @@ -101,13 +158,13 @@ responseHeaders[Connection]="max=1" type="" -if test ! -e "${server[real_path]}"; then +if test ! -e "${server[scriptFilename]}"; then status=404 content="$(placeholder < ./404.html)" type="-" -elif test "${settings[index]}" = "true" -a -d "${server[real_path]}"; then +elif test "${settings[index]}" = "true" -a -d "${server[scriptFilename]}"; then container="$(addStatusContainer)" content="$(./index.sh "$baseSource" "$container" "$(declare -p settings)" "$(declare -p server)" "$(declare -p headers)")" status=$(head -n 1 "$container") @@ -121,10 +178,10 @@ elif test "${settings[index]}" = "true" -a -d "${server[real_path]}"; then removeStatusContainer "$container" type="index" -elif $(isExecutable "${server[real_path]}"); then +elif isExecutableShell "${server[scriptFilename]}"; then container="$(addStatusContainer)" - pushd "$(dirname "${server[real_path]}")" > /dev/null - content=$("${server[real_path]}" "$baseSource" "$container" "$(declare -p settings)" "$(declare -p server)" "$(declare -p headers)") + pushd "$(dirname "${server[scriptFilename]}")" > /dev/null + content=$("${server[scriptFilename]}" "$baseSource" "$container" "$(declare -p settings)" "$(declare -p server)" "$(declare -p headers)") popd > /dev/null status=$(head -n 1 "$container") if test "$status" = ""; then @@ -139,12 +196,67 @@ elif $(isExecutable "${server[real_path]}"); then removeStatusContainer "$container" - type="exec" + type="shell" +elif isExecutableCGI "${server[scriptFilename]}"; then + content="" + status=200 + headerDone=0 + while read line; do + if test $headerDone = 1; then + content="${content}${line}"$'\n' + continue + fi + if test "$(echo "$line" | grep ':')" = ""; then + headerDone=1 + continue + fi + if test "$(echo "$line" | awk -F: '{print $1}')" = "Status"; then + status="$(echo "$line" | awk '{print $2}')" + continue + fi + responseHeaders[$(echo "$line" | awk -F: '{ print $1 }')]="$(echo "$line" | awk '{for (i=2; i<=NF; i++) print $i}')" + done <<< $( # open subshell + export GATEWAY_INTERFACE="CGI/1.1" + + export AUTH_TYPE="${server[authType]}" + export DOCUMENT_ROOT="${server[documentRoot]}" + export HTTP_ACCEPT_CHARSET="${server[httpAcceptCharset]}" + export HTTP_ACCEPT_ENCODING="${server[httpAcceptEncoding]}" + export HTTP_ACCEPT_LANGUAGE="${server[httpAcceptLanguage]}" + export HTTP_ACCEPT="${server[httpAccept]}" + export HTTP_CONNECTION="${server[httpConnection]}" + export HTTP_HOST="${server[httpHost]}" + export HTTPS="${server[https]}" + export HTTP_USER_AGENT="${server[httpUserAgent]}" + export QUERY_STRING="${server[queryString]}" + export REDIRECT_REMOTE_USER="${server[redirectRemoteUser]}" + export REMOTE_ADDR="${server[remoteAddress]}" + export REMOTE_HOST="${server[remoteHost]}" + export REMOTE_PORT="${server[remotePort]}" + export REMOTE_USER="${server[remoteUser]}" + export REQUEST_METHOD="${server[requestMethod]}" + export REQUEST_TIME_FLOAT="${server[requestTimeFloat]}" + export REQUEST_TIME="${server[requestTime]}" + export REQUEST_URI="${server[requestURI]}" + export SCRIPT_FILENAME="${server[scriptFilename]}" + export SCRIPT_NAME="${server[scriptName]}" + export SERVER_ADDR="${server[serverAddress]}" + export SERVER_ADMIN="${server[serverAdmin]}" + export SERVER_NAME="${server[serverName]}" + export SERVER_PORT="${server[serverPort]}" + export SERVER_PROTOCOL="${server[serverProtocol]}" + export SERVER_SIGNATURE="${server[serverSignature]}" + export SERVER_SOFTWARE="${server[serverSoftware]}" + + ${server[scriptFilename]} + ) + + type="cgi" else status=200 - #responseHeaders['Content-Type']="$(file -b --mime-type ${server[real_path]})" - responseHeaders['Content-Type']="$(mimetype -b "${server[real_path]}")" - content="$(cat ${server[real_path]})" + #responseHeaders['Content-Type']="$(file -b --mime-type ${server[scriptFilename]})" + responseHeaders['Content-Type']="$(mimetype -b "${server[scriptFilename]}")" + content="$(cat ${server[scriptFilename]})" type="static" fi @@ -152,13 +264,14 @@ fi length=$(printf "%s" "$content" | wc -c) if test "${settings[verbose]}" -ge "0"; then - echo "$(date --rfc-3339=ns) - ${server[remoteAddress]}:${server[remotePort]} - ${headers[Host]}${server[path]} - $type - $status - $length bytes" 1>&2 + echo "$(date --rfc-3339=ns) - ${server[remoteAddress]}:${server[remotePort]} - ${headers[Host]}${server[queryURI]} - $type - $status - $length bytes" 1>&2 fi 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" + #echo -en "$(urlencode "$key"): $(urlencode "${responseHeaders[$key]}")\r\n" + echo -en "$key: ${responseHeaders[$key]}\r\n" done echo -en "\r\n" printf "%s" "$content" diff --git a/server.sh b/server.sh index 6614232..e91ce37 100755 --- a/server.sh +++ b/server.sh @@ -27,7 +27,7 @@ echoOnVerbose() { progname="$0" -OPTS=$(getopt -o "p:vqh:" -l "port:,verbose,quiet,home:" -- $@) +OPTS=$(getopt -o "p:vqh:n:" -l "port:,verbose,quiet,home:,name:admin:" -- $@) if test $? != 0; then exit $EXIT_FAILURE fi @@ -35,6 +35,8 @@ fi eval set -- "$OPTS" home="./home/" +name="localhost" +admin="admin@localhost" while true; do case "$1" in @@ -42,6 +44,8 @@ while true; do -v|--verbose) verboselevel=$(($verboselevel+1)); shift;; -q|--quiet) verboselevel=-1; shift;; -h|--home) home=$2; shift 2;; + -n|--name) name=$2; shift 2;; + --admin) admin=$2; shift 2;; --) shift; break;; esac done @@ -55,8 +59,12 @@ settingsfile="/dev/shm/wserver-$$" declare -A settings settings[serverDirectory]="$(pwd)" settings[home]="$home" +settings[name]="$name" +settings[admin]="$admin" settings[verbose]=$verboselevel -settings[executeable]="sh php py cgi" +settings[shellExec]="sh" +settings[cgiExec]="cgi" +settings[phpExec]="php" settings[server]="ShellSpider V1" settings[index]="true" declare -p settings > $settingsfile
- $file + $file - $(file -b "${server[real_path]}/$file") + $(file -b "${server[scriptFilename]}/$file") - $(if test ! -d "${server[real_path]}/$file"; then if test -x "${server[real_path]}/$file"; then echo yes; else echo no; fi; fi) + $(if test ! -d "${server[scriptFilename]}/$file"; then if test -x "${server[scriptFilename]}/$file"; then echo yes; else echo no; fi; fi) - $(if test ! -d "${server[real_path]}/$file"; then du -kh "${server[real_path]}/$file" | cut -f1; fi) + $(if test ! -d "${server[scriptFilename]}/$file"; then du -kh "${server[scriptFilename]}/$file" | cut -f1; fi)