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("<", "\\&lt;", s);
           46         gsub(">", "\\&gt;", 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