http.sh/src/server.sh

230 lines
7.2 KiB
Bash
Raw Normal View History

2020-05-23 22:13:11 +02:00
#!/bin/bash
source config/master.sh
source src/mime.sh
source src/misc.sh
source src/account.sh
source src/mail.sh
2021-02-14 04:20:41 +01:00
source src/route.sh
2021-02-28 03:33:58 +01:00
source src/template.sh
2020-10-04 14:53:26 +02:00
[[ -f "${cfg[namespace]}/config.sh" ]] && source "${cfg[namespace]}/config.sh"
2020-05-23 22:13:11 +02:00
declare -A r # current request / response
declare -A meta # metadata for templates
declare -A cookies # cookies!
2021-02-14 04:20:41 +01:00
declare -A get_data # all GET params
declare -A post_data # all POST params
declare -A params # parsed router data
2020-05-23 22:13:11 +02:00
r[status]=210 # Mommy always said that I was special
post_length=0
2021-02-28 03:33:58 +01:00
while read -r param; do
param_l="${param,,}" # lowercase
2021-01-19 01:10:47 +01:00
name=''
value=''
data=''
unset IFS
if [[ "$param_l" == $'\015' ]]; then
2020-05-23 22:13:11 +02:00
break
elif [[ "$param_l" == *"content-length:"* ]]; then
r[content_length]="$(sed 's/Content-Length: //i;s/\r//' <<< "$param")"
2020-05-23 22:13:11 +02:00
elif [[ "$param_l" == *"content-type:"* ]]; then
r[content_type]="$(sed 's/Content-Type: //i;s/\r//' <<< "$param")"
if [[ "${r[content_type]}" == *"multipart/form-data"* ]]; then
2020-05-23 22:13:11 +02:00
tmpdir=$(mktemp -d)
fi
if [[ "${r[content_type]}" == *"boundary="* ]]; then
r[content_boundary]="$(sed -E 's/(.*)boundary=//i;s/\r//;s/ //' <<< "${r[content_type]}")"
2020-05-23 22:13:11 +02:00
fi
elif [[ "$param_l" == *"host:"* ]]; then
r[host]="$(sed 's/Host: //i;s/\r//;s/\\//g' <<< "$param")"
r[host_portless]="$(sed -E 's/:(.*)$//' <<< "${r[host]}")"
if [[ -f "config/$(basename -- ${r[host]})" ]]; then
source "config/$(basename -- ${r[host]})"
elif [[ -f "config/$(basename -- ${r[host_portless]})" ]]; then
source "config/$(basename -- ${r[host_portless]})"
2020-05-23 22:13:11 +02:00
fi
2021-02-02 16:14:39 +01:00
elif [[ "$param_l" == *"user-agent:"* ]]; then
r[user_agent]="$(sed 's/User-Agent: //i;s/\r//;s/\\//g' <<< "$param")"
2020-05-23 22:13:11 +02:00
elif [[ "$param_l" == *"upgrade:"* && $(sed 's/Upgrade: //i;s/\r//' <<< "$param") == "websocket" ]]; then
2020-05-23 22:13:11 +02:00
r[status]=101
elif [[ "$param_l" == *"sec-websocket-key:"* ]]; then
r[websocket_key]="$(sed 's/Sec-WebSocket-Key: //i;s/\r//' <<< "$param")"
2020-05-23 22:13:11 +02:00
elif [[ "$param_l" == *"authorization: basic"* ]]; then
login_simple "$param"
elif [[ "$param_l" == *"authorization: bearer"* ]]; then
r[authorization]="$(sed 's/Authorization: Bearer //i;s/\r//' <<< "$param")"
2021-01-19 01:10:47 +01:00
elif [[ "$param_l" == *"cookie: "* ]]; then
IFS=';'
for i in $(IFS=' '; echo "$param" | sed -E 's/Cookie: //i;;s/%/\\x/g'); do
2021-01-15 21:15:38 +01:00
name="$((grep -Poh "[^ ].*?(?==)" | head -1) <<< $i)"
value="$(sed "s/$name=//;s/^ //;s/ $//" <<< $i)"
cookies[$name]="$(echo -e $value)"
2020-05-23 22:13:11 +02:00
done
2021-05-03 01:05:31 +02:00
elif [[ "$param_l" == *"range: bytes="* ]]; then
r[range]="$(sed 's/Range: bytes=//;s/\r//' <<< "$param")"
2020-05-23 22:13:11 +02:00
elif [[ "$param" == *"GET "* ]]; then
r[url]="$(echo -ne "$(url_decode "$(sed -E 's/GET //;s/HTTP\/[0-9]+\.[0-9]+//;s/ //g;s/\/*\r//g;s/\/\/*/\//g' <<< "$param")")")"
2021-05-03 01:05:31 +02:00
data="$(sed -E 's/\?/<2F><>MaE_iS_CuTe<54>/;s/^(.*)<29><>MaE_iS_CuTe<54>//;s/\&/ /g' <<< "${r[url]}")"
if [[ "$data" != "${r[url]}" ]]; then
data="$(echo ${r[url]} | sed -E 's/^(.*)\?//')"
IFS='&'
2020-05-23 22:13:11 +02:00
for i in $data; do
name="$(sed -E 's/\=(.*)$//' <<< "$i")"
value="$(sed "s/$name\=//" <<< "$i")"
get_data[$name]="$value"
2020-05-23 22:13:11 +02:00
done
fi
elif [[ "$param" == *"POST "* ]]; then
r[url]="$(echo -ne "$(url_decode "$(sed -E 's/POST //;s/HTTP\/[0-9]+\.[0-9]+//;s/ //g;s/\/*\r//g;s/\/\/*/\//g' <<< "$param")")")"
2020-05-23 22:13:11 +02:00
r[post]=true
# below shamelessly copied from GET, should be moved to a function
2021-05-03 01:05:31 +02:00
data="$(sed -E 's/\?/<2F><>MaE_iS_CuTe<54>/;s/^(.*)<29><>MaE_iS_CuTe<54>//;s/\&/ /g' <<< "${r[url]}")"
if [[ "$data" != "${r[url]}" ]]; then
data="$(sed -E 's/^(.*)\?//' <<< "${r[url]}")"
IFS='&'
2020-05-23 22:13:11 +02:00
for i in $data; do
name="$(sed -E 's/\=(.*)$//' <<< "$i")"
value="$(sed "s/$name\=//" <<< "$i")"
post_data[$name]="$value"
2020-05-23 22:13:11 +02:00
done
2021-01-19 01:10:47 +01:00
fi
2020-05-23 22:13:11 +02:00
fi
done
r[uri]="$(realpath "${cfg[namespace]}/${cfg[root]}$(sed -E 's/\?(.*)$//' <<< "${r[url]}")")"
2020-05-23 22:13:11 +02:00
[[ -d "${r[uri]}/" ]] && pwd="${r[uri]}" || pwd=$(dirname "${r[uri]}")
2020-10-11 20:25:05 +02:00
if [[ $NCAT_LOCAL_PORT == '' ]]; then
2020-06-02 00:01:44 +02:00
r[proto]='http'
2020-10-11 20:25:05 +02:00
r[ip]="NCAT_IS_BORK"
2020-06-02 00:01:44 +02:00
else
r[proto]='https'
2020-10-11 20:25:05 +02:00
r[ip]="$NCAT_REMOTE_ADDR:$NCAT_REMOTE_PORT"
2020-06-02 00:01:44 +02:00
fi
2021-05-02 16:51:56 +02:00
echo "$(date) - IP: ${r[ip]}, PROTO: ${r[proto]}, URL: ${r[url]}, GET_data: ${get_data[@]}, POST_data: ${post_data[@]}, POST_multipart: ${post_multipart[@]}, UA: ${r[user_agent]}" >> "${cfg[namespace]}/${cfg[log]}"
2020-06-02 00:01:44 +02:00
2021-02-26 23:24:19 +01:00
[[ -f "${cfg[namespace]}/routes.sh" ]] && source "${cfg[namespace]}/routes.sh"
2020-05-23 22:13:11 +02:00
if [[ ${r[status]} != 101 ]]; then
2021-02-14 04:20:41 +01:00
for (( i=0; i<${#route[@]}; i=i+3 )); do
2021-02-18 00:39:26 +01:00
if [[ "$(grep -Poh "^${route[$((i+1))]}" <<< "${r[url]}")" != "" ]]; then
2021-02-14 04:20:41 +01:00
r[status]=212
r[view]="${route[$((i+2))]}"
IFS='/'
url=(${route[$i]})
url_=(${r[url]})
unset IFS
for (( j=0; j<${#url[@]}; j++ )); do
if [[ ${url_[$j]} != '' ]]; then
params[$(sed 's/://' <<< "${url[$j]}")]="${url_[$j]}"
fi
done
break
fi
done
unset IFS
if [[ ${r[status]} != 212 ]]; then
if [[ -a "${r[uri]}" && ! -r "${r[uri]}" ]]; then
r[status]=403
elif [[ "$(echo -n "${r[uri]}")" != "$(realpath "${cfg[namespace]}/${cfg[root]}")"* ]]; then
r[status]=403
elif [[ -f "${r[uri]}" ]]; then
r[status]=200
elif [[ -d "${r[uri]}" ]]; then
for name in ${cfg[index]}; do
if [[ -f "${r[uri]}/$name" ]]; then
r[uri]="${r[uri]}/$name"
r[status]=200
fi
done
else
r[status]=404
fi
2020-05-23 22:13:11 +02:00
fi
fi
2021-02-14 04:20:41 +01:00
echo "${r[url]}" >&2
2021-02-28 01:05:24 +01:00
if [[ "${cfg[auth_required]}" == true && "${r[authorized]}" != true ]]; then
2020-06-02 00:01:44 +02:00
echo "Auth failed." >> ${cfg[log_misc]}
2020-05-23 22:13:11 +02:00
r[status]=401
fi
2021-02-28 01:05:24 +01:00
if [[ "${cfg[proxy]}" == true ]]; then
r[status]=211
fi
2021-02-28 01:05:24 +01:00
if [[ "${r[post]}" == true && "${r[status]}" == 200 ]]; then
2020-05-23 22:13:11 +02:00
# This whole ordeal is here to prevent passing binary data as a variable.
# I could have done it as an array, but this solution works, and it's
# speedy enough so I don't care.
if [[ $tmpdir ]]; then
declare post_multipart
tmpfile=$(mktemp -p $tmpdir)
dd iflag=fullblock of=$tmpfile ibs=${r[content_length]} count=1 obs=1M
delimeter_len=$(echo -n "${r[content_boundary]}"$'\015' | wc -c)
2020-05-23 22:13:11 +02:00
boundaries_list=$(echo -ne $(grep $tmpfile -ao -e ${r[content_boundary]} --byte-offset | sed -E 's/:(.*)//g') | sed -E 's/ [0-9]+$//')
for i in $boundaries_list; do
tmpout=$(mktemp -p $tmpdir)
dd iflag=fullblock if=$tmpfile ibs=$(($i+$delimeter_len)) obs=1M skip=1 | while true; do
read line
if [[ $line == $'\015' ]]; then
cat - > $tmpout
break
fi
done
length=$(grep $tmpout --byte-offset -ae ${r[content_boundary]} | sed -E 's/:(.*)//' | head -n 1)
outfile=$(mktemp -p $tmpdir)
post_multipart+=($outfile)
dd iflag=fullblock if=$tmpout ibs=$length count=1 obs=1M of=$outfile
rm $tmpout
done
rm $tmpfile
else
2021-01-27 01:42:20 +01:00
read -N "${r[content_length]}" data
2021-02-14 04:20:41 +01:00
2021-01-27 01:42:20 +01:00
IFS='&'
for i in $(tr -d '\n' <<< "$data"); do
name="$(sed -E 's/\=(.*)$//' <<< "$i")"
param="$(sed "s/$name\=//" <<< "$i")"
post_data[$name]="$param"
2020-05-23 22:13:11 +02:00
done
2021-01-27 01:42:20 +01:00
unset IFS
2020-05-23 22:13:11 +02:00
fi
fi
if [[ ${r[status]} == 210 && ${cfg[autoindex]} == true ]]; then
source "src/response/listing.sh"
elif [[ ${r[status]} == 211 ]]; then
source "src/response/proxy.sh"
2021-02-14 04:20:41 +01:00
elif [[ ${r[status]} == 200 || ${r[status]} == 212 ]]; then
2020-05-23 22:13:11 +02:00
source "src/response/200.sh"
elif [[ ${r[status]} == 401 ]]; then
source "src/response/401.sh"
elif [[ ${r[status]} == 404 ]]; then
source "src/response/404.sh"
elif [[ ${r[status]} == 101 ]]; then
source "src/response/101.sh"
else
source "src/response/403.sh"
fi