httpd.sh - randomcrap - random crap programs of varying quality
(HTM) git clone git://git.codemadness.org/randomcrap
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
httpd.sh (3488B)
---
1 #!/bin/sh
2 # insecure mini httpd intended for local testing.
3 # Dependencies: socat, file, awk, UNIX tools, etc.
4
5 # log(status)
6 log() {
7 printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$REMOTE_ADDR" "$1" "$REQUEST_METHOD" "HTTP/1.0" "$REQUEST_PATH" "$QUERY_STRING" >&2
8 }
9
10 # httpheader(status) {
11 httpheader() {
12 log "$1"
13 printf 'HTTP/1.0 %s \r\nDate: %s\r\nConnection: close\r\n'\
14 "$1" "$(TZ=UTC date +'%a, %d %b %Y %H:%M:%S +0000')"
15 }
16
17 # httpstatus(status)
18 httpstatus() {
19 httpheader "$1"
20 printf 'Content-Type: text/plain\r\n\r\n%s\n' "$1"
21 }
22
23 # servedata(file)
24 servedata() {
25 httpheader '200 OK'
26 printf 'Content-Type: %s\r\n\r\n' "$(file -bi "$1")"
27 cat "$1"
28 }
29
30 # servefile(file)
31 servefile() {
32 servedata "$1"
33 }
34
35 # servedir(dir)
36 servedir() {
37 if cd "$1" >/dev/null 2>/dev/null; then
38 httpheader '200 OK'
39 printf 'Content-Type: text/html; charset=utf-8\r\n\r\n'
40 ls -a1p | LC_ALL=C awk -v "dir=$1" '
41 function encodehtml(s) {
42 gsub("&", "\\&", s);
43 gsub("\"", "\\"", s);
44 gsub("'"'"'", "\\'", s);
45 gsub("<", "\\<", s);
46 gsub(">", "\\>", s);
47 return s;
48 }
49 BEGIN { print "<pre>"; }
50 {
51 name = encodehtml($0);
52 printf("<a href=\"%s\">%s</a>\n", name, name);
53 }
54 END { print "</pre>"; }
55 '
56 else
57 httpstatus '403 Forbidden'
58 fi
59 }
60
61 # servescript(file)
62 servescript() {
63 t="$(mktemp)"
64 if "$1" > "$t"; then
65 cat "$t"
66 log '0 CGI' # CGI can return any HTTP status
67 else
68 httpstatus '500 Internal Server Error'
69 fi
70 rm -f "$t"
71 }
72
73 # percentdecode(path)
74 percentdecode() {
75 printf '%s' "$1" | sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf '%b'
76 }
77
78 # sanitizepath(path)
79 sanitizepath() {
80 # good enough for this crappy insecure local httpd.
81 printf '%s' "$1" | sed 's@\.\./@@;s@\./@@g'
82 }
83
84 script="$(readlink -f "$0")"
85 rootdir="$(dirname "$(readlink -f "$0")")"
86 htdocsdir="${rootdir}/htdocs"
87 scriptdir="${rootdir}/cgi-bin"
88
89 if test "$1" != "httpd"; then
90 export SERVER_NAME="${1:-127.0.0.1}"
91 export SERVER_PORT="${2:-8080}"
92 printf '# started minihttpd:\n#\n# host = %s\n# port = %s\n# htdocs = %s\n# scriptdir = %s\n#\n' "$SERVER_NAME" "$SERVER_PORT" "$htdocsdir" "$scriptdir" >&2
93 SOCAT_SOCKADDR="${SERVER_NAME}" socat "TCP4-LISTEN:${SERVER_PORT},reuseaddr,fork,bind=${SERVER_NAME}" "SYSTEM:'$script httpd'"
94 elif test "$1" = "httpd"; then
95 IFS=" " read -r method request proto
96 while IFS=": " read -r key value; do
97 test "$value" = "" && break
98 done
99
100 query="${request}?"
101 query="${query#*\?}"
102 query="${query%\?}"
103 requestpath="${request%\?*}"
104
105 if test "$requestpath" = "/"; then
106 file="index.txt"
107 else
108 file="${requestpath#/}"
109 file="$(percentdecode "$file")"
110 file="$(sanitizepath "$file")"
111 fi
112 realfile="${htdocsdir}/${file}"
113 basename="$(basename "$realfile")"
114 scriptname="/cgi-bin/${basename}" # only execute scripts in cgi-bin
115 # a few CGI variables (RFC3875) and custom ones.
116 QUERY_STRING="$query";REMOTE_ADDR="$SOCAT_PEERADDR";REQUEST_METHOD="$method";SERVER_PROTOCOL="$proto";REQUEST_PATH="/$file";RAW_REQUEST="$request"
117 for n in QUERY_STRING REMOTE_ADDR REQUEST_METHOD SERVER_PROTOCOL REQUEST_PATH RAW_REQUEST; do export "$n"; done
118
119 if test -d "$realfile"; then
120 if test "$file" != "${file%/}"; then
121 servedir "$realfile"
122 else
123 # redirect: append / for dirlisting.
124 httpheader '302 Found'
125 printf 'Location: %s\r\n\r\n' "/$file/"
126 fi
127 elif test "$requestpath" = "${scriptname}" && test -x "${scriptdir}/${basename}"; then
128 servescript "${scriptdir}/${basename}"
129 elif test -f "$realfile"; then
130 servefile "$realfile"
131 else
132 httpstatus "404 Not Found"
133 fi
134 fi