mirror of
https://github.com/sigmasternchen/ShellSpider
synced 2025-03-16 00:19:06 +00:00
basic version: index handler, cgi-style dynamic site generation, correct mime type for static files
This commit is contained in:
parent
9cc28dc1a9
commit
73a5498b83
16 changed files with 540 additions and 0 deletions
10
404.html
Normal file
10
404.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Not found</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Not found</h1>
|
||||
The resource [path] was not found on server [host].
|
||||
</body>
|
||||
</html>
|
20
home/hello-world.sh
Executable file
20
home/hello-world.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
statusContainer=$1
|
||||
eval "declare -A settings="${2#*=}
|
||||
eval "declare -A headers="${3#*=}
|
||||
|
||||
echo 200 > $statusContainer
|
||||
|
||||
cat <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Hello World</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World</h1>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
EOF
|
31
home/info.sh
Executable file
31
home/info.sh
Executable file
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
|
||||
statusContainer=$1
|
||||
eval "declare -A settings="${2#*=}
|
||||
eval "declare -A headers="${3#*=}
|
||||
|
||||
echo 200 > $statusContainer
|
||||
|
||||
cat <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Info</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Settings</h1>
|
||||
EOF
|
||||
for key in "${!settings[@]}"; do
|
||||
echo "$key -> ${settings[$key]}<br />"
|
||||
done
|
||||
cat <<EOF
|
||||
<h1>Headers</h1>
|
||||
EOF
|
||||
for key in "${!headers[@]}"; do
|
||||
echo "$key -> ${headers[$key]}<br />"
|
||||
done
|
||||
cat << EOF
|
||||
</body>
|
||||
</html>
|
||||
|
||||
EOF
|
60
home/stuff/add.sh
Executable file
60
home/stuff/add.sh
Executable file
|
@ -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<<EOF
|
||||
<!DOCTYPE>
|
||||
<html>
|
||||
<head>
|
||||
<title>Add to guestbook</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Add something to the guestbook</h1>
|
||||
<form action="?" method="GET">
|
||||
<input name="name" type="text" placeholder="Your name"><br />
|
||||
<textarea name="text"></textarea><br />
|
||||
<input type="submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
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 <<EOF
|
||||
===============================
|
||||
$(date): $(python -c "import urllib, sys; print urllib.unquote_plus(sys.argv[1]).decode('utf8')" "$name")
|
||||
|
||||
$(python -c "import urllib, sys; print urllib.unquote_plus(sys.argv[1]).decode('utf8')" "$text")
|
||||
|
||||
EOF
|
||||
|
||||
echo 302 > $statusContainer
|
||||
echo "Location: ./guestbook.txt" >> $statusContainer
|
6
home/stuff/guestbook.txt
Normal file
6
home/stuff/guestbook.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
===============================
|
||||
Sat Nov 18 01:20:41 CET 2017: overflow
|
||||
|
||||
Welcome. : )
|
||||
|
8
home/test/cgi-test.c
Normal file
8
home/test/cgi-test.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
|
||||
printf("Hello World");
|
||||
|
||||
return 0;
|
||||
}
|
BIN
home/test/cgi-test.cgi
Executable file
BIN
home/test/cgi-test.cgi
Executable file
Binary file not shown.
3
home/test/non-executeable-script.sh
Normal file
3
home/test/non-executeable-script.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo Hello World
|
4
home/test/php-test.php
Executable file
4
home/test/php-test.php
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
echo "PHP version: " . phpversion();
|
1
home/test/plaintest.txt
Normal file
1
home/test/plaintest.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Hello World
|
8
home/test/redirect.sh
Executable file
8
home/test/redirect.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
statusContainer=$1
|
||||
eval "declare -A settings="${2#*=}
|
||||
eval "declare -A headers="${3#*=}
|
||||
|
||||
echo 302 > $statusContainer
|
||||
echo "Location: ../stuff/guestbook.txt" >> $statusContainer
|
65
index.sh
Executable file
65
index.sh
Executable file
|
@ -0,0 +1,65 @@
|
|||
#!/bin/bash
|
||||
|
||||
server="$1"
|
||||
host="$2"
|
||||
rpath="$3"
|
||||
path="$4"
|
||||
statuscontainer=$5
|
||||
|
||||
echo 200 > $statuscontainer
|
||||
|
||||
cat <<EOF
|
||||
<!DOCTYPE>
|
||||
<html>
|
||||
<head>
|
||||
<title>Index of $rpath</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Index of $rpath</h1>
|
||||
<hr />
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
Name
|
||||
</th>
|
||||
<th>
|
||||
Type
|
||||
</th>
|
||||
<th>
|
||||
Executeable
|
||||
</th>
|
||||
<th>
|
||||
Size
|
||||
</th>
|
||||
</tr>
|
||||
EOF
|
||||
|
||||
for file in $(ls -a $path); do
|
||||
if test "$file" = ".." -a "$rpath" = "/"; then
|
||||
continue;
|
||||
fi
|
||||
cat <<EOF
|
||||
<tr>
|
||||
<td>
|
||||
<a href="$(realpath -sm "$rpath/$file")">$file</a>
|
||||
</td>
|
||||
<td>
|
||||
$(file -b $path/$file)
|
||||
</td>
|
||||
<td>
|
||||
$(if test ! -d $path/$file; then if test -x $path/$file; then echo yes; else echo no; fi; fi)
|
||||
</td>
|
||||
<td>
|
||||
$(if test ! -d $path/$file; then du -kh $path/$file | cut -f1; fi)
|
||||
</td>
|
||||
</tr>
|
||||
EOF
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
</table>
|
||||
<hr />
|
||||
$host ($server)
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
133
response.sh
Executable file
133
response.sh
Executable file
|
@ -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"
|
||||
|
68
server.sh
Executable file
68
server.sh
Executable file
|
@ -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
|
72
statusString.sh
Executable file
72
statusString.sh
Executable file
|
@ -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
|
51
test-response.sh
Executable file
51
test-response.sh
Executable file
|
@ -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="<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hallo Welt</h1>
|
||||
$(for key in ${!headers[@]}; do echo "$key" "->" "${headers[$key]}" "<br />"; done)
|
||||
<br />
|
||||
<br />
|
||||
$(for key in ${!settings[@]}; do echo "$key" "->" "${settings[$key]}" "<br />"; done)
|
||||
</body>
|
||||
</html>
|
||||
"
|
||||
|
||||
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"
|
||||
|
Loading…
Reference in a new issue