diff --git a/ncat/scripts/httpd.lua b/ncat/scripts/httpd.lua new file mode 100644 index 000000000..ee0a0c403 --- /dev/null +++ b/ncat/scripts/httpd.lua @@ -0,0 +1,118 @@ +input = io.stdin:read("*line") + +server_headers = { + ["Server"] = "Ncat --lua-exec httpd.lua", + ["Connection"] = "close", +} + +function make_reply(params) + + if params["status"] then + print(params["status"].."\r") + else + print("HTTP/1.1 200 OK") + end + + --Not sure if it's exactly RFC RFC 1123 - no idea how Lua handles locales + --and what happens if the day is one-digit. TODO: check it. + print("Date: "..os.date("!%a, %d %b %Y %H:%M:%S GMT").."\r") + + for key, value in pairs(server_headers) do + print(string.format("%s: %s\r", key, value)) + end + + if params["headers"] then + for key, value in pairs(params["headers"]) do + print(string.format("%s: %s\r", key, value)) + end + end + + if params["data"] then + print("Content-length: "..string.len(params["data"].."\r")) + print("\r") + print(params["data"]) + else + print("\r") + end + os.exit(1) +end + +function do_400() + make_reply({ + ["status"] = "HTTP/1.1 400 Bad Request\r", + ["headers"] = {["Content-type"] = "text/html"}, + ["data"] = "

Bad request.

", + }) +end + +function do_403() + make_reply({ + ["status"] = "HTTP/1.1 403 Forbidden\r", + ["headers"] = {["Content-type"] = "text/html"}, + ["data"] = "

Forbidden.

", + }) +end + +function do_404() + make_reply({ + ["status"] = "HTTP/1.1 404 Not Found\r", + ["headers"] = {["Content-type"] = "text/html"}, + ["data"] = "

Not Found.

", + }) +end + + + +--We assume that: +-- * a method is alphanumeric uppercase, +-- * resource may contain anything that's not a space, +-- * protocol version is followed by a single space. +pattern = "([A-Z]+ )([^ ]+) ?(.*)" +method, resource, protocol = string.match(input, pattern) + +headers = {} +while true do + + input = io.stdin:read("*line") + if not input or input ~= "\r" then + break + end + + --header line is anything before the colon (at least one character), + --then there's space, and the value is anything that is left, + --again - at least one character. + key, value = string.match(input, "(.+): (.+)") + + if key == nil then + do_400() + end + + headers[key] = value +end + +--make sure that the resource starts with a slash. +if string.sub(resource, 0, 1) ~= '/' then + do_400() --could probably use a fancier error here. +end + +--now, remove the beginning slash +resource = string.sub(resource, 2, string.len(resource)) + +--if the resource was made of a slash only, let's make it index.html. +if resource == "" then + resource = "index.html" +end + +--if it starts with a dot, forbid any acccess to it. +if string.sub(resource, 0, 1) == "." then + do_403() --no hidden Unix files or simple directory traversal, sorry! +end + +--try to open the file... +f = io.open(resource, "r") +if not f then + do_404() --opening file failed, throw a 404. +end + +--and output it all. +print(f:read("*all"))