ShellSpider/response.sh
2017-11-24 01:43:59 +01:00

345 lines
9.6 KiB
Bash
Executable file

#!/bin/bash
ssl="$2"
settingsfile="$1"
eval "$(cat $settingsfile)" # declare settings array
. misc.sh
declare -A server
server[remoteAddress]="$SOCAT_PEERADDR"
server[remotePort]="$SOCAT_PEERPORT"
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)"
requestTimeout() {
echo "$(date --rfc-3339=ns) - ${server[remoteAddress]}:${server[remotePort]} - Error: request timeout" 1>&2
content="408 Request Timeout
The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time
"
status=408
length=$(printf "%s" "$content" | wc -c)
echo -en "HTTP/1.1 $status $(./statusString.sh $status)\r\n"
echo -en "Content-Length: $length\r\n"
echo -en "Content-Type: text/plain\r\n"
echo -en "\r\n"
printf "%s" "$content"
exit 1
}
trap requestTimeout SIGUSR1
headerTimeout=10
set +bm
headerTimer() {
pid=$1
timeout=$2
sleep $2
kill -USR1 $pid
}
headerTimer $$ $headerTimeout &
headerTimerPid=$!
declare -A headers
first=1
while true; do
IFS=$'\r' read header
if test "$header" = ""; then
break
fi
if test $first = 1; then
server[requestMethod]="$(echo "$header" | awk '{ print $1 }')"
server[http]="$(echo "$header" | awk '{ print $3 }' | awk -F/ '{ print $2} ')"
server[https]="$ssl"
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
headers[$(echo $header | awk -F: '{ print $1}')]="$(echo "$header" | awk '{for (i=2; i<=NF; i++) print $i}')"
done
kill -TERM $headerTimerPid
wait $headerTimerPid 2> /dev/null
if test "$first" = 1; then
# wut?
echo "$(date --rfc-3339=ns) - ${server[remoteAddress]}:${server[remotePort]} - Error: Malformed HTTP request; ignoring" 1>&2
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[scriptName]}"
tokens[host]="${server[httpHost]}"
tokens[server]="${server[serverSoftware]}"
tokens[signature]="${server[serverSignature]}"
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"
}
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[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
done
return 1
}
cgiExec() {
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]}"
"$1"
}
baseSource="$(pwd)/base.source.sh"
status=500
content=""
declare -A responseHeaders
responseHeaders[Content-Type]="text/html"
responseHeaders[Server]="${settings[server]}"
responseHeaders[Connection]="max=1"
type=""
if test ! -e "${server[scriptFilename]}"; then
status=404
content="$(placeholder < ./404.html)"
type="-"
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")
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"
type="index"
elif isExecutableShell "${server[scriptFilename]}"; then
container="$(addStatusContainer)"
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
status=200
fi
while read line; do
if test "$(echo "$line" | grep ':')" = ""; then
continue
fi
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"
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
cgiExec "${server[scriptFilename]}"
)
type="cgi"
elif isExecutablePHP "${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 REDIRECT_STATUS=1
cgiExec php-cgi -f "${server[scriptFilename]}"
)
type="php"
else
status=200
#responseHeaders['Content-Type']="$(file -b --mime-type ${server[scriptFilename]})"
responseHeaders['Content-Type']="$(mimetype -b "${server[scriptFilename]}")"
content="$(cat ${server[scriptFilename]})"
type="static"
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[requestURI]} - $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 "$key: ${responseHeaders[$key]}\r\n"
done
echo -en "\r\n"
printf "%s" "$content"