#!/usr/local/bin/gawk -f # # awwwk -- HTTPd by awk # 2003/02/04 YAMAGUCHI Takanori # # gawk, nawk などで動作すると思うが、いちいち調べてないので確証はない。 # # 主な機能: # - リクエストされたファイルを返すだけの単純な httpd # - syslog によるアクセスロギング # - バーチャルホスト対応 # - CGI サポート(GET のみ) # - ただしロクにテストしてないのでちゃんと動くかどうかは知らん # # 起動方法: # djb の ucspi-tcp に含まれる tcpserver を使う。 # tcpserver -R -u $uid -g $gid 0 80 gawk -f /some/where/awwwk > /dev/null 2>&1 & # $uid, $gid は権限の低い任意のユーザID。uid=`id -u nobody` # inetd や xinetd 経由でも動くはずだが(試してはいない)、 # この場合は接続元がアクセスログに残らない。 # BEGIN{ # 環境設定 # MIME タイプ directoryindex = "index.html" typesconfig = "/usr/local/apache/conf/mime.types" defaulttype = "text/plain" # ロギング logger = "/usr/bin/logger" # ログ書き込みプログラム logname = "awwwk" # タグ logfacl = "user" # facility # ステータスコードとエラー status[200] = "OK" status[400] = "Bad Request" status[403] = "Forbidden" status[404] = "Not Found" status[405] = "Method Not Allowed" status[500] = "Internal Server Error" status[505] = "HTTP Version Not Supported" documentroot["error"] = "/home/www/error" errordocument[400] = "400.html" errordocument[403] = "403.html" errordocument[404] = "404.html" errordocument[405] = "405.html" errordocument[500] = "500.html" errordocument[505] = "505.html" # main サーバ servername["default"] = "main.server" documentroot["default"] = "/home/www/main" # バーチャルホスト # servername["virtual.host"] = ""virtual.host" # documentroot["virtual.host"] = "/home/www/virtual" } BEGIN{ # CGI に渡す環境変数 # tcpserver または inetd + tcp-env から起動されている必要がある env["REMOTE_ADDR"] = ENVIRON["TCPREMOTEIP"] env["REMOTE_PORT"] = ENVIRON["TCPREMOTEPORT"] if("TCPREMOTEHOST" in ENVIRON){ env["REMOTE_HOST"] = ENVIRON["TCPREMOTEHOST"] } else{ env["REMOTE_HOST"] = ENVIRON["TCPREMOTEIP"] } env["SERVER_ADDR"] = ENVIRON["TCPLOCALIP"] env["SERVER_PORT"] = ENVIRON["TCPLOCALPORT"] if("TCPLOCALHOST" in ENVIRON){ env["SERVER_NAME"] = ENVIRON["TCPLOCALHOST"] } } BEGIN{ IGNORECASE = 1 } { sub(/\r$/, "") } NR == 1{ request = $0 env["REQUEST_METHOD"] = $1 env["REQUEST_URI"] = $2 protocol = $3 trash = $4 } /^$/ { exit } NR > 1{ v1 = $1 v2 = $0 sub(v1 "[ \t]*", "", v2) sub(/:/, "", v1) gsub(/-/, "_", v1) env["HTTP_" toupper(v1)] = v2 } END{ IGNORECASE = 0 syslog(request, "info") if(!env["REQUEST_URI"]) error(400) if(trash) error(400) if(protocol !~ /^HTTP\/(1\.0|1\.1)$/) error(505) file = uri2file(env["REQUEST_URI"]) if(file ~ /\.cgi$/){ handler_cgi(env["REQUEST_URI"], file) } else if(env["REQUEST_METHOD"] == "GET"){ get_document(env["REQUEST_URI"], file) } else if(env["REQUEST_METHOD"] == "HEAD"){ head_document(env["REQUEST_URI"], file) } else{ error(405) } } function get_document(uri, file){ response_head(uri, file) response_body(uri, file) } function head_document(uri, file){ response_head(uri, file) } function uri2file(uri, docroot, file){ if(env["HTTP_HOST"] in documentroot || substr(match(env["HTTP_HOST"], /:/), 1, RSTART-1) in documentroot){ docroot = documentroot[env["HTTP_HOST"]] } else{ docroot = documentroot["default"] } file = uri if(file ~ /\/\.\./) error(403) # ../../../etc/passwd とかを拒否 if(file ~ /\/\.[^\/]*$/) error(403) # ドットファイル不可 if(file ~ /^\//){ if(match(file, /\?.*$/)){ env["QUERY_STRING"] = substr(uri, RSTART + 1) file = substr(uri, 1, RSTART - 1) } if(file ~ /\/$/){ file = file directoryindex } env["DOCUMENT_ROOT"] = docroot env["DOCUMENT_URI"] = file return docroot file } else{ error(403) } } function response_head(uri, file, code, contenttype){ if(is_file_exist(file) < 0){ if(errflg) fatalerror() error(404) } if(!code) code = 200 match(file, /\.[^.]+$/) contenttype = get_type(substr(file, RSTART + 1)) if(contenttype == "") contenttype = defaulttype print "HTTP/1.0 " code " " status[code] "\r" print "Content-Type: " contenttype "\r" print "\r" } function response_body(uri, file){ while(getline < file > 0) print close(file) } function handler_cgi(uri, file, cmd, d, e, i){ if(is_file_exist(file) < 0) error(404) if(file ~ /[;`'\"*?&|$<>\[\](){}]/) error(403) # sh で危険な文字はこのぐらい? d = file sub(/\/[^\/]*$/, "", d) env["SCRIPT_FILENAME"] = file env["SCRIPT_NAME"] = file sub(d, "", env["SCRIPT_NAME"]) for(i in env){ e = e i "='" env[i] "' " } cmd = "cd " d "; /usr/bin/env " e file print "HTTP/1.0 200 OK\r" if(env["REQUEST_METHOD"] == "HEAD"){ while(cmd | getline > 0){ print if(/^\r?$/) break } } else{ while(cmd | getline > 0) print } close(cmd) } function error(code, errordoc){ errflg = 1 errordoc = documentroot["error"] "/" errordocument[code] response_head(uri, errordoc, code) if(env["REQUEST_METHOD"] != "HEAD"){ response_body(uri, errordoc) } syslog(code " " (ERRNO ? ERRNO : status[code]), "err") exit(code) } function fatalerror(){ print "HTTP/1.0 500 Internal Server Error\r" print "Content-Type: text/plain\r" print "\r" print "Internal Server Error" syslog(ERRNO, "crit") exit(500) } function get_type(ext, s, a, i, r){ while(getline s < typesconfig > 0){ if(/^[ \t]*#/) continue i = split(s, a) while(--i>1){ if(a[i] == ext){ r = a[1] break } } } close(typesconfig) return (r == "") ? defaulttype : r } function is_file_exist(filename, r, s){ r = getline s < filename; close(filename); return r; } function syslog(mesg, facl){ if(facl == "") facl = "info" system(logger " -t " logname " -p " logfacl "." facl " " mesg) }