a sandboxed Lua backend for FastCGI
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

257 lines
5.4 KiB

  1. #include <stdlib.h>
  2. #include <stdint.h>
  3. #include <string.h>
  4. #include <fcgiapp.h>
  5. #include <lua5.1/lua.h>
  6. #include <lua5.1/lauxlib.h>
  7. #include "lua.h"
  8. #include "lfuncs.h"
  9. // replacement print function, outputs to FCGI stream
  10. static int LF_pprint(lua_State *l, int cr)
  11. {
  12. int args = lua_gettop(l);
  13. // Fetch the response
  14. lua_pushstring(l, "STATE");
  15. lua_rawget(l, LUA_REGISTRYINDEX);
  16. LF_state *state = lua_touserdata(l, args+1);
  17. lua_pop(l, 1);
  18. // fetch limits
  19. lua_pushstring(l, "RESPONSE_LIMIT");
  20. lua_rawget(l, LUA_REGISTRYINDEX);
  21. size_t *limit = lua_touserdata(l, args+1);
  22. lua_pop(l, 1);
  23. // If the response isn't committed, send the header
  24. if(!state->committed){
  25. lua_getglobal(l, "HEADER");
  26. if(!lua_istable(l, args+1)){ luaL_error(l, "Invalid HEADER (Not table)."); }
  27. lua_pushstring(l, "Status");
  28. lua_rawget(l, args+1);
  29. // If the status has been explicitly set, send that
  30. if(!lua_isnil(l, args+2)){
  31. if(!lua_isstring(l, args+2)){
  32. luaL_error(l, "Invalid HEADER (Invalid Status).");
  33. }
  34. size_t len;
  35. const char *str = lua_tolstring(l, args+2, &len);
  36. if(limit){
  37. if((len+10) > *limit){ luaL_error(l, "Output limit exceeded."); }
  38. *limit -= (len+10);
  39. }
  40. FCGX_PutStr("Status: ", 8, state->response);
  41. FCGX_PutStr(str, len, state->response);
  42. FCGX_PutStr("\r\n", 2, state->response);
  43. state->committed = 1;
  44. }
  45. lua_pop(l, 1); // Pop the status
  46. // Loop over the header, ignoring status, but sending everything else
  47. lua_pushnil(l);
  48. while(lua_next(l, args+1)){
  49. // If the key or the value isn't a string (or number) throw an error
  50. if(!lua_isstring(l, args+2) || !lua_isstring(l, args+3)){
  51. luaL_error(l, "Invalid HEADER (Invalid key and/or value).");
  52. }
  53. size_t keylen = 0;
  54. const char *key = lua_tolstring(l, args+2, &keylen);
  55. if(keylen == 6 && memcmp(key, "Status", 6) == 0){
  56. // Clear the last value out
  57. lua_pop(l, 1);
  58. continue;
  59. }
  60. size_t vallen = 0;
  61. const char *val = lua_tolstring(l, args+3, &vallen);
  62. if(limit){
  63. if((vallen+keylen+4) > *limit){ luaL_error(l, "Output limit exceeded."); }
  64. *limit -= (vallen+keylen+4);
  65. }
  66. FCGX_PutStr(key, keylen, state->response);
  67. FCGX_PutStr(": ", 2, state->response);
  68. FCGX_PutStr(val, vallen, state->response);
  69. FCGX_PutStr("\r\n", 2, state->response);
  70. state->committed = 1;
  71. lua_pop(l, 1); // Clear the last value out
  72. }
  73. lua_pop(l, 1); // Clear the table out
  74. if(limit){
  75. if(2 >= *limit){ luaL_error(l, "Output limit exceeded."); }
  76. *limit -= 2;
  77. }
  78. FCGX_PutS("\r\n", state->response);
  79. state->committed = 1;
  80. }
  81. size_t strlen;
  82. const char *str;
  83. for(int i=1; i <= args; i++){
  84. switch(lua_type(l, i)){
  85. case LUA_TSTRING:
  86. case LUA_TNUMBER:
  87. case LUA_TBOOLEAN:
  88. str = lua_tolstring(l, i, &strlen);
  89. if(limit){
  90. if(strlen > *limit){ luaL_error(l, "Output limit exceeded."); }
  91. *limit -= strlen;
  92. }
  93. FCGX_PutStr(str, strlen, state->response);
  94. break;
  95. default: /* Ignore other types */ break;
  96. }
  97. }
  98. if(cr){
  99. if(limit){
  100. if(*limit == 0){ luaL_error(l, "Output limit exceeded."); }
  101. (*limit)--;
  102. }
  103. FCGX_PutChar('\n', state->response);
  104. }
  105. return 0;
  106. }
  107. int LF_print(lua_State *l){ return LF_pprint(l, 1); }
  108. int LF_write(lua_State *l){ return LF_pprint(l, 0); }
  109. int LF_loadstring(lua_State *l)
  110. {
  111. size_t sz;
  112. const char *s = luaL_checklstring(l, 1, &sz);
  113. if(sz > 3 && memcmp(s, LUA_SIGNATURE, 4) == 0){
  114. lua_pushnil(l);
  115. lua_pushstring(l, "Compiled bytecode not supported.");
  116. return 2;
  117. }
  118. if(luaL_loadbuffer(l, s, sz, luaL_optstring(l, 2, s)) == 0){
  119. return 1;
  120. } else {
  121. lua_pushnil(l);
  122. lua_insert(l, -2);
  123. return 2;
  124. }
  125. }
  126. int LF_loadfile(lua_State *l)
  127. {
  128. size_t sz;
  129. const char *spath = luaL_checklstring(l, 1, &sz);
  130. lua_pushstring(l, "DOCUMENT_ROOT");
  131. lua_rawget(l, LUA_REGISTRYINDEX);
  132. char *document_root = lua_touserdata(l, -1);
  133. lua_pop(l, 1);
  134. if(document_root == NULL){
  135. lua_pushnil(l);
  136. lua_pushstring(l, "DOCUMENT_ROOT not defined.");
  137. return 2;
  138. }
  139. size_t dz = strlen(document_root);
  140. if(dz == 0){
  141. lua_pushnil(l);
  142. lua_pushstring(l, "DOCUMENT_ROOT empty.");
  143. return 2;
  144. }
  145. size_t hz = dz + sz;
  146. if((hz + 2) > 4096){
  147. lua_pushnil(l);
  148. lua_pushstring(l, "Path too large.");
  149. return 2;
  150. }
  151. char hpath[4096];
  152. memcpy(&hpath[0], document_root, dz);
  153. if(hpath[dz-1] != '/' && spath[0] != '/'){ hpath[dz] = '/'; }
  154. memcpy(&hpath[dz+1], spath, sz);
  155. hpath[hz+1] = 0;
  156. char rpath[4096];
  157. char *ptr = realpath(hpath, rpath);
  158. if(ptr == NULL || memcmp(document_root, rpath, dz) != 0){
  159. lua_pushnil(l);
  160. lua_pushstring(l, "Invalid path.");
  161. return 2;
  162. }
  163. switch(LF_fileload(l, &spath[0], &hpath[0])){
  164. case 0:
  165. return 1;
  166. break;
  167. case LF_ERRACCESS:
  168. lua_pushnil(l);
  169. lua_pushstring(l, "Access denied.");
  170. break;
  171. case LF_ERRMEMORY:
  172. lua_pushnil(l);
  173. lua_pushstring(l, "Not enough memory.");
  174. break;
  175. case LF_ERRNOTFOUND:
  176. lua_pushnil(l);
  177. lua_pushstring(l, "No such file or directory.");
  178. break;
  179. case LF_ERRSYNTAX:
  180. lua_pushnil(l);
  181. lua_insert(l, -2);
  182. break;
  183. case LF_ERRBYTECODE:
  184. lua_pushnil(l);
  185. lua_pushstring(l, "Compiled bytecode not supported.");
  186. break;
  187. case LF_ERRNOPATH:
  188. case LF_ERRNONAME:
  189. lua_pushnil(l);
  190. lua_pushstring(l, "Invalid path.");
  191. break;
  192. }
  193. return 2;
  194. }
  195. int LF_dofile(lua_State *l)
  196. {
  197. int r = LF_loadfile(l);
  198. if(r == 1 && lua_isfunction(l, -1)){
  199. lua_call(l, 0, LUA_MULTRET);
  200. return lua_gettop(l) - 1;
  201. } else {
  202. lua_error(l);
  203. }
  204. return 0;
  205. }