Switched to mmap for loading files. Added POST variable handling. Added
sandboxed loadstring, loadfile and dofile functions for Lua. Fixed bug where errors generated during request parsing may cause memory leaks on certain platforms. Fixed bug where non-string errors generated by Lua would cause a segmentation fault. Corrected various typos and unclear code comments.
This commit is contained in:
parent
95e1a981ab
commit
76d187e568
2
LICENCE
2
LICENCE
@ -9,7 +9,7 @@ are met:
|
|||||||
this list of conditions and the following disclaimer.
|
this list of conditions and the following disclaimer.
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
this list of conditions and the following disclaimer in the documentation
|
this list of conditions and the following disclaimer in the documentation
|
||||||
and/or other materials provided with the distribution.
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
1
Makefile
1
Makefile
@ -8,6 +8,7 @@ LDFLAGS=-O2 -Wl,-Bstatic -lfcgi -llua5.1 -Wl,-Bdynamic -lm -lpthread
|
|||||||
all: lua-fastcgi
|
all: lua-fastcgi
|
||||||
|
|
||||||
debug: CFLAGS+=-g -DDEBUG
|
debug: CFLAGS+=-g -DDEBUG
|
||||||
|
debug: LDFLAGS+=-lrt
|
||||||
debug: lua-fastcgi
|
debug: lua-fastcgi
|
||||||
|
|
||||||
lua-fastcgi: src/lua-fastcgi.o src/lfuncs.o src/lua.o src/config.o
|
lua-fastcgi: src/lua-fastcgi.o src/lfuncs.o src/lua.o src/config.o
|
||||||
|
@ -4,7 +4,7 @@ lua-fastcgi
|
|||||||
lua-fastcgi is a sandboxed Lua backend for FastCGI. That is, you can write
|
lua-fastcgi is a sandboxed Lua backend for FastCGI. That is, you can write
|
||||||
Lua scripts that serve up web pages. Options exist in lua-fastcgi.lua
|
Lua scripts that serve up web pages. Options exist in lua-fastcgi.lua
|
||||||
to configure a fixed amount of memory, cpu usage, and output bytes
|
to configure a fixed amount of memory, cpu usage, and output bytes
|
||||||
for each request. While sandboxed, lua-fastcgi supports a limit set
|
for each request. While sandboxed, lua-fastcgi supports a limited set
|
||||||
of functions. If sandboxing is disabled, lua-fastcgi loads the standard
|
of functions. If sandboxing is disabled, lua-fastcgi loads the standard
|
||||||
libraries and users may load modules as needed.
|
libraries and users may load modules as needed.
|
||||||
|
|
||||||
|
10
TODO
10
TODO
@ -1,17 +1,17 @@
|
|||||||
High Priority
|
High Priority
|
||||||
-------------
|
-------------
|
||||||
POST variable parsing
|
|
||||||
Basic sandboxed database support (e.g. GET/PUT)
|
Basic sandboxed database support (e.g. GET/PUT)
|
||||||
Sandboxed dofile() / loadfile() / loadstring()
|
Function for reading files into string
|
||||||
|
|
||||||
|
|
||||||
Medium Priority
|
Medium Priority
|
||||||
---------------
|
---------------
|
||||||
File Upload Support
|
File upload support
|
||||||
Database error logging
|
Logging of errors to database
|
||||||
Consider switching to mmap() for file reads
|
|
||||||
|
|
||||||
|
|
||||||
Low Priority
|
Low Priority
|
||||||
------------
|
------------
|
||||||
Database file adapter
|
Database file adapter
|
||||||
|
Session scoped variables
|
||||||
|
Application scoped variables
|
||||||
|
@ -30,7 +30,7 @@ return {
|
|||||||
|
|
||||||
-- Limit page output to x bytes or 0 for unlimited
|
-- Limit page output to x bytes or 0 for unlimited
|
||||||
-- Default: 65536
|
-- Default: 65536
|
||||||
output_max = 0,
|
output_max = 65536,
|
||||||
|
|
||||||
-- Default content type returned in header
|
-- Default content type returned in header
|
||||||
content_type = "text/html; charset=iso-8859-1"
|
content_type = "text/html; charset=iso-8859-1"
|
||||||
|
154
src/lfuncs.c
154
src/lfuncs.c
@ -17,9 +17,9 @@ static int LF_pprint(lua_State *l, int cr)
|
|||||||
int args = lua_gettop(l);
|
int args = lua_gettop(l);
|
||||||
|
|
||||||
// Fetch the response
|
// Fetch the response
|
||||||
lua_pushstring(l, "RESPONSE");
|
lua_pushstring(l, "STATE");
|
||||||
lua_rawget(l, LUA_REGISTRYINDEX);
|
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||||
LF_response *response = lua_touserdata(l, args+1);
|
LF_state *state = lua_touserdata(l, args+1);
|
||||||
lua_pop(l, 1);
|
lua_pop(l, 1);
|
||||||
|
|
||||||
// fetch limits
|
// fetch limits
|
||||||
@ -28,9 +28,8 @@ static int LF_pprint(lua_State *l, int cr)
|
|||||||
size_t *limit = lua_touserdata(l, args+1);
|
size_t *limit = lua_touserdata(l, args+1);
|
||||||
lua_pop(l, 1);
|
lua_pop(l, 1);
|
||||||
|
|
||||||
|
|
||||||
// If the response isn't committed, send the header
|
// If the response isn't committed, send the header
|
||||||
if(!response->committed){
|
if(!state->committed){
|
||||||
lua_getglobal(l, "HEADER");
|
lua_getglobal(l, "HEADER");
|
||||||
if(!lua_istable(l, args+1)){ luaL_error(l, "Invalid HEADER (Not table)."); }
|
if(!lua_istable(l, args+1)){ luaL_error(l, "Invalid HEADER (Not table)."); }
|
||||||
|
|
||||||
@ -51,10 +50,10 @@ static int LF_pprint(lua_State *l, int cr)
|
|||||||
*limit -= (len+10);
|
*limit -= (len+10);
|
||||||
}
|
}
|
||||||
|
|
||||||
FCGX_PutStr("Status: ", 8, response->out);
|
FCGX_PutStr("Status: ", 8, state->response);
|
||||||
FCGX_PutStr(str, len, response->out);
|
FCGX_PutStr(str, len, state->response);
|
||||||
FCGX_PutStr("\r\n", 2, response->out);
|
FCGX_PutStr("\r\n", 2, state->response);
|
||||||
response->committed = 1;
|
state->committed = 1;
|
||||||
}
|
}
|
||||||
lua_pop(l, 1); // Pop the status
|
lua_pop(l, 1); // Pop the status
|
||||||
|
|
||||||
@ -82,12 +81,12 @@ static int LF_pprint(lua_State *l, int cr)
|
|||||||
*limit -= (vallen+keylen+4);
|
*limit -= (vallen+keylen+4);
|
||||||
}
|
}
|
||||||
|
|
||||||
FCGX_PutStr(key, keylen, response->out);
|
FCGX_PutStr(key, keylen, state->response);
|
||||||
FCGX_PutStr(": ", 2, response->out);
|
FCGX_PutStr(": ", 2, state->response);
|
||||||
FCGX_PutStr(val, vallen, response->out);
|
FCGX_PutStr(val, vallen, state->response);
|
||||||
FCGX_PutStr("\r\n", 2, response->out);
|
FCGX_PutStr("\r\n", 2, state->response);
|
||||||
|
|
||||||
response->committed = 1;
|
state->committed = 1;
|
||||||
lua_pop(l, 1); // Clear the last value out
|
lua_pop(l, 1); // Clear the last value out
|
||||||
}
|
}
|
||||||
lua_pop(l, 1); // Clear the table out
|
lua_pop(l, 1); // Clear the table out
|
||||||
@ -97,8 +96,8 @@ static int LF_pprint(lua_State *l, int cr)
|
|||||||
*limit -= 2;
|
*limit -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
FCGX_PutS("\r\n", response->out);
|
FCGX_PutS("\r\n", state->response);
|
||||||
response->committed = 1;
|
state->committed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t strlen;
|
size_t strlen;
|
||||||
@ -114,7 +113,7 @@ static int LF_pprint(lua_State *l, int cr)
|
|||||||
*limit -= strlen;
|
*limit -= strlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
FCGX_PutStr(str, strlen, response->out);
|
FCGX_PutStr(str, strlen, state->response);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: /* Ignore other types */ break;
|
default: /* Ignore other types */ break;
|
||||||
@ -127,7 +126,7 @@ static int LF_pprint(lua_State *l, int cr)
|
|||||||
(*limit)--;
|
(*limit)--;
|
||||||
}
|
}
|
||||||
|
|
||||||
FCGX_PutChar('\n', response->out);
|
FCGX_PutChar('\n', state->response);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -135,3 +134,124 @@ static int LF_pprint(lua_State *l, int cr)
|
|||||||
|
|
||||||
int LF_print(lua_State *l){ return LF_pprint(l, 1); }
|
int LF_print(lua_State *l){ return LF_pprint(l, 1); }
|
||||||
int LF_write(lua_State *l){ return LF_pprint(l, 0); }
|
int LF_write(lua_State *l){ return LF_pprint(l, 0); }
|
||||||
|
|
||||||
|
|
||||||
|
int LF_loadstring(lua_State *l)
|
||||||
|
{
|
||||||
|
size_t sz;
|
||||||
|
const char *s = luaL_checklstring(l, 1, &sz);
|
||||||
|
|
||||||
|
if(sz > 3 && memcmp(s, LUA_SIGNATURE, 4) == 0){
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "Compiled bytecode not supported.");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(luaL_loadbuffer(l, s, sz, luaL_optstring(l, 2, s)) == 0){
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_insert(l, -2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int LF_loadfile(lua_State *l)
|
||||||
|
{
|
||||||
|
size_t sz;
|
||||||
|
const char *spath = luaL_checklstring(l, 1, &sz);
|
||||||
|
|
||||||
|
lua_pushstring(l, "DOCUMENT_ROOT");
|
||||||
|
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||||
|
char *document_root = lua_touserdata(l, -1);
|
||||||
|
lua_pop(l, 1);
|
||||||
|
|
||||||
|
if(document_root == NULL){
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "DOCUMENT_ROOT not defined.");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dz = strlen(document_root);
|
||||||
|
|
||||||
|
if(dz == 0){
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "DOCUMENT_ROOT empty.");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hz = dz + sz;
|
||||||
|
if((hz + 2) > 4096){
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "Path too large.");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
char hpath[4096];
|
||||||
|
memcpy(&hpath[0], document_root, dz);
|
||||||
|
if(hpath[dz-1] != '/' && spath[0] != '/'){ hpath[dz] = '/'; }
|
||||||
|
memcpy(&hpath[dz+1], spath, sz);
|
||||||
|
hpath[hz+1] = 0;
|
||||||
|
|
||||||
|
char rpath[4096];
|
||||||
|
char *ptr = realpath(hpath, rpath);
|
||||||
|
if(ptr == NULL || memcmp(document_root, rpath, dz) != 0){
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "Invalid path.");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(LF_fileload(l, &spath[0], &hpath[0])){
|
||||||
|
case 0:
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LF_ERRACCESS:
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "Access denied.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LF_ERRMEMORY:
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "Not enough memory.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LF_ERRNOTFOUND:
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "No such file or directory.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LF_ERRSYNTAX:
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_insert(l, -2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LF_ERRBYTECODE:
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "Compiled bytecode not supported.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LF_ERRNOPATH:
|
||||||
|
case LF_ERRNONAME:
|
||||||
|
lua_pushnil(l);
|
||||||
|
lua_pushstring(l, "Invalid path.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int LF_dofile(lua_State *l)
|
||||||
|
{
|
||||||
|
int r = LF_loadfile(l);
|
||||||
|
if(r == 1 && lua_isfunction(l, -1)){
|
||||||
|
lua_call(l, 0, LUA_MULTRET);
|
||||||
|
return lua_gettop(l) - 1;
|
||||||
|
} else {
|
||||||
|
lua_error(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
10
src/lfuncs.h
10
src/lfuncs.h
@ -1,4 +1,14 @@
|
|||||||
// Writes FCGI output followed by a carriage return
|
// Writes FCGI output followed by a carriage return
|
||||||
int LF_print(lua_State *);
|
int LF_print(lua_State *);
|
||||||
|
|
||||||
// Writes FCGI output without a carriage return
|
// Writes FCGI output without a carriage return
|
||||||
int LF_write(lua_State *);
|
int LF_write(lua_State *);
|
||||||
|
|
||||||
|
// loadstring() function with anti-bytecode security measures
|
||||||
|
int LF_loadstring(lua_State *);
|
||||||
|
|
||||||
|
// loadfile() function with sandboxing security measures
|
||||||
|
int LF_loadfile(lua_State *);
|
||||||
|
|
||||||
|
// dofile() function with sandboxing security measures
|
||||||
|
int LF_dofile(lua_State *);
|
||||||
|
@ -25,12 +25,12 @@ static char *http_status_strings[] = {
|
|||||||
|
|
||||||
|
|
||||||
#define senderror(status_code,error_string) \
|
#define senderror(status_code,error_string) \
|
||||||
if(!response.committed){ \
|
if(!state.committed){ \
|
||||||
FCGX_FPrintF(request.out, "Status: %d %s\r\n", status_code, http_status_strings[status_code]); \
|
FCGX_FPrintF(request.out, "Status: %d %s\r\n", status_code, http_status_strings[status_code]); \
|
||||||
FCGX_FPrintF(request.out, "Content-Type: %s\r\n\r\n", config->content_type); \
|
FCGX_FPrintF(request.out, "Content-Type: %s\r\n\r\n", config->content_type); \
|
||||||
response.committed = 1; \
|
state.committed = 1; \
|
||||||
} \
|
} \
|
||||||
FCGX_PutS(error_string, response.out);
|
FCGX_PutS(error_string, state.response);
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -64,7 +64,7 @@ void *thread_run(void *arg)
|
|||||||
LF_params *params = arg;
|
LF_params *params = arg;
|
||||||
LF_config *config = params->config;
|
LF_config *config = params->config;
|
||||||
LF_limits *limits = LF_newlimits();
|
LF_limits *limits = LF_newlimits();
|
||||||
LF_response response;
|
LF_state state;
|
||||||
lua_State *l;
|
lua_State *l;
|
||||||
|
|
||||||
FCGX_Request request;
|
FCGX_Request request;
|
||||||
@ -86,31 +86,43 @@ void *thread_run(void *arg)
|
|||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printvars(&request);
|
printvars(&request);
|
||||||
|
struct timespec rstart, rend;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &rstart);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LF_parserequest(l, &request, &state);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &rend);
|
||||||
|
// Assumes the request returns in less than a second (which it should)
|
||||||
|
printf("Request parsed in %luns\n", (rend.tv_nsec-rstart.tv_nsec));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LF_parserequest(l, &request, &response);
|
|
||||||
LF_enablelimits(l, limits);
|
LF_enablelimits(l, limits);
|
||||||
|
|
||||||
switch(LF_loadfile(l)){
|
switch(LF_loadscript(l)){
|
||||||
case 0:
|
case 0:
|
||||||
if(lua_pcall(l, 0, 0, 0)){
|
if(lua_pcall(l, 0, 0, 0)){
|
||||||
senderror(500, lua_tostring(l, -1));
|
if(lua_isstring(l, -1)){
|
||||||
} else if(!response.committed){
|
senderror(500, lua_tostring(l, -1));
|
||||||
|
} else {
|
||||||
|
senderror(500, "unspecified lua error");
|
||||||
|
}
|
||||||
|
} else if(!state.committed){
|
||||||
senderror(200, "");
|
senderror(200, "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EACCES:
|
case LF_ERRACCESS: senderror(403, "access denied"); break;
|
||||||
senderror(403, lua_tostring(l, -1));
|
case LF_ERRMEMORY: senderror(500, "not enough memory"); break;
|
||||||
break;
|
case LF_ERRNOTFOUND:
|
||||||
|
printf("404\n");
|
||||||
case ENOENT:
|
senderror(404, "no such file or directory");
|
||||||
senderror(404, lua_tostring(l, -1));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
senderror(500, lua_tostring(l, -1));
|
|
||||||
break;
|
break;
|
||||||
|
case LF_ERRSYNTAX: senderror(500, lua_tostring(l, -1)); break;
|
||||||
|
case LF_ERRBYTECODE: senderror(403, "compiled bytecode not supported"); break;
|
||||||
|
case LF_ERRNOPATH: senderror(500, "SCRIPT_FILENAME not provided"); break;
|
||||||
|
case LF_ERRNONAME: senderror(500, "SCRIPT_NAME not provided"); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
FCGX_Finish_r(&request);
|
FCGX_Finish_r(&request);
|
||||||
|
206
src/lua.c
206
src/lua.c
@ -1,10 +1,13 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
||||||
@ -17,14 +20,6 @@
|
|||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
#include "lfuncs.h"
|
#include "lfuncs.h"
|
||||||
|
|
||||||
#define LF_BUFFERSIZE 4096
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int fd;
|
|
||||||
char buffer[LF_BUFFERSIZE];
|
|
||||||
size_t total;
|
|
||||||
} LF_loaderdata;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void LF_printstack(lua_State *l)
|
void LF_printstack(lua_State *l)
|
||||||
@ -119,7 +114,6 @@ void LF_setlimits(LF_limits *limits, size_t memory, size_t output, uint32_t cpu_
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void LF_enablelimits(lua_State *l, LF_limits *limits)
|
void LF_enablelimits(lua_State *l, LF_limits *limits)
|
||||||
{
|
{
|
||||||
if(limits->cpu.tv_usec > 0 || limits->cpu.tv_sec > 0){
|
if(limits->cpu.tv_usec > 0 || limits->cpu.tv_sec > 0){
|
||||||
@ -142,7 +136,13 @@ void LF_enablelimits(lua_State *l, LF_limits *limits)
|
|||||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(limits->memory){ lua_setallocf(l, &LF_limit_alloc, &limits->memory); }
|
if(limits->memory){
|
||||||
|
lua_pushstring(l, "MEMORY_LIMIT");
|
||||||
|
lua_pushlightuserdata(l, &limits->memory);
|
||||||
|
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
lua_setallocf(l, &LF_limit_alloc, &limits->memory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -174,13 +174,16 @@ lua_State *LF_newstate(int sandbox, char *content_type)
|
|||||||
lua_call(l, 1, 0);
|
lua_call(l, 1, 0);
|
||||||
|
|
||||||
// Nil out unsafe functions/objects
|
// Nil out unsafe functions/objects
|
||||||
LF_nilglobal(l, "dofile");
|
|
||||||
LF_nilglobal(l, "load");
|
LF_nilglobal(l, "load");
|
||||||
LF_nilglobal(l, "loadfile");
|
|
||||||
LF_nilglobal(l, "xpcall");
|
LF_nilglobal(l, "xpcall");
|
||||||
LF_nilglobal(l, "pcall");
|
LF_nilglobal(l, "pcall");
|
||||||
LF_nilglobal(l, "module");
|
LF_nilglobal(l, "module");
|
||||||
LF_nilglobal(l, "require");
|
LF_nilglobal(l, "require");
|
||||||
|
|
||||||
|
// Override unsafe functions
|
||||||
|
lua_register(l, "loadstring", &LF_loadstring);
|
||||||
|
lua_register(l, "loadfile", &LF_loadfile);
|
||||||
|
lua_register(l, "dofile", &LF_dofile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the print function
|
// Register the print function
|
||||||
@ -202,7 +205,7 @@ lua_State *LF_newstate(int sandbox, char *content_type)
|
|||||||
|
|
||||||
|
|
||||||
// Set GET variables
|
// Set GET variables
|
||||||
static void LF_parsequerystring(lua_State *l, char *query_string)
|
static void LF_parsequerystring(lua_State *l, char *query_string, char *table)
|
||||||
{
|
{
|
||||||
lua_newtable(l);
|
lua_newtable(l);
|
||||||
|
|
||||||
@ -262,7 +265,7 @@ static void LF_parsequerystring(lua_State *l, char *query_string)
|
|||||||
if(lua_gettop(l) == (stack+2)){ lua_rawset(l, stack); }
|
if(lua_gettop(l) == (stack+2)){ lua_rawset(l, stack); }
|
||||||
|
|
||||||
// Finally, set the table
|
// Finally, set the table
|
||||||
lua_setglobal(l, "GET");
|
lua_setglobal(l, table);
|
||||||
return;
|
return;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -275,8 +278,17 @@ static void LF_parsequerystring(lua_State *l, char *query_string)
|
|||||||
|
|
||||||
|
|
||||||
// Parses fastcgi request
|
// Parses fastcgi request
|
||||||
void LF_parserequest(lua_State *l, FCGX_Request *request, LF_response *response)
|
void LF_parserequest(lua_State *l, FCGX_Request *request, LF_state *state)
|
||||||
{
|
{
|
||||||
|
uintmax_t content_length = 0;
|
||||||
|
char *content_type = NULL;
|
||||||
|
|
||||||
|
state->committed = 0;
|
||||||
|
state->response = request->out;
|
||||||
|
lua_pushstring(l, "STATE");
|
||||||
|
lua_pushlightuserdata(l, state);
|
||||||
|
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
lua_newtable(l);
|
lua_newtable(l);
|
||||||
for(char **p = request->envp; *p; ++p){
|
for(char **p = request->envp; *p; ++p){
|
||||||
char *vptr = strchr(*p, '=');
|
char *vptr = strchr(*p, '=');
|
||||||
@ -285,77 +297,129 @@ void LF_parserequest(lua_State *l, FCGX_Request *request, LF_response *response)
|
|||||||
lua_pushlstring(l, *p, keylen); // Push Key
|
lua_pushlstring(l, *p, keylen); // Push Key
|
||||||
lua_pushstring(l, (vptr+1)); // Push Value
|
lua_pushstring(l, (vptr+1)); // Push Value
|
||||||
lua_rawset(l, 1); // Set key/value into table
|
lua_rawset(l, 1); // Set key/value into table
|
||||||
|
|
||||||
|
switch(keylen){
|
||||||
|
case 11:
|
||||||
|
if(memcmp(*p, "SCRIPT_NAME", 11) == 0){
|
||||||
|
lua_pushstring(l, "SCRIPT_NAME");
|
||||||
|
lua_pushlightuserdata(l, (vptr+1));
|
||||||
|
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if(keylen == 12 && memcmp(*p, "QUERY_STRING", 12) == 0){
|
case 12:
|
||||||
LF_parsequerystring(l, (vptr+1));
|
if(memcmp(*p, "QUERY_STRING", 12) == 0){
|
||||||
|
LF_parsequerystring(l, (vptr+1), "GET");
|
||||||
|
} if(memcmp(*p, "CONTENT_TYPE", 12) == 0){
|
||||||
|
content_type = (vptr+1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 13:
|
||||||
|
if(memcmp(*p, "DOCUMENT_ROOT", 13) == 0){
|
||||||
|
lua_pushstring(l, "DOCUMENT_ROOT");
|
||||||
|
lua_pushlightuserdata(l, (vptr+1));
|
||||||
|
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 14:
|
||||||
|
if(memcmp(*p, "CONTENT_LENGTH", 14) == 0){
|
||||||
|
content_length = strtoumax((vptr+1), NULL, 10);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 15:
|
||||||
|
if(memcmp(*p, "SCRIPT_FILENAME", 15) == 0){
|
||||||
|
lua_pushstring(l, "SCRIPT_FILENAME");
|
||||||
|
lua_pushlightuserdata(l, (vptr+1));
|
||||||
|
lua_rawset(l, LUA_REGISTRYINDEX);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lua_setglobal(l, "REQUEST");
|
lua_setglobal(l, "REQUEST");
|
||||||
|
|
||||||
response->committed = 0;
|
if(content_length > 0 && content_type != NULL && memcmp(content_type, "application/x-www-form-urlencoded", 33) == 0){
|
||||||
response->out = request->out;
|
char *content = lua_newuserdata(l, content_length+1);
|
||||||
lua_pushstring(l, "RESPONSE");
|
int r = FCGX_GetStr(
|
||||||
lua_pushlightuserdata(l, response);
|
content, (content_length > INT_MAX ? INT_MAX : content_length),
|
||||||
lua_rawset(l, LUA_REGISTRYINDEX);
|
request->in
|
||||||
}
|
);
|
||||||
|
*(content + r) = 0; // Add NUL byte at end for proper string
|
||||||
|
LF_parsequerystring(l, content, "POST");
|
||||||
static const char *LF_filereader(lua_State *l, void *data, size_t *size)
|
lua_pop(l, 1);
|
||||||
{
|
|
||||||
LF_loaderdata *ld = data;
|
|
||||||
|
|
||||||
*size = read(ld->fd, ld->buffer, LF_BUFFERSIZE);
|
|
||||||
|
|
||||||
if(ld->total == 0 && *size > 3){
|
|
||||||
if(memcmp(ld->buffer, LUA_SIGNATURE, 4) == 0){
|
|
||||||
luaL_error(l, "Compiled bytecode not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(*size){
|
|
||||||
case 0: return NULL;
|
|
||||||
case -1: luaL_error(l, strerror(errno));
|
|
||||||
default:
|
|
||||||
ld->total += *size;
|
|
||||||
return ld->buffer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Loads a lua file into a state
|
// Load script by name and path
|
||||||
int LF_loadfile(lua_State *l)
|
int LF_fileload(lua_State *l, const char *name, char *scriptpath)
|
||||||
{
|
{
|
||||||
lua_getglobal(l, "REQUEST");
|
char *script = NULL;
|
||||||
|
int fd = -1, r = 0;
|
||||||
int stack = lua_gettop(l);
|
struct stat sb;
|
||||||
|
|
||||||
lua_pushstring(l, "SCRIPT_FILENAME");
|
if(scriptpath == NULL){ return LF_ERRNOPATH; }
|
||||||
lua_rawget(l, stack);
|
if(name == NULL){ return LF_ERRNONAME; }
|
||||||
const char *path = lua_tostring(l, stack+1);
|
|
||||||
|
|
||||||
lua_pushstring(l, "SCRIPT_NAME");
|
|
||||||
lua_rawget(l, stack);
|
|
||||||
const char *name = lua_tostring(l, stack+2);
|
|
||||||
|
|
||||||
LF_loaderdata ld;
|
|
||||||
ld.total = 0;
|
|
||||||
ld.fd = open(path, O_RDONLY);
|
|
||||||
if(ld.fd == -1){
|
|
||||||
lua_pushstring(l, strerror(errno));
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a string with an '=' followed by the script name
|
// Generate a string with an '=' followed by the script name
|
||||||
// this ensures lua will generation a reasonable error
|
// this ensures lua will generation a reasonable error
|
||||||
size_t len = strlen(name) + 1;
|
size_t namelen = strlen(name);
|
||||||
char scriptname[len + 1];
|
char scriptname[namelen+2];
|
||||||
scriptname[0] = '=';
|
scriptname[0] = '=';
|
||||||
memcpy(&scriptname[1], name, len);
|
memcpy(&scriptname[1], name, namelen+1);
|
||||||
|
|
||||||
int r = lua_load(l, &LF_filereader, &ld, scriptname);
|
|
||||||
|
|
||||||
close(ld.fd);
|
if((fd = open(scriptpath, O_RDONLY)) == -1){ goto errorL; }
|
||||||
return (r == 0 ? 0 : ENOMSG);
|
|
||||||
|
if(fstat(fd, &sb) == -1){ goto errorL; }
|
||||||
|
|
||||||
|
if((script = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0)) == NULL){
|
||||||
|
goto errorL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(madvise(script, sb.st_size, MADV_SEQUENTIAL) == -1){ goto errorL; }
|
||||||
|
|
||||||
|
if(sb.st_size > 3 && memcmp(script, LUA_SIGNATURE, 4) == 0){
|
||||||
|
r = LF_ERRBYTECODE;
|
||||||
|
} else {
|
||||||
|
switch(luaL_loadbuffer(l, script, sb.st_size, scriptname)){
|
||||||
|
case LUA_ERRSYNTAX: r = LF_ERRSYNTAX; break;
|
||||||
|
case LUA_ERRMEM: r = LF_ERRMEMORY; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(script != NULL){ munmap(script, sb.st_size); }
|
||||||
|
if(fd != -1){ close(fd); }
|
||||||
|
return r;
|
||||||
|
|
||||||
|
errorL:
|
||||||
|
if(script != NULL){ munmap(script, sb.st_size); }
|
||||||
|
if(fd != -1){ close(fd); }
|
||||||
|
switch(errno){
|
||||||
|
case EACCES: return r = LF_ERRACCESS;
|
||||||
|
case ENOENT: return r = LF_ERRNOTFOUND;
|
||||||
|
case ENOMEM: return r = LF_ERRMEMORY;
|
||||||
|
default: return r = LF_ERRANY;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Loads script specified in registryindex into lua state
|
||||||
|
int LF_loadscript(lua_State *l)
|
||||||
|
{
|
||||||
|
lua_pushstring(l, "SCRIPT_FILENAME");
|
||||||
|
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||||
|
char *scriptpath = lua_touserdata(l, 1);
|
||||||
|
lua_pop(l, 1);
|
||||||
|
|
||||||
|
lua_pushstring(l, "SCRIPT_NAME");
|
||||||
|
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||||
|
char *name = lua_touserdata(l, 1);
|
||||||
|
lua_pop(l, 1);
|
||||||
|
|
||||||
|
return LF_fileload(l, name, scriptpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
19
src/lua.h
19
src/lua.h
@ -1,7 +1,17 @@
|
|||||||
|
#define LF_ERRNONE 0
|
||||||
|
#define LF_ERRANY 1
|
||||||
|
#define LF_ERRACCESS 2
|
||||||
|
#define LF_ERRMEMORY 3
|
||||||
|
#define LF_ERRNOTFOUND 4
|
||||||
|
#define LF_ERRSYNTAX 5
|
||||||
|
#define LF_ERRBYTECODE 6
|
||||||
|
#define LF_ERRNOPATH 7
|
||||||
|
#define LF_ERRNONAME 8
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
FCGX_Stream *response;
|
||||||
int committed;
|
int committed;
|
||||||
FCGX_Stream *out;
|
} LF_state;
|
||||||
} LF_response;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t memory;
|
size_t memory;
|
||||||
@ -14,7 +24,8 @@ lua_State *LF_newstate(int, char *);
|
|||||||
LF_limits *LF_newlimits();
|
LF_limits *LF_newlimits();
|
||||||
void LF_setlimits(LF_limits *, size_t, size_t, uint32_t, uint32_t);
|
void LF_setlimits(LF_limits *, size_t, size_t, uint32_t, uint32_t);
|
||||||
void LF_enablelimits(lua_State *, LF_limits *);
|
void LF_enablelimits(lua_State *, LF_limits *);
|
||||||
void LF_parserequest(lua_State *l, FCGX_Request *, LF_response *);
|
void LF_parserequest(lua_State *l, FCGX_Request *, LF_state *);
|
||||||
void LF_emptystack(lua_State *);
|
void LF_emptystack(lua_State *);
|
||||||
int LF_loadfile(lua_State *);
|
int LF_fileload(lua_State *, const char *, char *);
|
||||||
|
int LF_loadscript(lua_State *);
|
||||||
void LF_closestate(lua_State *);
|
void LF_closestate(lua_State *);
|
||||||
|
Reference in New Issue
Block a user