1 #include "first.h"
2
3 #include "sys-crypto-md.h"
4 #include "algo_hmac.h"
5 #include "base.h"
6 #include "base64.h"
7 #include "burl.h"
8 #include "log.h"
9 #include "buffer.h"
10 #include "chunk.h"
11 #include "ck.h"
12 #include "fdevent.h"
13 #include "h2.h"
14 #include "http_chunk.h"
15 #include "http_etag.h"
16 #include "http_header.h"
17 #include "rand.h"
18 #include "response.h" /* http_response_send_1xx() */
19
20 #include "plugin.h"
21
22 #include "mod_magnet_cache.h"
23 #include "sock_addr.h"
24 #include "stat_cache.h"
25
26 #include <dirent.h>
27 #include <stdlib.h>
28 #include <string.h>
29 /*#include <setjmp.h>*//*(not currently used)*/
30
31 #include <lua.h>
32 #include <lauxlib.h>
33
34 #define MAGNET_RESTART_REQUEST 99
35
36 /* plugin config for all request/connections */
37
38 /*static jmp_buf exceptionjmp;*//*(not currently used)*/
39
40 typedef struct {
41 script * const *url_raw;
42 script * const *physical_path;
43 script * const *response_start;
44 int stage;
45 } plugin_config;
46
47 typedef struct {
48 PLUGIN_DATA;
49 plugin_config defaults;
50 plugin_config conf;
51
52 script_cache cache;
53 } plugin_data;
54
55 static plugin_data *plugin_data_singleton;
56
INIT_FUNC(mod_magnet_init)57 INIT_FUNC(mod_magnet_init) {
58 plugin_data_singleton = (plugin_data *)ck_calloc(1, sizeof(plugin_data));
59 return plugin_data_singleton;
60 }
61
FREE_FUNC(mod_magnet_free)62 FREE_FUNC(mod_magnet_free) {
63 plugin_data * const p = p_d;
64 script_cache_free_data(&p->cache);
65 if (NULL == p->cvlist) return;
66 /* (init i to 0 if global context; to 1 to skip empty global context) */
67 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
68 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
69 for (; -1 != cpv->k_id; ++cpv) {
70 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue;
71 switch (cpv->k_id) {
72 case 0: /* magnet.attract-raw-url-to */
73 case 1: /* magnet.attract-physical-path-to */
74 case 2: /* magnet.attract-response-start-to */
75 free(cpv->v.v);
76 break;
77 default:
78 break;
79 }
80 }
81 }
82 }
83
mod_magnet_merge_config_cpv(plugin_config * const pconf,const config_plugin_value_t * const cpv)84 static void mod_magnet_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
85 if (cpv->vtype != T_CONFIG_LOCAL)
86 return;
87 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
88 case 0: /* magnet.attract-raw-url-to */
89 pconf->url_raw = cpv->v.v;
90 break;
91 case 1: /* magnet.attract-physical-path-to */
92 pconf->physical_path = cpv->v.v;
93 break;
94 case 2: /* magnet.attract-response-start-to */
95 pconf->response_start = cpv->v.v;
96 break;
97 default:/* should not happen */
98 return;
99 }
100 }
101
mod_magnet_merge_config(plugin_config * const pconf,const config_plugin_value_t * cpv)102 static void mod_magnet_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
103 do {
104 mod_magnet_merge_config_cpv(pconf, cpv);
105 } while ((++cpv)->k_id != -1);
106 }
107
mod_magnet_patch_config(request_st * const r,plugin_data * const p)108 static void mod_magnet_patch_config(request_st * const r, plugin_data * const p) {
109 p->conf = p->defaults; /* copy small struct instead of memcpy() */
110 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
111 for (int i = 1, used = p->nconfig; i < used; ++i) {
112 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
113 mod_magnet_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
114 }
115 }
116
SETDEFAULTS_FUNC(mod_magnet_set_defaults)117 SETDEFAULTS_FUNC(mod_magnet_set_defaults) {
118 static const config_plugin_keys_t cpk[] = {
119 { CONST_STR_LEN("magnet.attract-raw-url-to"),
120 T_CONFIG_ARRAY_VLIST,
121 T_CONFIG_SCOPE_CONNECTION }
122 ,{ CONST_STR_LEN("magnet.attract-physical-path-to"),
123 T_CONFIG_ARRAY_VLIST,
124 T_CONFIG_SCOPE_CONNECTION }
125 ,{ CONST_STR_LEN("magnet.attract-response-start-to"),
126 T_CONFIG_ARRAY_VLIST,
127 T_CONFIG_SCOPE_CONNECTION }
128 ,{ NULL, 0,
129 T_CONFIG_UNSET,
130 T_CONFIG_SCOPE_UNSET }
131 };
132
133 plugin_data * const p = p_d;
134 if (!config_plugin_values_init(srv, p, cpk, "mod_magnet"))
135 return HANDLER_ERROR;
136
137 /* process and validate config directives
138 * (init i to 0 if global context; to 1 to skip empty global context) */
139 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
140 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
141 for (; -1 != cpv->k_id; ++cpv) {
142 switch (cpv->k_id) {
143 case 0: /* magnet.attract-raw-url-to */
144 case 1: /* magnet.attract-physical-path-to */
145 case 2: /* magnet.attract-response-start-to */
146 if (0 == cpv->v.a->used) {
147 cpv->v.v = NULL;
148 cpv->vtype = T_CONFIG_LOCAL;
149 }
150 else {
151 script ** const a =
152 ck_malloc((cpv->v.a->used+1)*sizeof(script *));
153 for (uint32_t j = 0; j < cpv->v.a->used; ++j) {
154 data_string *ds = (data_string *)cpv->v.a->data[j];
155 if (buffer_is_blank(&ds->value)) {
156 log_error(srv->errh, __FILE__, __LINE__,
157 "unexpected (blank) value for %s; "
158 "expected list of \"scriptpath\"", cpk[cpv->k_id].k);
159 free(a);
160 return HANDLER_ERROR;
161 }
162 a[j] = script_cache_get_script(&p->cache, &ds->value);
163 }
164 a[cpv->v.a->used] = NULL;
165 cpv->v.v = a;
166 cpv->vtype = T_CONFIG_LOCAL;
167 }
168 break;
169 default:/* should not happen */
170 break;
171 }
172 }
173 }
174
175 /* initialize p->defaults from global config context */
176 if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
177 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
178 if (-1 != cpv->k_id)
179 mod_magnet_merge_config(&p->defaults, cpv);
180 }
181
182 return HANDLER_GO_ON;
183 }
184
185 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 504
186 #define lua_newuserdata0(L, sz) lua_newuserdata((L),(sz))
187 #else
188 #define lua_newuserdata0(L, sz) lua_newuserdatauv((L),(sz),0)
189 #endif
190
191 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
192 #define lua_getfield_and_type(L,idx,k) \
193 (lua_getfield((L),(idx),(k)), lua_type((L),-1))
194 #define lua_getglobal_and_type(L,name) \
195 (lua_getglobal((L),(name)), lua_type((L),-1))
196 #else
197 #define lua_getfield_and_type(L,idx,k) \
198 lua_getfield((L),(idx),(k))
199 #define lua_getglobal_and_type(L,name) \
200 lua_getglobal((L),(name))
201 #endif
202
203 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
204 static lua_Integer
lua_tointegerx(lua_State * const L,int idx,int * isnum)205 lua_tointegerx (lua_State * const L, int idx, int *isnum)
206 {
207 /*(caller should check for LUA_TNIL if using a default value is desired)*/
208 /*(note: return 0 for floating point not convertible to integer)*/
209 *isnum = lua_isnumber(L, idx);
210 return *isnum ? lua_tointeger(L, idx) : 0;
211 }
212 #endif
213
214 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
215 /* lua5.1 backward compat definition */
lua_pushglobaltable(lua_State * L)216 static void lua_pushglobaltable(lua_State *L) { /* (-0, +1, -) */
217 lua_pushvalue(L, LUA_GLOBALSINDEX);
218 }
219 #endif
220
magnet_setfenv_mainfn(lua_State * L,int funcIndex)221 static void magnet_setfenv_mainfn(lua_State *L, int funcIndex) { /* (-1, 0, -) */
222 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502
223 /* set "_ENV" upvalue, which should be the first upvalue of a "main" lua
224 * function if it uses any global names
225 */
226
227 const char* first_upvalue_name = lua_getupvalue(L, funcIndex, 1);
228 if (NULL == first_upvalue_name) return; /* doesn't have any upvalues */
229 lua_pop(L, 1); /* only need the name of the upvalue, not the value */
230
231 if (0 != strcmp(first_upvalue_name, "_ENV")) return;
232
233 if (NULL == lua_setupvalue(L, funcIndex, 1)) {
234 /* pop value if lua_setupvalue didn't set the (not existing) upvalue */
235 lua_pop(L, 1);
236 }
237 #else
238 lua_setfenv(L, funcIndex);
239 #endif
240 }
241
242 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
243 /* lua 5.1 deprecated luaL_getn() for lua_objlen() */
244 /* lua 5.2 renamed lua_objlen() to lua_rawlen() */
245 #define lua_rawlen lua_objlen
246 /* lua 5.2 deprecated luaL_register() for luaL_setfuncs()
247 * (this define is valid only when 0 == nup) */
248 #define luaL_setfuncs(L, l, nup) luaL_register((L), NULL, (l))
249 #endif
250
251 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
252 /* lua 5.2 already supports __pairs */
253
254 /* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details.
255 * Override the default pairs() function to allow us to use a __pairs metakey
256 */
magnet_pairs(lua_State * L)257 static int magnet_pairs(lua_State *L) {
258 luaL_checkany(L, 1); /* "self" */
259
260 if (luaL_getmetafield(L, 1, "__pairs")) {
261 /* call __pairs(self) */
262 lua_pushvalue(L, 1);
263 lua_call(L, 1, 3);
264 } else {
265 /* call <original-pairs-method>(self) */
266 lua_pushvalue(L, lua_upvalueindex(1));
267 lua_pushvalue(L, 1);
268 lua_call(L, 1, 3);
269 }
270 return 3;
271 }
272 #endif
273
274
275 /* XXX: mystery why dir walk (readdir) is not already part of lua io liolib.c */
276
277 #ifndef _D_EXACT_NAMLEN
278 #ifdef _DIRENT_HAVE_D_NAMLEN
279 #define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
280 #else
281 #define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name))
282 #endif
283 #endif
284
magnet_readdir_iter(lua_State * L)285 static int magnet_readdir_iter(lua_State *L) {
286 DIR ** const d = (DIR **)lua_touserdata(L, lua_upvalueindex(1));
287 if (NULL == *d) return 0;
288
289 /* readdir() and skip over "." and ".." */
290 struct dirent *de;
291 const char *n;
292 do {
293 de = readdir(*d);
294 } while (de && (n = de->d_name)[0] == '.'
295 && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')));
296
297 if (de) {
298 lua_pushlstring(L, de->d_name, _D_EXACT_NAMLEN(de));
299 return 1;
300 }
301 else { /* EOF */
302 closedir(*d);
303 *d = NULL;
304 return 0;
305 }
306 }
307
magnet_readdir_gc(lua_State * L)308 static int magnet_readdir_gc(lua_State *L) {
309 /*DIR ** const d = ((DIR **)luaL_checkudata(L, 1, "li.DIR"));*/
310 DIR ** const d = lua_touserdata(L, 1);
311 if (*d) closedir(*d);
312 return 0;
313 }
314
magnet_readdir_metatable(lua_State * const L)315 static void magnet_readdir_metatable(lua_State * const L) {
316 if (luaL_newmetatable(L, "li.DIR")) { /* (sp += 1) */
317 lua_pushcclosure(L, magnet_readdir_gc, 0); /* (sp += 1) */
318 lua_setfield(L, -2, "__gc"); /* (sp -= 1) */
319 lua_pushboolean(L, 0); /* (sp += 1) */
320 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
321 }
322 }
323
magnet_readdir(lua_State * L)324 static int magnet_readdir(lua_State *L) {
325 const char * const s = luaL_checkstring(L, 1);
326 DIR * const d = opendir(s);
327 if (d) {
328 *(DIR **)lua_newuserdata0(L, sizeof(DIR *)) = d;
329 magnet_readdir_metatable(L);
330 lua_setmetatable(L, -2);
331 lua_pushcclosure(L, magnet_readdir_iter, 1);
332 }
333 else
334 lua_pushnil(L);
335 return 1;
336 }
337
338
339 __attribute_cold__
magnet_newindex_readonly(lua_State * L)340 static int magnet_newindex_readonly(lua_State *L) {
341 lua_pushliteral(L, "lua table is read-only");
342 return lua_error(L);
343 }
344
magnet_push_buffer(lua_State * L,const buffer * b)345 static void magnet_push_buffer(lua_State *L, const buffer *b) {
346 if (b && !buffer_is_unset(b))
347 lua_pushlstring(L, BUF_PTR_LEN(b));
348 else
349 lua_pushnil(L);
350 }
351
352 #if 0
353 static int magnet_array_get_element(lua_State *L, const array *a) {
354 /* __index: param 1 is the (empty) table the value was not found in */
355 size_t klen;
356 const char * const k = luaL_checklstring(L, 2, &klen);
357 const data_string * const ds = (const data_string *)
358 array_get_element_klen(a, k, klen);
359 magnet_push_buffer(L, NULL != ds ? &ds->value : NULL);
360 return 1;
361 }
362 #endif
363
364 /* Define a function that will iterate over an array* (in upval 2) using current position (upval 1) */
magnet_array_next(lua_State * L)365 static int magnet_array_next(lua_State *L) {
366 lua_settop(L, 0);
367 const uint32_t pos = lua_tointeger(L, lua_upvalueindex(1));
368 const array * const a = lua_touserdata(L, lua_upvalueindex(2));
369 const data_unset * const du = pos < a->used ? a->data[pos] : NULL;
370 if (NULL == du) return 0;
371
372 lua_pushlstring(L, BUF_PTR_LEN(&du->key));
373 switch (du->type) {
374 case TYPE_STRING:
375 magnet_push_buffer(L, &((const data_string *)du)->value);
376 break;
377 case TYPE_INTEGER:
378 lua_pushinteger(L, ((const data_integer *)du)->value);
379 break;
380 default:
381 lua_pushnil(L);
382 break;
383 }
384
385 /* Update our positional upval to reflect our new current position */
386 lua_pushinteger(L, pos+1);
387 lua_replace(L, lua_upvalueindex(1));
388
389 /* Returning 2 items on the stack (key, value) */
390 return 2;
391 }
392
393 /* Create the closure necessary to iterate over the array *a with the above function */
394 __attribute_noinline__
magnet_array_pairs(lua_State * L,array * a)395 static int magnet_array_pairs(lua_State *L, array *a) {
396 lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */
397 lua_pushlightuserdata(L, a); /* Push our array *a into upval 2 */
398 lua_pushcclosure(L, magnet_array_next, 2); /* Push our new closure with 2 upvals */
399 return 1;
400 }
401
402 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
403 #define LUA_RIDX_LIGHTTPD_REQUEST "li.request"
404 #endif
405
406 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
407 __attribute_noinline__
408 #endif
magnet_get_request(lua_State * L)409 static request_st * magnet_get_request(lua_State *L) {
410 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
411 lua_getfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_REQUEST);
412 request_st * const r = lua_touserdata(L, -1);
413 lua_pop(L, 1);
414 return r;
415 #else
416 return *(request_st **)lua_getextraspace(L);
417 #endif
418 }
419
magnet_set_request(lua_State * L,request_st * const r)420 static void magnet_set_request(lua_State *L, request_st * const r) {
421 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
422 lua_pushlightuserdata(L, r);
423 lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_REQUEST);
424 #else
425 *(request_st **)lua_getextraspace(L) = r;
426 #endif
427 }
428
429 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
430 __attribute_noinline__
431 #endif
magnet_tmpbuf_acquire(lua_State * L)432 static buffer * magnet_tmpbuf_acquire(lua_State *L)
433 {
434 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
435 UNUSED(L);
436 return chunk_buffer_acquire();
437 #else
438 request_st * const r = magnet_get_request(L);
439 buffer * const tb = r->tmp_buf;
440 buffer_clear(tb);
441 return tb;
442 #endif
443 }
444
magnet_tmpbuf_release(buffer * b)445 static void magnet_tmpbuf_release(buffer *b)
446 {
447 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
448 chunk_buffer_release(b);
449 #else
450 UNUSED(b);
451 #endif
452 }
453
454 typedef struct {
455 const char *ptr;
456 size_t len;
457 } const_buffer;
458
459 __attribute_noinline__
magnet_checkconstbuffer(lua_State * L,int idx)460 static const_buffer magnet_checkconstbuffer(lua_State *L, int idx) {
461 const_buffer cb;
462 if (!lua_isnoneornil(L, idx))
463 cb.ptr = luaL_checklstring(L, idx, &cb.len);
464 else {
465 cb.ptr = NULL;
466 cb.len = 0;
467 }
468 return cb;
469 }
470
magnet_checkbuffer(lua_State * L,int idx,buffer * b)471 static const buffer* magnet_checkbuffer(lua_State *L, int idx, buffer *b) {
472 const_buffer cb = magnet_checkconstbuffer(L, idx);
473 /* assign result into (buffer *), and return (const buffer *)
474 * (note: caller must not free result) */
475 *(const char **)&b->ptr = cb.ptr ? cb.ptr : "";
476 b->used = cb.len+1;
477 b->size = 0;
478 return b;
479 }
480
481
magnet_return_upvalue2(lua_State * L)482 static int magnet_return_upvalue2(lua_State *L) {
483 /*(XXX: is there a better way to do this?)*/
484 lua_pushvalue(L, lua_upvalueindex(1));
485 lua_pushvalue(L, lua_upvalueindex(2));
486 return 2;
487 }
488
magnet_stat_field(lua_State * L)489 static int magnet_stat_field(lua_State *L) {
490 if (lua_gettop(L) != 2)
491 return 0; /*(should not happen; __index method in protected metatable)*/
492
493 stat_cache_entry * const sce = *(stat_cache_entry **)lua_touserdata(L, -2);
494 const_buffer k = magnet_checkconstbuffer(L, -1);
495 switch (k.len ? k.ptr[0] : 0) {
496 case 'c': { /* content-type */
497 if (0 != strcmp(k.ptr, "content-type")) break;
498 request_st * const r = magnet_get_request(L);
499 const buffer *content_type = stat_cache_content_type_get(sce, r);
500 if (content_type && !buffer_is_blank(content_type))
501 lua_pushlstring(L, BUF_PTR_LEN(content_type));
502 else
503 lua_pushnil(L);
504 return 1;
505 }
506 case 'e': { /* etag */
507 if (0 != strcmp(k.ptr, "etag")) break;
508 request_st * const r = magnet_get_request(L);
509 const buffer *etag = stat_cache_etag_get(sce, r->conf.etag_flags);
510 if (etag && !buffer_is_blank(etag))
511 lua_pushlstring(L, BUF_PTR_LEN(etag));
512 else
513 lua_pushnil(L);
514 return 1;
515 }
516 case 'h': { /* http-response-send-file */
517 if (0 != strcmp(k.ptr, "http-response-send-file")) break;
518 request_st * const r = magnet_get_request(L);
519 r->http_status = 0;
520 http_response_body_clear(r, 0);
521 http_response_send_file(r, &sce->name, sce);
522 lua_pushinteger(L, r->http_status);
523 return 1;
524 }
525 case 'i': /* is_* */
526 if (k.len < 4) break;
527 switch (k.ptr[3]) {
528 case 'b': /* is_block */
529 if (0 == strcmp(k.ptr, "is_block")) {
530 lua_pushboolean(L, S_ISBLK(sce->st.st_mode));
531 return 1;
532 }
533 break;
534 case 'c': /* is_char */
535 if (0 == strcmp(k.ptr, "is_char")) {
536 lua_pushboolean(L, S_ISCHR(sce->st.st_mode));
537 return 1;
538 }
539 break;
540 case 'd': /* is_dir */
541 if (0 == strcmp(k.ptr, "is_dir")) {
542 lua_pushboolean(L, S_ISDIR(sce->st.st_mode));
543 return 1;
544 }
545 break;
546 case 'f': /* is_file is_fifo */
547 if (0 == strcmp(k.ptr, "is_file")) {
548 lua_pushboolean(L, S_ISREG(sce->st.st_mode));
549 return 1;
550 }
551 if (0 == strcmp(k.ptr, "is_fifo")) {
552 lua_pushboolean(L, S_ISFIFO(sce->st.st_mode));
553 return 1;
554 }
555 break;
556 case 'l': /* is_link */
557 if (0 == strcmp(k.ptr, "is_link")) {
558 lua_pushboolean(L, S_ISLNK(sce->st.st_mode));
559 return 1;
560 }
561 break;
562 case 's': /* is_socket */
563 if (0 == strcmp(k.ptr, "is_socket")) {
564 lua_pushboolean(L, S_ISSOCK(sce->st.st_mode));
565 return 1;
566 }
567 break;
568 default:
569 break;
570 }
571 break;
572 case 's': /* st_* */
573 if (k.len < 4) break;
574 switch (k.ptr[3]) {
575 case 'a': /* st_atime */
576 if (0 == strcmp(k.ptr, "st_atime")) {
577 lua_pushinteger(L, TIME64_CAST(sce->st.st_atime));
578 return 1;
579 }
580 if (0 == strcmp(k.ptr, "st_atim")) {
581 lua_pushinteger(L, TIME64_CAST(sce->st.st_atime));
582 #ifdef st_atime /* high-precision timestamp if available */
583 #if defined(__APPLE__) && defined(__MACH__)
584 lua_pushinteger(L, sce->st.st_atimespec.tv_nsec);
585 #else
586 lua_pushinteger(L, sce->st.st_atim.tv_nsec);
587 #endif
588 #else
589 lua_pushinteger(L, 0);
590 #endif
591 lua_pushcclosure(L, magnet_return_upvalue2, 2);
592 return 1;
593 }
594 break;
595 case 'c': /* st_ctime */
596 if (0 == strcmp(k.ptr, "st_ctime")) {
597 lua_pushinteger(L, TIME64_CAST(sce->st.st_ctime));
598 return 1;
599 }
600 if (0 == strcmp(k.ptr, "st_ctim")) {
601 lua_pushinteger(L, TIME64_CAST(sce->st.st_ctime));
602 #ifdef st_ctime /* high-precision timestamp if available */
603 #if defined(__APPLE__) && defined(__MACH__)
604 lua_pushinteger(L, sce->st.st_ctimespec.tv_nsec);
605 #else
606 lua_pushinteger(L, sce->st.st_ctim.tv_nsec);
607 #endif
608 #else
609 lua_pushinteger(L, 0);
610 #endif
611 lua_pushcclosure(L, magnet_return_upvalue2, 2);
612 return 1;
613 }
614 break;
615 case 'i': /* st_ino */
616 if (0 == strcmp(k.ptr, "st_ino")) {
617 lua_pushinteger(L, sce->st.st_ino);
618 return 1;
619 }
620 break;
621 case 'm': /* st_mtime st_mode */
622 if (0 == strcmp(k.ptr, "st_mtime")) {
623 lua_pushinteger(L, TIME64_CAST(sce->st.st_mtime));
624 return 1;
625 }
626 if (0 == strcmp(k.ptr, "st_mtim")) {
627 lua_pushinteger(L, TIME64_CAST(sce->st.st_mtime));
628 #ifdef st_mtime /* high-precision timestamp if available */
629 #if defined(__APPLE__) && defined(__MACH__)
630 lua_pushinteger(L, sce->st.st_mtimespec.tv_nsec);
631 #else
632 lua_pushinteger(L, sce->st.st_mtim.tv_nsec);
633 #endif
634 #else
635 lua_pushinteger(L, 0);
636 #endif
637 lua_pushcclosure(L, magnet_return_upvalue2, 2);
638 return 1;
639 }
640 if (0 == strcmp(k.ptr, "st_mode")) {
641 lua_pushinteger(L, sce->st.st_mode);
642 return 1;
643 }
644 break;
645 case 'g': /* st_gid */
646 if (0 == strcmp(k.ptr, "st_gid")) {
647 lua_pushinteger(L, sce->st.st_gid);
648 return 1;
649 }
650 break;
651 case 's': /* st_size */
652 if (0 == strcmp(k.ptr, "st_size")) {
653 lua_pushinteger(L, sce->st.st_size);
654 return 1;
655 }
656 break;
657 case 'u': /* st_uid */
658 if (0 == strcmp(k.ptr, "st_uid")) {
659 lua_pushinteger(L, sce->st.st_uid);
660 return 1;
661 }
662 break;
663 default:
664 break;
665 }
666 break;
667 default:
668 break;
669 }
670
671 lua_pushliteral(L, "stat[\"field\"] invalid: ");
672 lua_pushvalue(L, -2); /* field */
673 lua_concat(L, 2);
674 lua_error(L);
675 return 0;
676 }
677
678
679 __attribute_cold__
magnet_stat_pairs_noimpl_iter(lua_State * L)680 static int magnet_stat_pairs_noimpl_iter(lua_State *L) {
681 request_st * const r = magnet_get_request(L);
682 log_error(r->conf.errh, __FILE__, __LINE__,
683 "(lua) pairs() not implemented on lighty.stat object; "
684 "returning empty iter");
685 return 0;
686 }
687
688
689 __attribute_cold__
magnet_stat_pairs_noimpl(lua_State * L)690 static int magnet_stat_pairs_noimpl(lua_State *L) {
691 lua_pushcclosure(L, magnet_stat_pairs_noimpl_iter, 0);
692 return 1;
693 }
694
695
magnet_stat_metatable(lua_State * L)696 static void magnet_stat_metatable(lua_State *L) {
697 if (luaL_newmetatable(L, "li.stat")) { /* (sp += 1) */
698 lua_pushcfunction(L, magnet_stat_field); /* (sp += 1) */
699 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
700 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */
701 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
702 lua_pushcfunction(L, magnet_stat_pairs_noimpl); /* (sp += 1) */
703 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */
704 lua_pushboolean(L, 0); /* (sp += 1) */
705 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
706 }
707 }
708
709
magnet_stat(lua_State * L)710 static int magnet_stat(lua_State *L) {
711 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/
712 const buffer * const sb = magnet_checkbuffer(L, 1, &stor);
713 stat_cache_entry * const sce = (!buffer_is_blank(sb))
714 ? stat_cache_get_entry(sb)
715 : NULL;
716 if (NULL == sce) {
717 lua_pushnil(L);
718 return 1;
719 }
720
721 /* note: caching sce valid only for procedural script which does not yield;
722 * (sce might not be valid if script yields and is later resumed)
723 * (script must not cache sce in persistent global state for later use)
724 * (If we did want sce to be persistent, then could increment sce refcnt,
725 * and set up __gc metatable method to decrement sce refcnt) */
726 stat_cache_entry ** const udata =(struct stat_cache_entry**)/* (sp += 1) */
727 lua_newuserdata0(L, sizeof(stat_cache_entry *));
728 *udata = sce;
729
730 magnet_stat_metatable(L); /* (sp += 1) */
731 lua_setmetatable(L, -2); /* (sp -= 1) */
732 return 1;
733 }
734
735
magnet_time(lua_State * L)736 static int magnet_time(lua_State *L) {
737 lua_pushinteger(L, (lua_Integer)log_epoch_secs);
738 return 1;
739 }
740
741
magnet_hrtime(lua_State * L)742 static int magnet_hrtime(lua_State *L) {
743 unix_timespec64_t ts;
744 if (0 != log_clock_gettime_realtime(&ts))
745 return 0;
746 lua_pushinteger(L, (lua_Integer)ts.tv_sec);
747 lua_pushinteger(L, (lua_Integer)ts.tv_nsec);
748 return 2;
749 }
750
751
magnet_rand(lua_State * L)752 static int magnet_rand(lua_State *L) {
753 lua_pushinteger(L, (lua_Integer)li_rand_pseudo());
754 return 1;
755 }
756
757
magnet_md_once(lua_State * L)758 static int magnet_md_once(lua_State *L) {
759 if (lua_gettop(L) != 2) {
760 lua_pushliteral(L,
761 "lighty.c.md(algo, data): incorrect number of arguments");
762 return lua_error(L);
763 }
764 const_buffer algo = magnet_checkconstbuffer(L, -2);
765 const_buffer msg = magnet_checkconstbuffer(L, -1);
766 uint8_t digest[MD_DIGEST_LENGTH_MAX];
767 uint32_t dlen = 0;
768 switch (algo.len) {
769 #ifdef USE_LIB_CRYPTO
770 case 6:
771 #ifdef USE_LIB_CRYPTO_SHA512
772 if (0 == memcmp(algo.ptr, "sha512", 6)) {
773 SHA512_once(digest, msg.ptr, msg.len);
774 dlen = SHA512_DIGEST_LENGTH;
775 break;
776 }
777 #endif
778 #ifdef USE_LIB_CRYPTO_SHA256
779 if (0 == memcmp(algo.ptr, "sha256", 6)) {
780 SHA256_once(digest, msg.ptr, msg.len);
781 dlen = SHA256_DIGEST_LENGTH;
782 break;
783 }
784 #endif
785 break;
786 case 4:
787 #ifdef USE_LIB_CRYPTO_SHA1
788 if (0 == memcmp(algo.ptr, "sha1", 4)) {
789 SHA1_once(digest, msg.ptr, msg.len);
790 dlen = SHA1_DIGEST_LENGTH;
791 break;
792 }
793 #endif
794 break;
795 #endif
796 case 3:
797 if (0 == memcmp(algo.ptr, "md5", 3)) {
798 MD5_once(digest, msg.ptr, msg.len);
799 dlen = MD5_DIGEST_LENGTH;
800 break;
801 }
802 break;
803 default:
804 break;
805 }
806
807 if (dlen) {
808 char dighex[MD_DIGEST_LENGTH_MAX*2+1];
809 li_tohex_uc(dighex, sizeof(dighex), (char *)digest, dlen);
810 lua_pushlstring(L, dighex, dlen*2);
811 }
812 else
813 lua_pushnil(L);
814
815 return 1;
816 }
817
magnet_hmac_once(lua_State * L)818 static int magnet_hmac_once(lua_State *L) {
819 if (lua_gettop(L) != 3) {
820 lua_pushliteral(L,
821 "lighty.c.hmac(algo, secret, data): incorrect number of arguments");
822 return lua_error(L);
823 }
824 const_buffer algo = magnet_checkconstbuffer(L, -3);
825 const_buffer secret = magnet_checkconstbuffer(L, -2);
826 const_buffer msg = magnet_checkconstbuffer(L, -1);
827 const uint8_t * const msgptr = (uint8_t *)msg.ptr;
828 uint8_t digest[MD_DIGEST_LENGTH_MAX];
829 uint32_t dlen = 0;
830 int rc = 0;
831 switch (algo.len) {
832 #ifdef USE_LIB_CRYPTO
833 case 6:
834 #ifdef USE_LIB_CRYPTO_SHA512
835 if (0 == memcmp(algo.ptr, "sha512", 6)) {
836 rc = li_hmac_sha512(digest,secret.ptr,secret.len,msgptr,msg.len);
837 dlen = SHA512_DIGEST_LENGTH;
838 break;
839 }
840 #endif
841 #ifdef USE_LIB_CRYPTO_SHA256
842 if (0 == memcmp(algo.ptr, "sha256", 6)) {
843 rc = li_hmac_sha256(digest,secret.ptr,secret.len,msgptr,msg.len);
844 dlen = SHA256_DIGEST_LENGTH;
845 break;
846 }
847 #endif
848 break;
849 case 4:
850 #ifdef USE_LIB_CRYPTO_SHA1
851 if (0 == memcmp(algo.ptr, "sha1", 4)) {
852 rc = li_hmac_sha1(digest,secret.ptr,secret.len,msgptr,msg.len);
853 dlen = SHA1_DIGEST_LENGTH;
854 break;
855 }
856 #endif
857 break;
858 #endif
859 case 3:
860 if (0 == memcmp(algo.ptr, "md5", 3)) {
861 rc = li_hmac_md5(digest,secret.ptr,secret.len,msgptr,msg.len);
862 dlen = MD5_DIGEST_LENGTH;
863 break;
864 }
865 break;
866 default:
867 break;
868 }
869
870 if (rc) {
871 char dighex[MD_DIGEST_LENGTH_MAX*2+1];
872 li_tohex_uc(dighex, sizeof(dighex), (char *)digest, dlen);
873 lua_pushlstring(L, dighex, dlen*2);
874 }
875 else
876 lua_pushnil(L);
877
878 return 1;
879 }
880
magnet_digest_eq(lua_State * L)881 static int magnet_digest_eq(lua_State *L) {
882 if (lua_gettop(L) != 2) {
883 lua_pushliteral(L,
884 "lighty.c.digest_eq(d1, d2): incorrect number of arguments");
885 return lua_error(L);
886 }
887 const_buffer d1 = magnet_checkconstbuffer(L, -2);
888 const_buffer d2 = magnet_checkconstbuffer(L, -1);
889 /* convert hex to binary: validate hex and eliminate hex case comparison */
890 uint8_t b1[MD_DIGEST_LENGTH_MAX];
891 uint8_t b2[MD_DIGEST_LENGTH_MAX];
892 int rc = (d1.len == d2.len)
893 && 0 == li_hex2bin(b1, sizeof(b1), d1.ptr, d1.len)
894 && 0 == li_hex2bin(b2, sizeof(b2), d2.ptr, d2.len)
895 && ck_memeq_const_time_fixed_len(b1, b2, d2.len >> 1);
896 lua_pushboolean(L, rc);
897 return 1;
898 }
899
magnet_secret_eq(lua_State * L)900 static int magnet_secret_eq(lua_State *L) {
901 if (lua_gettop(L) != 2) {
902 lua_pushliteral(L,
903 "lighty.c.secret_eq(d1, d2): incorrect number of arguments");
904 return lua_error(L);
905 }
906 const_buffer d1 = magnet_checkconstbuffer(L, -2);
907 const_buffer d2 = magnet_checkconstbuffer(L, -1);
908 lua_pushboolean(L, ck_memeq_const_time(d1.ptr, d1.len, d2.ptr, d2.len));
909 return 1;
910 }
911
magnet_b64dec(lua_State * L,base64_charset dict)912 static int magnet_b64dec(lua_State *L, base64_charset dict) {
913 if (lua_isnoneornil(L, -1)) {
914 lua_pushlstring(L, "", 0);
915 return 1;
916 }
917 const_buffer s = magnet_checkconstbuffer(L, -1);
918 if (0 == s.len) {
919 lua_pushvalue(L, -1);
920 return 1;
921 }
922 buffer * const b = magnet_tmpbuf_acquire(L);
923 if (buffer_append_base64_decode(b, s.ptr, s.len, dict))
924 lua_pushlstring(L, BUF_PTR_LEN(b));
925 else
926 lua_pushnil(L);
927 magnet_tmpbuf_release(b);
928 return 1;
929 }
930
magnet_b64enc(lua_State * L,base64_charset dict)931 static int magnet_b64enc(lua_State *L, base64_charset dict) {
932 if (lua_isnoneornil(L, -1)) {
933 lua_pushlstring(L, "", 0);
934 return 1;
935 }
936 const_buffer s = magnet_checkconstbuffer(L, -1);
937 if (0 == s.len) {
938 lua_pushvalue(L, -1);
939 return 1;
940 }
941 buffer * const b = magnet_tmpbuf_acquire(L);
942 buffer_append_base64_encode_no_padding(b, (uint8_t *)s.ptr, s.len, dict);
943 lua_pushlstring(L, BUF_PTR_LEN(b));
944 magnet_tmpbuf_release(b);
945 return 1;
946 }
947
magnet_b64urldec(lua_State * L)948 static int magnet_b64urldec(lua_State *L) {
949 return magnet_b64dec(L, BASE64_URL);
950 }
951
magnet_b64urlenc(lua_State * L)952 static int magnet_b64urlenc(lua_State *L) {
953 return magnet_b64enc(L, BASE64_URL);
954 }
955
magnet_b64stddec(lua_State * L)956 static int magnet_b64stddec(lua_State *L) {
957 return magnet_b64dec(L, BASE64_STANDARD);
958 }
959
magnet_b64stdenc(lua_State * L)960 static int magnet_b64stdenc(lua_State *L) {
961 return magnet_b64enc(L, BASE64_STANDARD);
962 }
963
magnet_hexdec(lua_State * L)964 static int magnet_hexdec(lua_State *L) {
965 if (lua_isnoneornil(L, -1)) {
966 lua_pushlstring(L, "", 0);
967 return 1;
968 }
969 const_buffer s = magnet_checkconstbuffer(L, -1);
970 if (0 == s.len) {
971 lua_pushvalue(L, -1);
972 return 1;
973 }
974 buffer * const b = magnet_tmpbuf_acquire(L);
975 uint8_t * const p = (uint8_t *)buffer_extend(b, s.len >> 1);
976 int rc = li_hex2bin(p, s.len >> 1, s.ptr, s.len);
977 if (0 == rc)
978 lua_pushlstring(L, BUF_PTR_LEN(b));
979 magnet_tmpbuf_release(b);
980 return rc+1; /* 1 on success (pushed string); 0 on failure (no value) */
981 }
982
magnet_hexenc(lua_State * L)983 static int magnet_hexenc(lua_State *L) {
984 if (lua_isnoneornil(L, -1)) {
985 lua_pushlstring(L, "", 0);
986 return 1;
987 }
988 const_buffer s = magnet_checkconstbuffer(L, -1);
989 if (0 == s.len) {
990 lua_pushvalue(L, -1);
991 return 1;
992 }
993 buffer * const b = magnet_tmpbuf_acquire(L);
994 buffer_append_string_encoded_hex_uc(b, s.ptr, s.len);
995 lua_pushlstring(L, BUF_PTR_LEN(b));
996 magnet_tmpbuf_release(b);
997 return 1; /* uppercase hex string; use lua s = s:lower() to lowercase */
998 }
999
magnet_quoteddec(lua_State * L)1000 static int magnet_quoteddec(lua_State *L) {
1001 if (lua_isnoneornil(L, -1)) {
1002 lua_pushlstring(L, "", 0);
1003 return 1;
1004 }
1005 const_buffer s = magnet_checkconstbuffer(L, -1);
1006 if (0 == s.len || s.ptr[0] != '"') {
1007 lua_pushvalue(L, -1);
1008 return 1;
1009 }
1010 buffer * const b = magnet_tmpbuf_acquire(L);
1011 char *p = buffer_string_prepare_append(b, s.len);/*(s.len-1 is sufficient)*/
1012 size_t i = 1;
1013 for (; i < s.len && s.ptr[i] != '"'; ++i) {
1014 if (s.ptr[i] == '\\') {
1015 if (i+2 < s.len)
1016 ++i;
1017 else
1018 break;
1019 }
1020 *p++ = s.ptr[i];
1021 }
1022 int rc = (i == s.len-1 && s.ptr[i] == '"');
1023 if (rc)
1024 lua_pushlstring(L, b->ptr, (size_t)(p - b->ptr));
1025 magnet_tmpbuf_release(b);
1026 return rc; /* 1 on success (pushed string); 0 on failure (no value) */
1027 }
1028
magnet_quotedenc(lua_State * L)1029 static int magnet_quotedenc(lua_State *L) {
1030 if (lua_isnoneornil(L, -1)) {
1031 lua_pushlstring(L, "", 0);
1032 return 1;
1033 }
1034 const_buffer s = magnet_checkconstbuffer(L, -1);
1035 if (0 == s.len) {
1036 lua_pushvalue(L, -1);
1037 return 1;
1038 }
1039 buffer * const b = magnet_tmpbuf_acquire(L);
1040 char *p = buffer_string_prepare_append(b, 2+(s.len << 1));
1041 *p++ = '"';
1042 for (size_t i = 0; i < s.len; ++i) {
1043 /*(note: not strictly checking for TEXT)*/
1044 /*(TEXT: any OCTET except CTLs but including LWS)*/
1045 if (s.ptr[i] == '"' || s.ptr[i] == '\\')
1046 *p++ = '\\';
1047 *p++ = s.ptr[i];
1048 }
1049 *p++ = '"';
1050 lua_pushlstring(L, b->ptr, (size_t)(p - b->ptr));
1051 magnet_tmpbuf_release(b);
1052 return 1;
1053 }
1054
1055 /*(future: might move to buffer.c:buffer_append_bs_unescaped())*/
1056 static void
magnet_buffer_append_bsdec(buffer * const restrict b,const char * restrict s,const size_t len)1057 magnet_buffer_append_bsdec (buffer * const restrict b,
1058 const char * restrict s, const size_t len)
1059 {
1060 /* decode backslash escapes */
1061 /*(caller must check result for decoded '\0', if necessary)*/
1062 char *d = buffer_string_prepare_append(b, len); /*(upper-bound len)*/
1063 for (const char * const end = s+len; s < end; ++s) {
1064 const char * const ptr = s;
1065 while (__builtin_expect( (*s != '\\'), 1) && ++s < end) ;
1066 if (s - ptr) {
1067 memcpy(d, ptr, (size_t)(s - ptr));
1068 d += (size_t)(s - ptr);
1069 }
1070
1071 if (s == end)
1072 break;
1073
1074 int c;
1075 switch ((c = ++s != end ? *s : '\\')) { /*(preserve stray '\\' at end)*/
1076 case '"': case '\\':
1077 default:
1078 break;
1079 case 'x':
1080 if (s+3 <= end) {
1081 unsigned char hi = hex2int(((unsigned char *)s)[1]);
1082 unsigned char lo = hex2int(((unsigned char *)s)[2]);
1083 if (0xFF != hi && 0xFF != lo) {
1084 c = (hi << 4) | lo;
1085 s += 2;
1086 }
1087 }
1088 break;
1089 case 'a':case 'b':case 't':case 'n':case 'v':case 'f':case 'r':
1090 c = "\a\bcde\fghijklm\nopq\rstu\vwxyz"[c-'a'];
1091 break;
1092 case 'u':
1093 if (s+5 <= end) {
1094 unsigned char hi = hex2int(((unsigned char *)s)[3]);
1095 unsigned char lo = hex2int(((unsigned char *)s)[4]);
1096 if (0xFF == hi || 0xFF == lo)
1097 break;
1098 c = (hi << 4) | lo;
1099 if (__builtin_expect( (s[1] != '0'), 0)
1100 || __builtin_expect( (s[2] != '0'), 0)) {
1101 unsigned char hhi = hex2int(((unsigned char *)s)[1]);
1102 unsigned char hlo = hex2int(((unsigned char *)s)[2]);
1103 if (0xFF == hhi || 0xFF == hlo)
1104 break;
1105 c |= (int)((hhi << 12) | (hlo << 8));
1106 if ((unsigned int)c - 0xd800u < 0x800)
1107 break; /* 0xD800 - 0xDFFF ill-formed UTF-8 */
1108 }
1109 /* adapted from
1110 * https://stackoverflow.com/questions/4607413/is-there-a-c-library-to-convert-unicode-code-points-to-utf-8 */
1111 if (__builtin_expect( (c > 0x7F), 0)) {
1112 if (c < 0x800)
1113 *d++ = 0xC0 | (c >> 6);
1114 else {
1115 *d++ = 0xE0 | (c >> 12);
1116 *d++ = 0x80 | ((c >> 6) & 0x3F);
1117 }
1118 c = 0x80 | (c & 0x3F);
1119 }
1120 s += 4;
1121 }
1122 break;
1123 case '0': case '1': case '2': case '3':
1124 if (s+3 <= end
1125 /*&& ((unsigned char *)s)[0] - '0' < 4*//* 0-3 */
1126 && ((unsigned char *)s)[1] - '0' < 8 /* 0-7 */
1127 && ((unsigned char *)s)[2] - '0' < 8) { /* 0-7 */
1128 c = ((s[0]-'0') << 6) | ((s[1]-'0') << 3) | (s[2]-'0');
1129 s += 2;
1130 }
1131 else if (*s == '0')
1132 c = '\0'; /*(special-case "\\0" not part of octal "\\ooo")*/
1133 break;
1134 }
1135 *d++ = c;
1136 }
1137 buffer_truncate(b, d - b->ptr);
1138 }
1139
magnet_bsdec(lua_State * L)1140 static int magnet_bsdec(lua_State *L) {
1141 if (lua_isnoneornil(L, -1)) {
1142 lua_pushlstring(L, "", 0);
1143 return 1;
1144 }
1145 const_buffer s = magnet_checkconstbuffer(L, -1);
1146 if (0 == s.len) {
1147 lua_pushvalue(L, -1);
1148 return 1;
1149 }
1150 const char *ptr = s.ptr;
1151 size_t len = s.len;
1152 if (ptr[0] == '"' && ptr[len-1] == '"') {
1153 /*(ignore double-quotes ('"') surrounding string for convenience)*/
1154 ++ptr;
1155 len -= 2;
1156 }
1157 buffer * const b = magnet_tmpbuf_acquire(L);
1158 magnet_buffer_append_bsdec(b, ptr, len);
1159 lua_pushlstring(L, BUF_PTR_LEN(b));
1160 magnet_tmpbuf_release(b);
1161 return 1;
1162 }
1163
magnet_bsenc(lua_State * L,const int esc_json)1164 static int magnet_bsenc(lua_State *L, const int esc_json) {
1165 if (lua_isnoneornil(L, -1)) {
1166 lua_pushlstring(L, "", 0);
1167 return 1;
1168 }
1169 const_buffer s = magnet_checkconstbuffer(L, -1);
1170 if (0 == s.len) {
1171 lua_pushvalue(L, -1);
1172 return 1;
1173 }
1174 buffer * const b = magnet_tmpbuf_acquire(L);
1175 if (esc_json)
1176 buffer_append_bs_escaped(b, s.ptr, s.len);
1177 else
1178 buffer_append_bs_escaped_json(b, s.ptr, s.len);
1179 lua_pushlstring(L, BUF_PTR_LEN(b));
1180 magnet_tmpbuf_release(b);
1181 return 1;
1182 }
1183
magnet_bsenc_default(lua_State * L)1184 static int magnet_bsenc_default(lua_State *L) {
1185 return magnet_bsenc(L, 0);
1186 }
1187
magnet_bsenc_json(lua_State * L)1188 static int magnet_bsenc_json(lua_State *L) {
1189 return magnet_bsenc(L, 1);
1190 }
1191
magnet_xmlenc(lua_State * L)1192 static int magnet_xmlenc(lua_State *L) {
1193 if (lua_isnoneornil(L, -1)) {
1194 lua_pushlstring(L, "", 0);
1195 return 1;
1196 }
1197 const_buffer s = magnet_checkconstbuffer(L, -1);
1198 if (0 == s.len) {
1199 lua_pushvalue(L, -1);
1200 return 1;
1201 }
1202 buffer * const b = magnet_tmpbuf_acquire(L);
1203 #if 1
1204 buffer_append_string_encoded(b, s.ptr, s.len, ENCODING_MINIMAL_XML);
1205 #else
1206 const char *e;
1207 size_t i, n, elen;
1208 for (i = 0, n = 0; i < s.len; ++i) {
1209 switch (s.ptr[i]) {
1210 default: continue;
1211 case '<': e = "<"; elen = sizeof("<")-1; break;
1212 case '>': e = ">"; elen = sizeof(">")-1; break;
1213 case '&': e = "&"; elen = sizeof("&")-1; break;
1214 case '\'': e = "'"; elen = sizeof("'")-1; break;
1215 case '"': e = """; elen = sizeof(""")-1; break;
1216 /*(XXX: would be good idea to add CTRLs, DEL, '`' */
1217 }
1218 buffer_append_str2(b, s.ptr+n, i-n, e, elen);
1219 n = i+1;
1220 }
1221 if (i-n)
1222 buffer_append_string_len(b, s.ptr+n, i-n);
1223 #endif
1224 lua_pushlstring(L, BUF_PTR_LEN(b));
1225 magnet_tmpbuf_release(b);
1226 return 1;
1227 }
1228
magnet_urldec(lua_State * L)1229 static int magnet_urldec(lua_State *L) {
1230 /* url-decode and replace non-printable chars with '_'
1231 * This function should not be used on query-string unless it is used on
1232 * portions of query-string after splitting on '&', replacing '+' w/ ' ' */
1233 if (lua_isnoneornil(L, -1)) {
1234 lua_pushlstring(L, "", 0);
1235 return 1;
1236 }
1237 const_buffer s = magnet_checkconstbuffer(L, -1);
1238 if (0 == s.len) {
1239 lua_pushvalue(L, -1);
1240 return 1;
1241 }
1242 buffer * const b = magnet_tmpbuf_acquire(L);
1243 buffer_copy_string_len(b, s.ptr, s.len);
1244 buffer_urldecode_path(b);
1245 lua_pushlstring(L, BUF_PTR_LEN(b));
1246 magnet_tmpbuf_release(b);
1247 return 1;
1248 }
1249
magnet_urlenc(lua_State * L)1250 static int magnet_urlenc(lua_State *L) {
1251 /* url-encode path
1252 * ('?' is encoded, if present)
1253 * caller must split string if '?' is part of query-string)
1254 * ('/' is not encoded; caller must encode if not path separator) */
1255 if (lua_isnoneornil(L, -1)) {
1256 lua_pushlstring(L, "", 0);
1257 return 1;
1258 }
1259 const_buffer s = magnet_checkconstbuffer(L, -1);
1260 if (0 == s.len) {
1261 lua_pushvalue(L, -1);
1262 return 1;
1263 }
1264 buffer * const b = magnet_tmpbuf_acquire(L);
1265 buffer_append_string_encoded(b, s.ptr, s.len, ENCODING_REL_URI);
1266 lua_pushlstring(L, BUF_PTR_LEN(b));
1267 magnet_tmpbuf_release(b);
1268 return 1;
1269 }
1270
magnet_urldec_query_part(buffer * const b,const char * s,const size_t slen)1271 static void magnet_urldec_query_part(buffer * const b, const char *s, const size_t slen) {
1272 buffer_clear(b);
1273 char *p = buffer_extend(b, slen);
1274 for (size_t i = 0; i < slen; ++i)
1275 p[i] = (s[i] != '+') ? s[i] : ' ';
1276 buffer_urldecode_path(b);
1277 }
1278
magnet_urldec_query(lua_State * L)1279 static int magnet_urldec_query(lua_State *L) {
1280 /* split on '&' and '=', url-decode and replace non-printable chars w/ '_',
1281 * and store components in table
1282 * (string input should be query-string without leading '?')
1283 * (note: duplicated keys replace earlier values, but this interface returns
1284 * a table useful for lookups, so this limitation is often acceptable) */
1285 lua_createtable(L, 0, 0);
1286 if (lua_isnoneornil(L, 1)) {
1287 return 1;
1288 }
1289 const_buffer s = magnet_checkconstbuffer(L, 1);
1290 if (0 == s.len) {
1291 return 1;
1292 }
1293 buffer * const b = magnet_tmpbuf_acquire(L);
1294 for (const char *qs = s.ptr, *eq, *amp; *qs; qs = amp+1) {
1295 for (amp = qs, eq = NULL; *amp && *amp != '&'; ++amp) {
1296 if (*amp == '=' && !eq) eq = amp;
1297 }
1298 if (amp != qs) {
1299 if (eq) {
1300 magnet_urldec_query_part(b, qs, (size_t)(eq - qs));
1301 lua_pushlstring(L, BUF_PTR_LEN(b));
1302 magnet_urldec_query_part(b, eq+1, (size_t)(amp - (eq+1)));
1303 lua_pushlstring(L, BUF_PTR_LEN(b));
1304 }
1305 else {
1306 magnet_urldec_query_part(b, qs, (size_t)(amp - qs));
1307 lua_pushlstring(L, BUF_PTR_LEN(b));
1308 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/
1309 }
1310 lua_rawset(L, -3);
1311 }
1312 if (*amp == '\0') break;
1313 }
1314 magnet_tmpbuf_release(b);
1315 return 1;
1316 }
1317
magnet_urlenc_query_part(buffer * const b,const char * const s,const size_t slen,const int iskey)1318 static void magnet_urlenc_query_part(buffer * const b, const char * const s, const size_t slen, const int iskey) {
1319 /* encode query part (each part is typically much smaller than chunk buffer)
1320 * all required chars plus '&' ';' '+' '\'' (and encode '=' if part of key)
1321 * (burl_append(b,str,len,BURL_ENCODE_ALL) works, but over-encodes) */
1322 #if 0
1323 /* alternative: (over-encodes some, but less than burl_append()) */
1324 UNUSED(iskey);
1325 buffer_append_string_encoded(b, s, slen, ENCODING_REL_URI);
1326 #else
1327 static const char hex_chars_uc[] = "0123456789ABCDEF";
1328 char * const p = buffer_string_prepare_append(b, slen*3);
1329 int j = 0;
1330 for (size_t i = 0; i < slen; ++i, ++j) {
1331 int c = s[i];
1332 if (!light_isalnum(c)) switch (c) {
1333 case ' ':
1334 c = '+';
1335 break;
1336 /*case '\'':*//*(ok in url query-part, but might be misused in HTML)*/
1337 case '!': case '$': case '(': case ')': case '*': case ',': case '-':
1338 case '.': case '/': case ':': case '?': case '@': case '_': case '~':
1339 break;
1340 case '=':
1341 if (!iskey) break;
1342 __attribute_fallthrough__
1343 default:
1344 p[j] = '%';
1345 p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF];
1346 p[++j] = hex_chars_uc[s[i] & 0xF];
1347 continue;
1348 }
1349 p[j] = c;
1350 }
1351 buffer_commit(b, j);
1352 #endif
1353 }
1354
magnet_urlenc_query(lua_State * L)1355 static int magnet_urlenc_query(lua_State *L) {
1356 /* encode pairs in lua table into query string
1357 * (caller should add leading '?' or '&' when appending to full URL)
1358 * (caller should skip empty table if appending to existing query-string) */
1359 if (!lua_istable(L, 1)) {
1360 lua_pushlstring(L, "", 0);
1361 return 1;
1362 }
1363 buffer * const b = magnet_tmpbuf_acquire(L);
1364 const_buffer s;
1365 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
1366 if (lua_isstring(L, -2)) {
1367 if (!buffer_is_blank(b))
1368 buffer_append_char(b, '&');
1369 s = magnet_checkconstbuffer(L, -2);
1370 magnet_urlenc_query_part(b, s.ptr, s.len, 1);
1371 if (!lua_isnil(L, -1)) {
1372 s = magnet_checkconstbuffer(L, -1);
1373 buffer_append_char(b, '=');
1374 magnet_urlenc_query_part(b, s.ptr, s.len, 0);
1375 }
1376 }
1377 }
1378 lua_pushlstring(L, BUF_PTR_LEN(b));
1379 magnet_tmpbuf_release(b);
1380 return 1;
1381 }
1382
magnet_urlenc_normalize(lua_State * L)1383 static int magnet_urlenc_normalize(lua_State *L) {
1384 /* normalize url-encoding
1385 * url-encode (and url-decode safe chars) to normalize url-path
1386 * ('?' is treated as start of query-string and is not encoded;
1387 * caller must encode '?' if intended to be part of url-path)
1388 * ('/' is not encoded; caller must encode if not path separator)
1389 * (burl_append() is not exposed here; caller might want to build
1390 * url with lighty.c.urlenc() and lighty.c.urlenc_query(),
1391 * then call lighty.c.urlenc_normalize()) */
1392 if (lua_isnoneornil(L, -1)) {
1393 lua_pushlstring(L, "", 0);
1394 return 1;
1395 }
1396 const_buffer s = magnet_checkconstbuffer(L, -1);
1397 if (0 == s.len) {
1398 lua_pushvalue(L, -1);
1399 return 1;
1400 }
1401 buffer * const b = magnet_tmpbuf_acquire(L);
1402 buffer * const t = chunk_buffer_acquire();
1403 #if 0 /*(?maybe have different interface to use config policy?)*/
1404 request_st * const r = magnet_get_request(L);
1405 const int flags = r->conf.http_parseopts;
1406 #else
1407 const int flags = HTTP_PARSEOPT_URL_NORMALIZE
1408 | HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED
1409 | HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED
1410 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
1411 | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
1412 | HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS;
1413 #endif
1414 buffer_copy_string_len(b, s.ptr, s.len);
1415 burl_normalize(b, t, flags);
1416 lua_pushlstring(L, BUF_PTR_LEN(b));
1417 chunk_buffer_release(t);
1418 magnet_tmpbuf_release(b);
1419 return 1;
1420 }
1421
magnet_fspath_simplify(lua_State * L)1422 static int magnet_fspath_simplify(lua_State *L) {
1423 /* simplify filesystem path */
1424 if (lua_isnoneornil(L, -1)) {
1425 lua_pushlstring(L, "", 0);
1426 return 1;
1427 }
1428 const_buffer s = magnet_checkconstbuffer(L, -1);
1429 if (0 == s.len) {
1430 lua_pushvalue(L, -1);
1431 return 1;
1432 }
1433 buffer * const b = magnet_tmpbuf_acquire(L);
1434 buffer_copy_string_len(b, s.ptr, s.len);
1435 buffer_path_simplify(b);
1436 lua_pushlstring(L, BUF_PTR_LEN(b));
1437 magnet_tmpbuf_release(b);
1438 return 1;
1439 }
1440
1441 __attribute_pure__
magnet_scan_quoted_string(const char * s)1442 static const char * magnet_scan_quoted_string (const char *s) {
1443 /* scan to end of of quoted-string (with s starting at '"')
1444 * loose parse; non-validating
1445 * - permit missing '"' at end of string (caller should check)
1446 * - stop at stray '\\' at end of string missing closing '"'
1447 * - not rejecting non-WS CTL chars
1448 */
1449 /*force_assert(*s == '"');*/
1450 do { ++s; } while (*s && *s != '"' && (*s != '\\' || (s[1] ? ++s : 0)));
1451 /*do { ++s; } while (*s && *s != '"' && (*s != '\\' || (*++s || (--s, 0))));*/
1452 return s;
1453 }
1454
magnet_push_quoted_string_range(lua_State * L,const char * b,const char * s)1455 static const char * magnet_push_quoted_string_range (lua_State *L, const char *b, const char *s) {
1456 /* quoted-string is unmodified (except for quoted-string end consistency)
1457 * including surrounding double-quotes and with quoted-pair unmodified */
1458 if (__builtin_expect( (*s == '"'), 1))
1459 lua_pushlstring(L, b, (size_t)(++s-b));
1460 else { /*(else invalid quoted-string, but handle anyway)*/
1461 /* append closing '"' for consistency */
1462 lua_pushlstring(L, b, (size_t)(s-b));
1463 if (*s != '\\')
1464 lua_pushlstring(L, "\"", 1);
1465 else { /* unescaped backslash at end of string; escape and close '"' */
1466 lua_pushlstring(L, "\\\\\"", 3);
1467 ++s; /*(now *s == '\0')*/
1468 }
1469 lua_concat(L, 2);
1470 }
1471 return s;
1472 }
1473
magnet_push_quoted_string(lua_State * L,const char * s)1474 static const char * magnet_push_quoted_string(lua_State *L, const char *s) {
1475 return magnet_push_quoted_string_range(L, s, magnet_scan_quoted_string(s));
1476 }
1477
magnet_cookie_param_push_token(lua_State * L,const char * s)1478 static const char * magnet_cookie_param_push_token(lua_State *L, const char *s) {
1479 const char *b = s;
1480 while (*s!='=' /*(note: not strictly rejecting all 'separators')*/
1481 && *s!=';' && *s!=' ' && *s!='\t' && *s!='\r' && *s!='\n' && *s)
1482 ++s;
1483 lua_pushlstring(L, b, (size_t)(s-b));
1484 return s;
1485 }
1486
magnet_cookie_tokens(lua_State * L)1487 static int magnet_cookie_tokens(lua_State *L) {
1488 /*(special-case cookies (';' separator); dup cookie-names overwritten)*/
1489 lua_createtable(L, 0, 0);
1490 if (lua_isnoneornil(L, 1))
1491 return 1;
1492 const char *s = luaL_checkstring(L, 1);
1493 do {
1494 while (*s==';' || *s==' ' || *s=='\t' || *s=='\r' || *s=='\n')
1495 ++s;
1496 if (*s == '\0') break;
1497 s = magnet_cookie_param_push_token(L, s);
1498 while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n')
1499 ++s;
1500 if (*s == '=') {
1501 do {
1502 ++s;
1503 } while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n');
1504 if (*s==';' || *s=='\0')
1505 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/
1506 else if (*s != '"')
1507 s = magnet_cookie_param_push_token(L, s);
1508 else
1509 s = magnet_push_quoted_string(L, s);
1510 }
1511 else {
1512 lua_pushlstring(L, "", 0); /*(lua_pushnil() would delete key)*/
1513 }
1514 lua_settable(L, -3);
1515 while (*s!=';' && *s!='\0') ++s; /* ignore/skip stray tokens */
1516 } while (*s++);
1517 return 1;
1518 }
1519
magnet_push_token(lua_State * L,const char * s)1520 static const char * magnet_push_token(lua_State *L, const char *s) {
1521 const char *b = s;
1522 while ( *s!=' ' && *s!='\t' && *s!='\r' && *s!='\n'
1523 && *s!=',' && *s!=';' && *s!='=' && *s)
1524 ++s;
1525 lua_pushlstring(L, b, (size_t)(s-b));
1526 return s;
1527 }
1528
magnet_header_tokens(lua_State * L)1529 static int magnet_header_tokens(lua_State *L) {
1530 /* split into sequence of tokens/words
1531 * Intermediate table is then more convenient to walk once quoted-string
1532 * parsed into table entries since quoted-string may contain separators.
1533 * Each token can be passed to lighty.c.quoteddec()
1534 * (lighty.c.quoteddec() returns string as-is if not quoted-string)
1535 * (note: non-validating;
1536 * e.g. existence of '=' token does not mean that next token is value,
1537 * and '=' in value which is not part of quoted-string will be
1538 * treated as separate token)
1539 * (note: case is preserved; non-quoted-string tokens are not lower-cased)
1540 * (words separated by whitespace are separate tokens unless quoted-string)
1541 * (if that format not permitted in a specific header, caller must detect)
1542 * (optional whitespace (OWS) and bad whitespace (BWS) are removed)
1543 * (caller can create lookup table from sequence table, as appropriate) */
1544 lua_createtable(L, 0, 0);
1545 if (lua_isnoneornil(L, 1))
1546 return 1;
1547 const char *s = luaL_checkstring(L, 1);
1548 int i = 0;
1549 do {
1550 while ( *s==' ' || *s=='\t' || *s=='\r' || *s=='\n')
1551 ++s;
1552 if (*s=='\0') break;
1553 if (*s==',' || *s==';' || *s=='=')
1554 lua_pushlstring(L, s++, 1);
1555 else if (*s != '"')
1556 s = magnet_push_token(L, s);
1557 else
1558 s = magnet_push_quoted_string(L, s);
1559 lua_rawseti(L, -2, ++i);
1560 } while (*s);
1561 return 1;
1562 }
1563
magnet_reqhdr_get(lua_State * L)1564 static int magnet_reqhdr_get(lua_State *L) {
1565 size_t klen;
1566 const char * const k = luaL_checklstring(L, 2, &klen);
1567 const request_st * const r = **(request_st ***)lua_touserdata(L, 1);
1568 const int id = http_header_hkey_get(k, (uint32_t)klen);
1569 magnet_push_buffer(L, http_header_request_get(r, id, k, klen));
1570 return 1;
1571 }
1572
magnet_reqhdr_set(lua_State * L)1573 static int magnet_reqhdr_set(lua_State *L) {
1574 const_buffer k = magnet_checkconstbuffer(L, 2);
1575 const_buffer v = magnet_checkconstbuffer(L, 3);
1576
1577 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
1578 enum http_header_e id = http_header_hkey_get(k.ptr, (uint32_t)k.len);
1579
1580 switch (id) {
1581 /*case HTTP_HEADER_OTHER:*/
1582 default:
1583 break;
1584
1585 case HTTP_HEADER_HOST:
1586 /* do not allow Host to be unset, even if HTTP/1.0
1587 * (change Host to something else, if you must */
1588 if (0 == v.len) return 0;
1589
1590 /*(must set r->http_host if r->http_host was not previously set)*/
1591 /* copied from request.c:http_request_header_set_Host() */
1592 r->http_host = http_header_request_set_ptr(r, HTTP_HEADER_HOST,
1593 CONST_STR_LEN("Host"));
1594 buffer_copy_string_len_lc(r->http_host, v.ptr, v.len);
1595 return 0;
1596
1597 case HTTP_HEADER_CONTENT_LENGTH:
1598 /* not attempting to handle Content-Length modification; may revisit */
1599 /* future: might permit setting to 0 to force discard of request body
1600 * but would have to check if request body present, and if
1601 * Content-Length was set, or if Transfer-Encoding: chunked,
1602 * and handle resetting internal chunked encoding state,
1603 * as well as making sure that this is handled properly for HTTP/2 */
1604 return 0; /* silently ignore; do not allow modification */
1605
1606 /* do not permit modification of hop-by-hop (connection) headers */
1607
1608 case HTTP_HEADER_CONNECTION:
1609 /* do not permit modification of Connection, incl add/remove tokens */
1610 /* future: might provide a different interface to set r->keep_alive = 0,
1611 * and also handle in context if HTTP/2 */
1612 case HTTP_HEADER_TRANSFER_ENCODING:
1613 case HTTP_HEADER_SET_COOKIE:/*(response hdr;avoid accidental reflection)*/
1614 return 0; /* silently ignore; do not allow modification */
1615 #if 0 /*(eh, if script sets Upgrade, script probably intends this action)*/
1616 case HTTP_HEADER_UPGRADE:
1617 /* note: modifications here do not modify Connection header
1618 * to add or remove "upgrade" token */
1619 /* future: might allow removal of existing tokens, but not addition */
1620 if (0 != v.len) return 0; /* do not allow Upgrade other than to unset */
1621 break;
1622 #endif
1623 #if 0 /*(eh, if script sets TE, script probably intends this action)*/
1624 case HTTP_HEADER_TE:
1625 if (0 != v.len) return 0; /* do not allow TE other than to unset */
1626 break;
1627 #endif
1628 }
1629
1630 v.len
1631 ? http_header_request_set(r, id, k.ptr, k.len, v.ptr, v.len)
1632 : http_header_request_unset(r, id, k.ptr, k.len);
1633 return 0;
1634 }
1635
magnet_reqhdr_pairs(lua_State * L)1636 static int magnet_reqhdr_pairs(lua_State *L) {
1637 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
1638 return magnet_array_pairs(L, &r->rqst_headers);
1639 }
1640
magnet_resphdr_get(lua_State * L)1641 static int magnet_resphdr_get(lua_State *L) {
1642 /* Note: access to lighttpd r->resp_headers here is *independent* from
1643 * the (pending) changes in the (deprecated) lua lighty.header[] table */
1644 size_t klen;
1645 const char * const k = luaL_checklstring(L, 2, &klen);
1646 const request_st * const r = **(request_st ***)lua_touserdata(L, 1);
1647 const int id = http_header_hkey_get(k, (uint32_t)klen);
1648 magnet_push_buffer(L, http_header_response_get(r, id, k, klen));
1649 return 1;
1650 }
1651
magnet_resphdr_set_kv(lua_State * L,request_st * const r)1652 static int magnet_resphdr_set_kv(lua_State *L, request_st * const r) {
1653 const const_buffer k = magnet_checkconstbuffer(L, -2);
1654 const const_buffer v = magnet_checkconstbuffer(L, -1);
1655 const enum http_header_e id = http_header_hkey_get(k.ptr, (uint32_t)k.len);
1656
1657 switch (id) {
1658 /*case HTTP_HEADER_OTHER:*/
1659 default:
1660 break;
1661
1662 case HTTP_HEADER_CONTENT_LENGTH:
1663 /* lighttpd handles Content-Length or Transfer-Encoding for response */
1664 return 0; /* silently ignore; do not allow modification */
1665
1666 /* do not permit modification of hop-by-hop (connection) headers */
1667
1668 case HTTP_HEADER_CONNECTION:
1669 /* do not permit modification of Connection, incl add/remove tokens */
1670 /* future: might provide a different interface to set r->keep_alive = 0,
1671 * and also handle in context if HTTP/2 */
1672 case HTTP_HEADER_TRANSFER_ENCODING:
1673 return 0; /* silently ignore; do not allow modification */
1674 }
1675
1676 if (0 == v.len) {
1677 http_header_response_unset(r, id, k.ptr, k.len);
1678 return 0;
1679 }
1680
1681 buffer * const vb = http_header_response_set_ptr(r, id, k.ptr, k.len);
1682 buffer_copy_string_len(vb, v.ptr, v.len);
1683
1684 if (r->http_version >= HTTP_VERSION_2) {
1685 /* handle multi-line response headers with HTTP/2
1686 * (lowercase header name and mark r->resp_header_repeated)
1687 * (similar to http_header.c:http_header_response_insert_addtl()) */
1688 for (char *n = vb->ptr; (n = strchr(n, '\n')); ) {
1689 r->resp_header_repeated = 1;
1690 do {
1691 ++n;
1692 if (light_isupper(*n)) *n |= 0x20;
1693 } while (*n != ':' && *n != '\n' && *n != '\0');
1694 }
1695 }
1696
1697 return 0;
1698 }
1699
magnet_resphdr_set(lua_State * L)1700 static int magnet_resphdr_set(lua_State *L) {
1701 /*const_buffer k = magnet_checkconstbuffer(L, 2);*/
1702 /*const_buffer v = magnet_checkconstbuffer(L, 3);*/
1703 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
1704 return magnet_resphdr_set_kv(L, r);
1705 }
1706
magnet_resphdr_pairs(lua_State * L)1707 static int magnet_resphdr_pairs(lua_State *L) {
1708 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
1709 return magnet_array_pairs(L, &r->resp_headers);
1710 }
1711
magnet_plugin_stats_get(lua_State * L)1712 static int magnet_plugin_stats_get(lua_State *L) {
1713 const_buffer k = magnet_checkconstbuffer(L, 2);
1714 lua_pushinteger(L, (lua_Integer)*plugin_stats_get_ptr(k.ptr, k.len));
1715 return 1;
1716 }
1717
magnet_plugin_stats_set(lua_State * L)1718 static int magnet_plugin_stats_set(lua_State *L) {
1719 const_buffer k = magnet_checkconstbuffer(L, 2);
1720 plugin_stats_set(k.ptr, k.len, luaL_checkinteger(L, 3));
1721 return 0;
1722 }
1723
magnet_plugin_stats_pairs(lua_State * L)1724 static int magnet_plugin_stats_pairs(lua_State *L) {
1725 return magnet_array_pairs(L, &plugin_stats);
1726 }
1727
1728
1729 static int
magnet_req_item_get(lua_State * L)1730 magnet_req_item_get (lua_State *L)
1731 {
1732 size_t klen;
1733 const char * const k = luaL_checklstring(L, 2, &klen);
1734 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
1735 switch (klen) {
1736 case 8:
1737 if (0 == memcmp(k, "bytes_in", 8)) {
1738 lua_pushinteger(L, (lua_Integer)http_request_stats_bytes_in(r));
1739 return 1;
1740 }
1741 break;
1742 case 9:
1743 if (0 == memcmp(k, "bytes_out", 9)) {
1744 lua_pushinteger(L, (lua_Integer)http_request_stats_bytes_out(r));
1745 return 1;
1746 }
1747 if (0 == memcmp(k, "stream_id", 9)) {
1748 lua_pushinteger(L, (lua_Integer)r->h2id);
1749 return 1;
1750 }
1751 if (0 == memcmp(k, "req_count", 9)) {
1752 lua_pushinteger(L, (lua_Integer)r->con->request_count);
1753 return 1;
1754 }
1755 break;
1756 case 10:
1757 if (0 == memcmp(k, "start_time", 10)) {
1758 lua_pushinteger(L, (lua_Integer)r->start_hp.tv_sec);
1759 lua_pushinteger(L, (lua_Integer)r->start_hp.tv_nsec);
1760 lua_pushcclosure(L, magnet_return_upvalue2, 2);
1761 return 1;
1762 }
1763 if (0 == memcmp(k, "keep_alive", 10)) {
1764 lua_pushinteger(L, (lua_Integer)r->keep_alive);
1765 return 1;
1766 }
1767 break;
1768 case 11:
1769 if (0 == memcmp(k, "http_status", 11)) {
1770 lua_pushinteger(L, (lua_Integer)r->http_status);
1771 return 1;
1772 }
1773 break;
1774 case 14:
1775 if (0 == memcmp(k, "req_header_len", 14)) {
1776 lua_pushinteger(L, (lua_Integer)r->rqst_header_len);
1777 return 1;
1778 }
1779 break;
1780 case 15:
1781 if (0 == memcmp(k, "resp_header_len", 15)) {
1782 lua_pushinteger(L, (lua_Integer)r->resp_header_len);
1783 return 1;
1784 }
1785 break;
1786 default:
1787 break;
1788 }
1789 return luaL_error(L, "r.req_item['%s'] invalid", k);
1790 }
1791
1792 static int
magnet_req_item_set(lua_State * L)1793 magnet_req_item_set (lua_State *L)
1794 {
1795 size_t klen;
1796 const char * const k = luaL_checklstring(L, 2, &klen);
1797 int v = (int)luaL_checkinteger(L, 3);
1798
1799 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
1800 switch (klen) {
1801 case 10:
1802 if (0 == memcmp(k, "keep_alive", 10)) {
1803 if (v == 0 || v == -1) r->keep_alive = v;
1804 return 0;
1805 }
1806 break;
1807 default:
1808 break;
1809 }
1810 return luaL_error(L, "r.req_item['%s'] invalid or read-only", k);
1811 }
1812
1813
1814 typedef struct {
1815 const char *name;
1816 uint32_t nlen;
1817 enum {
1818 MAGNET_ENV_UNSET,
1819
1820 MAGNET_ENV_PHYSICAL_PATH,
1821 MAGNET_ENV_PHYSICAL_REL_PATH,
1822 MAGNET_ENV_PHYSICAL_DOC_ROOT,
1823 MAGNET_ENV_PHYSICAL_BASEDIR,
1824
1825 MAGNET_ENV_URI_PATH,
1826 MAGNET_ENV_URI_PATH_RAW,
1827 MAGNET_ENV_URI_SCHEME,
1828 MAGNET_ENV_URI_AUTHORITY,
1829 MAGNET_ENV_URI_QUERY,
1830
1831 MAGNET_ENV_REQUEST_METHOD,
1832 MAGNET_ENV_REQUEST_URI,
1833 MAGNET_ENV_REQUEST_ORIG_URI,
1834 MAGNET_ENV_REQUEST_PATH_INFO,
1835 MAGNET_ENV_REQUEST_REMOTE_ADDR,
1836 MAGNET_ENV_REQUEST_REMOTE_PORT,
1837 MAGNET_ENV_REQUEST_SERVER_ADDR,
1838 MAGNET_ENV_REQUEST_SERVER_PORT,
1839 MAGNET_ENV_REQUEST_PROTOCOL,
1840 MAGNET_ENV_REQUEST_SERVER_NAME,
1841 MAGNET_ENV_REQUEST_STAGE
1842 } type;
1843 } magnet_env_t;
1844
1845 /*(NB: coordinate any changes with scan offsets in magnet_env_get_id())*/
1846 static const magnet_env_t magnet_env[] = {
1847 { CONST_STR_LEN("physical.path"), MAGNET_ENV_PHYSICAL_PATH },
1848 { CONST_STR_LEN("physical.rel-path"), MAGNET_ENV_PHYSICAL_REL_PATH },
1849 { CONST_STR_LEN("physical.doc-root"), MAGNET_ENV_PHYSICAL_DOC_ROOT },
1850 { CONST_STR_LEN("physical.basedir"), MAGNET_ENV_PHYSICAL_BASEDIR },
1851
1852 { CONST_STR_LEN("uri.path"), MAGNET_ENV_URI_PATH },
1853 { CONST_STR_LEN("uri.path-raw"), MAGNET_ENV_URI_PATH_RAW },
1854 { CONST_STR_LEN("uri.scheme"), MAGNET_ENV_URI_SCHEME },
1855 { CONST_STR_LEN("uri.authority"), MAGNET_ENV_URI_AUTHORITY },
1856 { CONST_STR_LEN("uri.query"), MAGNET_ENV_URI_QUERY },
1857
1858 { CONST_STR_LEN("request.method"), MAGNET_ENV_REQUEST_METHOD },
1859 { CONST_STR_LEN("request.uri"), MAGNET_ENV_REQUEST_URI },
1860 { CONST_STR_LEN("request.orig-uri"), MAGNET_ENV_REQUEST_ORIG_URI },
1861 { CONST_STR_LEN("request.path-info"), MAGNET_ENV_REQUEST_PATH_INFO },
1862 { CONST_STR_LEN("request.remote-ip"), MAGNET_ENV_REQUEST_REMOTE_ADDR },
1863 { CONST_STR_LEN("request.remote-addr"), MAGNET_ENV_REQUEST_REMOTE_ADDR },
1864 { CONST_STR_LEN("request.remote-port"), MAGNET_ENV_REQUEST_REMOTE_PORT },
1865 { CONST_STR_LEN("request.server-addr"), MAGNET_ENV_REQUEST_SERVER_ADDR },
1866 { CONST_STR_LEN("request.server-port"), MAGNET_ENV_REQUEST_SERVER_PORT },
1867 { CONST_STR_LEN("request.protocol"), MAGNET_ENV_REQUEST_PROTOCOL },
1868 { CONST_STR_LEN("request.server-name"), MAGNET_ENV_REQUEST_SERVER_NAME },
1869 { CONST_STR_LEN("request.stage"), MAGNET_ENV_REQUEST_STAGE },
1870
1871 { NULL, 0, MAGNET_ENV_UNSET }
1872 };
1873
1874 __attribute_cold__
1875 static void
magnet_env_get_uri_path_raw(buffer * const dest,const buffer * const target)1876 magnet_env_get_uri_path_raw (buffer * const dest, const buffer * const target)
1877 {
1878 const uint32_t len = buffer_clen(target);
1879 const char * const qmark = memchr(target->ptr, '?', len);
1880 buffer_copy_string_len(dest, target->ptr,
1881 qmark ? (uint32_t)(qmark - target->ptr) : len);
1882 }
1883
1884 __attribute_cold__
1885 static int
magnet_env_set_uri_path_raw(request_st * const r,const const_buffer * const val)1886 magnet_env_set_uri_path_raw (request_st * const r,
1887 const const_buffer * const val)
1888 {
1889 /* modify uri-path of r->target; preserve query-part, if present */
1890 /* XXX: should we require that resulting path begin with '/' or %2F ? */
1891 const uint32_t len = buffer_clen(&r->target);
1892 const char * const qmark = memchr(r->target.ptr, '?', len);
1893 if (NULL != qmark)
1894 buffer_copy_string_len(r->tmp_buf, qmark,
1895 len - (uint32_t)(qmark - r->target.ptr));
1896 buffer_copy_string_len(&r->target, val->ptr, val->len);
1897 if (NULL != qmark)
1898 buffer_append_string_buffer(&r->target, r->tmp_buf);
1899 return 0;
1900 }
1901
1902 __attribute_cold__
1903 __attribute_noinline__
1904 static buffer *
magnet_env_get_laddr_by_id(request_st * const r,const int id)1905 magnet_env_get_laddr_by_id (request_st * const r, const int id)
1906 {
1907 buffer * const dest = r->tmp_buf;
1908 const server_socket * const srv_socket = r->con->srv_socket;
1909 switch (id) {
1910 case MAGNET_ENV_REQUEST_SERVER_ADDR: /* local IP without port */
1911 if (sock_addr_is_addr_wildcard(&srv_socket->addr)) {
1912 sock_addr addrbuf;
1913 socklen_t addrlen = sizeof(addrbuf);
1914 const int fd = r->con->fd;
1915 if (0 == getsockname(fd,(struct sockaddr *)&addrbuf,&addrlen)) {
1916 char buf[INET6_ADDRSTRLEN + 1];
1917 const char *s = sock_addr_inet_ntop(&addrbuf, buf, sizeof(buf));
1918 if (NULL != s) {
1919 buffer_copy_string_len(dest, s, strlen(s));
1920 break;
1921 }
1922 }
1923 }
1924 buffer_copy_string_len(dest, srv_socket->srv_token->ptr,
1925 srv_socket->srv_token_colon);
1926 break;
1927 case MAGNET_ENV_REQUEST_SERVER_PORT:
1928 {
1929 const buffer * const srv_token = srv_socket->srv_token;
1930 const uint32_t tlen = buffer_clen(srv_token);
1931 uint32_t portoffset = srv_socket->srv_token_colon;
1932 portoffset = portoffset < tlen ? portoffset+1 : tlen;
1933 buffer_copy_string_len(dest, srv_token->ptr+portoffset,
1934 tlen-portoffset);
1935 break;
1936 }
1937 default:
1938 break;
1939 }
1940 return dest;
1941 }
1942
1943 static buffer *
magnet_env_get_buffer_by_id(request_st * const r,const int id)1944 magnet_env_get_buffer_by_id (request_st * const r, const int id)
1945 {
1946 buffer *dest = r->tmp_buf;
1947 buffer_clear(dest);
1948
1949 switch (id) {
1950 case MAGNET_ENV_PHYSICAL_PATH: dest = &r->physical.path; break;
1951 case MAGNET_ENV_PHYSICAL_REL_PATH: dest = &r->physical.rel_path; break;
1952 case MAGNET_ENV_PHYSICAL_DOC_ROOT: dest = &r->physical.doc_root; break;
1953 case MAGNET_ENV_PHYSICAL_BASEDIR: dest = &r->physical.basedir; break;
1954
1955 case MAGNET_ENV_URI_PATH: dest = &r->uri.path; break;
1956 case MAGNET_ENV_URI_PATH_RAW:
1957 magnet_env_get_uri_path_raw(dest, &r->target);
1958 break;
1959 case MAGNET_ENV_URI_SCHEME: dest = &r->uri.scheme; break;
1960 case MAGNET_ENV_URI_AUTHORITY: dest = &r->uri.authority; break;
1961 case MAGNET_ENV_URI_QUERY: dest = &r->uri.query; break;
1962
1963 case MAGNET_ENV_REQUEST_METHOD:
1964 http_method_append(dest, r->http_method);
1965 break;
1966 case MAGNET_ENV_REQUEST_URI: dest = &r->target; break;
1967 case MAGNET_ENV_REQUEST_ORIG_URI: dest = &r->target_orig; break;
1968 case MAGNET_ENV_REQUEST_PATH_INFO: dest = &r->pathinfo; break;
1969 case MAGNET_ENV_REQUEST_REMOTE_ADDR: dest = r->dst_addr_buf; break;
1970 case MAGNET_ENV_REQUEST_REMOTE_PORT:
1971 buffer_append_int(dest, sock_addr_get_port(r->dst_addr));
1972 break;
1973 case MAGNET_ENV_REQUEST_SERVER_ADDR: /* local IP without port */
1974 case MAGNET_ENV_REQUEST_SERVER_PORT:
1975 return magnet_env_get_laddr_by_id(r, id);
1976 case MAGNET_ENV_REQUEST_PROTOCOL:
1977 http_version_append(dest, r->http_version);
1978 break;
1979 case MAGNET_ENV_REQUEST_SERVER_NAME:
1980 buffer_copy_buffer(dest, r->server_name);
1981 break;
1982 case MAGNET_ENV_REQUEST_STAGE:
1983 if (http_request_state_is_keep_alive(r))
1984 buffer_append_string_len(dest, CONST_STR_LEN("keep-alive"));
1985 else
1986 http_request_state_append(dest, r->state);
1987 break;
1988
1989 case MAGNET_ENV_UNSET:
1990 return NULL;
1991 }
1992
1993 return dest;
1994 }
1995
1996 __attribute_pure__
magnet_env_get_id(const char * const key,const size_t klen)1997 static int magnet_env_get_id(const char * const key, const size_t klen) {
1998 /*(NB: ensure offsets match position in magnet_env[])*/
1999 int i; /* magnet_env[] scan offset */
2000 switch (*key) {
2001 case 'r': /* request.* or response.* */
2002 i = klen > 7 && key[7] == '.' ? 9 : 21;
2003 break;
2004 case 'u': /* uri.* */
2005 default:
2006 i = 4;
2007 break;
2008 case 'p': /* physical.* */
2009 i = 0;
2010 break;
2011 }
2012 for (; i < (int)(sizeof(magnet_env)/sizeof(*magnet_env)); ++i) {
2013 if (klen == magnet_env[i].nlen
2014 && 0 == memcmp(key, magnet_env[i].name, klen))
2015 return magnet_env[i].type;
2016 }
2017 return MAGNET_ENV_UNSET;
2018 }
2019
magnet_env_get(lua_State * L)2020 static int magnet_env_get(lua_State *L) {
2021 size_t klen;
2022 const char * const k = luaL_checklstring(L, 2, &klen);
2023 const int env_id = magnet_env_get_id(k, klen);
2024 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
2025 magnet_push_buffer(L, magnet_env_get_buffer_by_id(r, env_id));
2026 return 1;
2027 }
2028
2029 __attribute_cold__
2030 static int
magnet_env_set_raddr_by_id(lua_State * L,request_st * const r,const int id,const const_buffer * const val)2031 magnet_env_set_raddr_by_id (lua_State *L, request_st * const r, const int id,
2032 const const_buffer * const val)
2033 {
2034 switch (id) {
2035 case MAGNET_ENV_REQUEST_REMOTE_ADDR:
2036 #ifdef HAVE_SYS_UN_H
2037 if (val->len && *val->ptr == '/'
2038 && 0 == sock_addr_assign(r->dst_addr, AF_UNIX, 0, val->ptr)) {
2039 }
2040 else
2041 #endif
2042 {
2043 sock_addr saddr;
2044 saddr.plain.sa_family = AF_UNSPEC;
2045 if (1 == sock_addr_from_str_numeric(&saddr, val->ptr, r->conf.errh)
2046 && saddr.plain.sa_family != AF_UNSPEC) {
2047 sock_addr_set_port(&saddr, 0);
2048 memcpy(r->dst_addr, &saddr, sizeof(sock_addr));
2049 }
2050 else {
2051 return luaL_error(L,
2052 "r.req_attr['remote-addr'] invalid addr: %s",
2053 val->ptr);
2054 }
2055 }
2056 buffer_copy_string_len(r->dst_addr_buf, val->ptr, val->len);
2057 config_cond_cache_reset_item(r, COMP_HTTP_REMOTE_IP);
2058 break;
2059 case MAGNET_ENV_REQUEST_REMOTE_PORT:
2060 sock_addr_set_port(r->dst_addr, (unsigned short)atoi(val->ptr));
2061 break;
2062 default:
2063 break;
2064 }
2065 return 0;
2066 }
2067
2068 #if 0
2069 __attribute_cold__
2070 static int
2071 magnet_env_set_server_name (request_st * const r,
2072 const const_buffer * const val)
2073 {
2074 r->server_name = &r->server_name_buf;
2075 buffer_copy_string_len(&r->server_name_buf, val->ptr, val->len);
2076 return 0;
2077 }
2078 #endif
2079
magnet_env_set(lua_State * L)2080 static int magnet_env_set(lua_State *L) {
2081 size_t klen;
2082 const char * const key = luaL_checklstring(L, 2, &klen);
2083 const_buffer val = magnet_checkconstbuffer(L, 3);
2084
2085 const int env_id = magnet_env_get_id(key, klen);
2086 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
2087
2088 switch (env_id) {
2089 default:
2090 break;
2091 case MAGNET_ENV_URI_PATH_RAW:
2092 return magnet_env_set_uri_path_raw(r, &val);
2093 case MAGNET_ENV_REQUEST_REMOTE_ADDR:
2094 case MAGNET_ENV_REQUEST_REMOTE_PORT:
2095 return magnet_env_set_raddr_by_id(L, r, env_id, &val);
2096 #if 0 /*(leave read-only for now; change attempts silently ignored)*/
2097 case MAGNET_ENV_REQUEST_SERVER_NAME:
2098 return magnet_env_set_server_name(r, &val);
2099 #endif
2100 /*case MAGNET_ENV_REQUEST_STAGE:*//*(change attempts silently ignored)*/
2101 }
2102
2103 buffer * const dest = magnet_env_get_buffer_by_id(r, env_id);
2104 if (NULL == dest)
2105 return luaL_error(L, "couldn't store '%s' in r.req_attr[]", key);
2106
2107 if (lua_isnoneornil(L, 3)) {
2108 if (env_id==MAGNET_ENV_URI_QUERY || env_id==MAGNET_ENV_PHYSICAL_PATH)
2109 buffer_clear(dest);
2110 else
2111 buffer_blank(dest);
2112 }
2113 else {
2114 buffer_copy_string_len(dest, val.ptr, val.len);
2115 /* NB: setting r->uri.query does not modify query-part in r->target */
2116 }
2117
2118 switch (env_id) {
2119 case MAGNET_ENV_URI_SCHEME:
2120 buffer_to_lower(dest);
2121 config_cond_cache_reset_item(r, COMP_HTTP_SCHEME);
2122 break;
2123 case MAGNET_ENV_URI_AUTHORITY:
2124 r->server_name = dest;
2125 buffer_to_lower(dest);
2126 config_cond_cache_reset_item(r, COMP_HTTP_HOST);
2127 break;
2128 case MAGNET_ENV_URI_PATH:
2129 config_cond_cache_reset_item(r, COMP_HTTP_URL);
2130 break;
2131 case MAGNET_ENV_URI_QUERY:
2132 config_cond_cache_reset_item(r, COMP_HTTP_QUERY_STRING);
2133 break;
2134 default:
2135 break;
2136 }
2137
2138 return 0;
2139 }
2140
magnet_env_next(lua_State * L)2141 static int magnet_env_next(lua_State *L) {
2142 /* ignore previous key: use upvalue for current pos */
2143 lua_settop(L, 0);
2144 const int pos = lua_tointeger(L, lua_upvalueindex(1));
2145
2146 if (NULL == magnet_env[pos].name) return 0; /* end of list */
2147 /* Update our positional upval to reflect our new current position */
2148 lua_pushinteger(L, pos + 1);
2149 lua_replace(L, lua_upvalueindex(1));
2150
2151 /* key to return */
2152 lua_pushlstring(L, magnet_env[pos].name, magnet_env[pos].nlen);
2153
2154 /* get value */
2155 request_st * const r = lua_touserdata(L, lua_upvalueindex(2));
2156 magnet_push_buffer(L, magnet_env_get_buffer_by_id(r, magnet_env[pos].type));
2157
2158 /* return 2 items on the stack (key, value) */
2159 return 2;
2160 }
2161
magnet_env_pairs(lua_State * L)2162 static int magnet_env_pairs(lua_State *L) {
2163 lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */
2164 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
2165 lua_pushlightuserdata(L, r); /* Push request_st *r into upval 2 */
2166 lua_pushcclosure(L, magnet_env_next, 2); /* Push new closure with 2 upvals*/
2167 return 1;
2168 }
2169
magnet_envvar_get(lua_State * L)2170 static int magnet_envvar_get(lua_State *L) {
2171 size_t klen;
2172 const char * const k = luaL_checklstring(L, 2, &klen);
2173 const request_st * const r = **(request_st ***)lua_touserdata(L, 1);
2174 magnet_push_buffer(L, http_header_env_get(r, k, klen));
2175 return 1;
2176 }
2177
magnet_envvar_set(lua_State * L)2178 static int magnet_envvar_set(lua_State *L) {
2179 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
2180 const_buffer key = magnet_checkconstbuffer(L, 2);
2181 if (__builtin_expect( (lua_isnil(L, 3)), 0)) {
2182 buffer * const v = http_header_env_get(r, key.ptr, key.len);
2183 if (v) buffer_clear(v); /*(unset)*/
2184 return 0;
2185 }
2186 const_buffer val = magnet_checkconstbuffer(L, 3);
2187 http_header_env_set(r, key.ptr, key.len, val.ptr, val.len);
2188 return 0;
2189 }
2190
magnet_envvar_pairs(lua_State * L)2191 static int magnet_envvar_pairs(lua_State *L) {
2192 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
2193 return magnet_array_pairs(L, &r->env);
2194 }
2195
2196
magnet_respbody_add(lua_State * L)2197 static int magnet_respbody_add(lua_State *L) {
2198 request_st * const r = lua_touserdata(L, lua_upvalueindex(1));
2199 if (lua_isstring(L, -1)) {
2200 const_buffer data = magnet_checkconstbuffer(L, -1);
2201 http_chunk_append_mem(r, data.ptr, data.len);
2202 return 1; /* boolean true */
2203 }
2204 else if (!lua_istable(L, -1))
2205 return 0; /* boolean false */
2206
2207 /* note: differs from magnet_attach_content();
2208 * magnet_attach_content() has misnamed 'length' param which
2209 * is treated as 0-offset pos one after end of range to send.
2210 * Here, 'length' means 'length', as one would expect */
2211 for (int i=1, end=0, n=(int)lua_rawlen(L,-1); !end && i <= n; ++i) {
2212 lua_rawgeti(L, -1, i);
2213
2214 if (lua_isstring(L, -1)) {
2215 const_buffer data = magnet_checkconstbuffer(L, -1);
2216 http_chunk_append_mem(r, data.ptr, data.len);
2217 }
2218 else if (lua_istable(L, -1)) {
2219 lua_getfield(L, -1, "filename");
2220 lua_getfield(L, -2, "length");
2221 lua_getfield(L, -3, "offset");
2222
2223 if (lua_isstring(L, -3)) { /* filename has to be a string */
2224 off_t off = (off_t) luaL_optinteger(L, -1, 0);
2225 off_t len = (off_t) luaL_optinteger(L, -2, -1);
2226 /*(-1 len as flag to use file size minus offset (below))*/
2227 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/
2228 const buffer * const fn = magnet_checkbuffer(L, -3, &stor);
2229 stat_cache_entry * const sce = (!buffer_is_blank(fn))
2230 ? stat_cache_get_entry_open(fn, r->conf.follow_symlink)
2231 : NULL;
2232 if (sce && (sce->fd >= 0 || sce->st.st_size == 0)) {
2233 /* treat negative offset as bytes from end of file */
2234 /* treat negative len as bytes from offset to end of file */
2235 if (off > sce->st.st_size)
2236 off = sce->st.st_size;
2237 else if (off < 0) {
2238 off = sce->st.st_size - off;
2239 if (off < 0) off = 0;
2240 }
2241 if (len < 0 || sce->st.st_size - off < len)
2242 len = sce->st.st_size - off;
2243 if (len)
2244 http_chunk_append_file_ref_range(r, sce, off, len);
2245 }
2246 else {
2247 log_error(r->conf.errh, __FILE__, __LINE__,
2248 "error opening file '%s'", fn->ptr);
2249 end = 1;
2250 }
2251 }
2252 else {
2253 log_error(r->conf.errh, __FILE__, __LINE__,
2254 "body[%d] table field \"filename\" must be a string", i);
2255 end = 1;
2256 }
2257
2258 lua_pop(L, 3);
2259 }
2260 else if (lua_isnil(L, -1)) { /* end of list */
2261 end = 1;
2262 }
2263 else {
2264 log_error(r->conf.errh, __FILE__, __LINE__,
2265 "body[%d] is neither a string nor a table", i);
2266 end = 1;
2267 }
2268
2269 lua_pop(L, 1); /* pop the content[...] entry value */
2270 }
2271
2272 return 1; /* boolean true */
2273 }
2274
2275
magnet_respbody(lua_State * L)2276 static int magnet_respbody(lua_State *L) {
2277 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
2278 size_t klen;
2279 const char * const k = luaL_checklstring(L, 2, &klen);
2280 switch (k[0]) {
2281 case 'a': /* add; r.resp_body.add */
2282 if (k[1] == 'd' && k[2] == 'd' && k[3] == '\0') {
2283 lua_pushlightuserdata(L, r);
2284 lua_pushcclosure(L, magnet_respbody_add, 1);
2285 return 1;
2286 }
2287 break;
2288 case 'b':
2289 if (klen == 8 && 0 == memcmp(k, "bytes_in", 8)) {
2290 lua_pushinteger(L, r->write_queue.bytes_in);
2291 return 1;
2292 }
2293 if (klen == 9 && 0 == memcmp(k, "bytes_out", 9)) {
2294 lua_pushinteger(L, r->write_queue.bytes_out);
2295 return 1;
2296 }
2297 break;
2298 #if 0 /*(future: provide pairs() interface to iterate over chunkqueue)*/
2299 /*(might convert chunks into table of strings, {filename="..."})*/
2300 /*(what about c->offset into chunk?)*/
2301 #endif
2302 case 'g': /* get; r.resp_body.get */
2303 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') {
2304 if (r->resp_body_finished) {
2305 chunkqueue * const cq = &r->write_queue;
2306 chunkqueue_length(cq)
2307 ? magnet_push_buffer(L,
2308 chunkqueue_read_squash(cq,r->conf.errh))
2309 : (void)lua_pushlstring(L, "", 0);
2310 }
2311 else
2312 lua_pushnil(L); /*(?maybe return -1 instead if len unknown?)*/
2313 return 1;
2314 }
2315 break;
2316 case 'l': /* len; r.resp_body.len */
2317 if (k[1] == 'e' && k[2] == 'n' && k[3] == '\0') {
2318 if (r->resp_body_finished)
2319 lua_pushinteger(L, chunkqueue_length(&r->write_queue));
2320 else
2321 lua_pushnil(L); /*(?maybe return -1 instead if len unknown?)*/
2322 return 1;
2323 }
2324 break;
2325 case 's': /* set; r.resp_body.set */
2326 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') {
2327 http_response_body_clear(r, 0); /* clear respbody, then add */
2328 lua_pushlightuserdata(L, r);
2329 lua_pushcclosure(L, magnet_respbody_add, 1);
2330 return 1;
2331 }
2332 break;
2333 default:
2334 break;
2335 }
2336 lua_pushliteral(L, "r.resp_body invalid method or param");
2337 lua_error(L);
2338 return 0;
2339 }
2340
2341
magnet_reqbody_add(lua_State * L)2342 static int magnet_reqbody_add(lua_State *L) {
2343 request_st * const r = lua_touserdata(L, lua_upvalueindex(1));
2344 chunkqueue * const cq = &r->reqbody_queue;
2345 const int tempfile = (cq->last && cq->last->file.is_temp);
2346 if (lua_isstring(L, -1)) {
2347 const_buffer data = magnet_checkconstbuffer(L, -1);
2348 r->reqbody_length += data.len;
2349 if (r->reqbody_length <= 65536 && !tempfile)
2350 chunkqueue_append_mem(cq, data.ptr, data.len);
2351 else if (chunkqueue_append_mem_to_tempfile(cq, data.ptr, data.len,
2352 r->conf.errh))
2353 return 0; /* boolean false */
2354 return 1; /* boolean true */
2355 }
2356 else if (!lua_istable(L, -1))
2357 return 0; /* boolean false */
2358
2359 for (int i=1, end=0, n=(int)lua_rawlen(L,-1); !end && i <= n; ++i) {
2360 lua_rawgeti(L, -1, i);
2361
2362 if (lua_isstring(L, -1)) {
2363 const_buffer data = magnet_checkconstbuffer(L, -1);
2364 r->reqbody_length += data.len;
2365 if (r->reqbody_length <= 65536 && !tempfile)
2366 chunkqueue_append_mem(cq, data.ptr, data.len);
2367 else if (chunkqueue_append_mem_to_tempfile(cq, data.ptr, data.len,
2368 r->conf.errh))
2369 return 0; /* boolean false */
2370 }
2371 else if (lua_isnil(L, -1)) { /* end of list */
2372 end = 1;
2373 }
2374 else {
2375 log_error(r->conf.errh, __FILE__, __LINE__,
2376 "body[%d] table must contain strings", i);
2377 end = 1;
2378 }
2379
2380 lua_pop(L, 1); /* pop the content[...] entry value */
2381 }
2382
2383 return 1; /* boolean true */
2384 }
2385
2386
magnet_reqbody(lua_State * L)2387 static int magnet_reqbody(lua_State *L) {
2388 request_st * const r = **(request_st ***)lua_touserdata(L, 1);
2389 size_t klen;
2390 const char * const k = luaL_checklstring(L, 2, &klen);
2391 switch (k[0]) {
2392 case 'a': /* add; r.req_body.add */
2393 if (k[1] == 'd' && k[2] == 'd' && k[3] == '\0') {
2394 chunkqueue * const cq = &r->reqbody_queue;
2395 if (cq->bytes_in == (off_t)r->reqbody_length) {
2396 lua_pushlightuserdata(L, r);
2397 lua_pushcclosure(L, magnet_reqbody_add, 1);
2398 }
2399 else /* reqbody not yet collected */
2400 lua_pushnil(L);
2401 return 1;
2402 }
2403 break;
2404 case 'b':
2405 if (klen == 8 && 0 == memcmp(k, "bytes_in", 8)) {
2406 lua_pushinteger(L, r->reqbody_queue.bytes_in);
2407 return 1;
2408 }
2409 if (klen == 9 && 0 == memcmp(k, "bytes_out", 9)) {
2410 lua_pushinteger(L, r->reqbody_queue.bytes_out);
2411 return 1;
2412 }
2413 break;
2414 case 'c': /* collect; r.req_body.collect */
2415 if (klen == 7 && 0 == memcmp(k, "collect", 7)) {
2416 chunkqueue * const cq = &r->reqbody_queue;
2417 if (cq->bytes_in == (off_t)r->reqbody_length)
2418 lua_pushboolean(L, 1);
2419 else if (NULL == r->handler_module) {
2420 r->conf.stream_request_body &=
2421 ~(FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN);
2422 r->handler_module = plugin_data_singleton->self;
2423 lua_pushboolean(L, 0);
2424 }
2425 else if (0 == strcmp(r->handler_module->name, "security3")) {
2426 /*(mod_security3 uses similar technique to collect req body)*/
2427 lua_pushboolean(L, 0);
2428 }
2429 else {
2430 log_error(r->conf.errh, __FILE__, __LINE__,
2431 "unable to collect request body (handler already set); "
2432 "(perhaps load mod_magnet earlier in server.modules, "
2433 "before mod_%s; or require r.req_env['REMOTE_USER'] before "
2434 "attempting r.req_body.collect?)", r->handler_module->name);
2435 lua_pushnil(L);
2436 }
2437 return 1;
2438 }
2439 break;
2440 case 'g': /* get; r.req_body.get */
2441 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') {
2442 chunkqueue * const cq = &r->reqbody_queue;
2443 if (cq->bytes_in == (off_t)r->reqbody_length)
2444 chunkqueue_length(cq)
2445 ? magnet_push_buffer(L,
2446 chunkqueue_read_squash(cq, r->conf.errh))
2447 : (void)lua_pushlstring(L, "", 0);
2448 else
2449 lua_pushnil(L); /*(?maybe return -1 instead if len unknown?)*/
2450 return 1;
2451 }
2452 break;
2453 case 'l': /* len */
2454 if (k[1] == 'e' && k[2] == 'n' && k[3] == '\0') {
2455 lua_pushinteger(L, r->reqbody_length);
2456 return 1;
2457 }
2458 break;
2459 case 's': /* set; r.req_body.set */
2460 if (k[1] == 'e' && k[2] == 't' && k[3] == '\0') {
2461 chunkqueue * const cq = &r->reqbody_queue;
2462 if (cq->bytes_in == (off_t)r->reqbody_length) {
2463 r->reqbody_length = 0;
2464 chunkqueue_reset(&r->reqbody_queue);
2465 lua_pushlightuserdata(L, r);
2466 lua_pushcclosure(L, magnet_reqbody_add, 1);
2467 }
2468 else /* reqbody not yet collected */
2469 lua_pushnil(L);
2470 return 1;
2471 }
2472 break;
2473 default:
2474 break;
2475 }
2476 lua_pushliteral(L, "r.req_body invalid method or param");
2477 lua_error(L);
2478 return 0;
2479 }
2480
2481
2482 __attribute_cold__
magnet_lighty_result_get(lua_State * L)2483 static int magnet_lighty_result_get(lua_State *L) {
2484 /* __index: param 1 is the lighty table the value was not found in */
2485 lua_pushvalue(L, 2);
2486 lua_rawget(L, lua_upvalueindex(1));
2487 if (lua_isnil(L, -1)) {
2488 const_buffer k = magnet_checkconstbuffer(L, 2);
2489 if ( (k.len == 6 && 0 == memcmp(k.ptr, "header", 6))
2490 || (k.len == 7 && 0 == memcmp(k.ptr, "content", 7))) {
2491 lua_pop(L, 1); /* pop nil */
2492 lua_createtable(L, 0, 0); /* create "header","content" on demand */
2493 lua_pushvalue(L, 2); /* k: "header" or "content" */
2494 lua_pushvalue(L, -2); /* v: table */
2495 lua_rawset(L, lua_upvalueindex(1)); /* set in result table */
2496 }
2497 }
2498 return 1;
2499 }
2500
2501 __attribute_cold__
magnet_lighty_result_set(lua_State * L)2502 static int magnet_lighty_result_set(lua_State *L) {
2503 /* __newindex: param 1 is lighty table the value is supposed to be set in */
2504 /* assign value to alternate table; replacing existing value, if any */
2505 lua_rawset(L, lua_upvalueindex(1)); /* set in result table */
2506 return 0;
2507 }
2508
2509
2510 __attribute_cold__
2511 __attribute_noinline__
magnet_copy_response_header(lua_State * const L,request_st * const r)2512 static void magnet_copy_response_header(lua_State * const L, request_st * const r) {
2513 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
2514 if (lua_isstring(L, -1) && lua_isstring(L, -2))
2515 magnet_resphdr_set_kv(L, r);
2516 }
2517 }
2518
2519 /**
2520 * (deprecated API)
2521 * walk through the content array set by lua script, e.g.
2522 * lighty.header["Content-Type"] = "text/html"
2523 * lighty.content =
2524 * { "<html><body><pre>", { file = "/content" } , "</pre></body></html>" }
2525 * return 200
2526 */
2527 __attribute_cold__
2528 __attribute_noinline__
magnet_attach_content(lua_State * const L,request_st * const r)2529 static void magnet_attach_content(lua_State * const L, request_st * const r) {
2530 http_response_body_clear(r, 0);
2531 for (int i=1, end=0, n=(int)lua_rawlen(L,-1); !end && i <= n; ++i) {
2532 lua_rawgeti(L, -1, i);
2533
2534 /* -1 is the value and should be the value ... aka a table */
2535 if (lua_isstring(L, -1)) {
2536 const_buffer data = magnet_checkconstbuffer(L, -1);
2537 http_chunk_append_mem(r, data.ptr, data.len);
2538 } else if (lua_istable(L, -1)) {
2539 lua_getfield(L, -1, "filename");
2540 lua_getfield(L, -2, "length"); /* (0-based) end of range (not actually "length") */
2541 lua_getfield(L, -3, "offset"); /* (0-based) start of range */
2542
2543 if (lua_isstring(L, -3)) { /* filename has to be a string */
2544 /*(luaL_optinteger might raise error, which we want to avoid)*/
2545 /*off_t off = (off_t) luaL_optinteger(L, -1, 0);*/
2546 /*off_t len = (off_t) luaL_optinteger(L, -2, -1);*/ /*(-1 as flag to use file size minus offset (below))*/
2547 int isnum = 1;
2548 off_t off = lua_isnil(L, -1) ? 0 : (off_t) lua_tointegerx(L, -1, &isnum);
2549 if (!isnum) {
2550 off = 0;
2551 log_error(r->conf.errh, __FILE__, __LINE__,
2552 "content[%d] is a table and field \"offset\" must be an integer", i);
2553 }
2554 isnum = 1;
2555 off_t len = lua_isnil(L, -2) ? -1 : (off_t) lua_tointegerx(L, -2, &isnum);
2556 /*(-1 len as flag to use file size minus offset (below))*/
2557 if (!isnum) {
2558 len = -1;
2559 log_error(r->conf.errh, __FILE__, __LINE__,
2560 "content[%d] is a table and field \"length\" must be an integer", i);
2561 }
2562 if (off < 0) {
2563 log_error(r->conf.errh, __FILE__, __LINE__,
2564 "offset for '%s' is negative", lua_tostring(L, -3));
2565 end = 1;
2566 } else if (len >= off) {
2567 len -= off;
2568 } else if (-1 != len) {
2569 log_error(r->conf.errh, __FILE__, __LINE__,
2570 "offset > length for '%s'", lua_tostring(L, -3));
2571 end = 1;
2572 }
2573
2574 if (!end && 0 != len) {
2575 buffer stor; /*(note: do not free magnet_checkbuffer() result)*/
2576 const buffer * const fn = magnet_checkbuffer(L, -3, &stor);
2577 stat_cache_entry * const sce = (!buffer_is_blank(fn))
2578 ? stat_cache_get_entry_open(fn, r->conf.follow_symlink)
2579 : NULL;
2580 if (sce && (sce->fd >= 0 || sce->st.st_size == 0)) {
2581 if (len == -1 || sce->st.st_size - off < len)
2582 len = sce->st.st_size - off;
2583 if (len > 0)
2584 http_chunk_append_file_ref_range(r, sce, off, len);
2585 } else {
2586 log_error(r->conf.errh, __FILE__, __LINE__,
2587 "error opening file content '%s' at offset %lld",
2588 lua_tostring(L, -3), (long long)off);
2589 end = 1;
2590 }
2591 }
2592 } else {
2593 log_error(r->conf.errh, __FILE__, __LINE__,
2594 "content[%d] is a table and field \"filename\" must be a string", i);
2595 end = 1;
2596 }
2597
2598 lua_pop(L, 3);
2599 } else if (lua_isnil(L, -1)) {
2600 /* end of list */
2601 end = 1;
2602 } else {
2603 log_error(r->conf.errh, __FILE__, __LINE__,
2604 "content[%d] is neither a string nor a table", i);
2605 end = 1;
2606 }
2607
2608 lua_pop(L, 1); /* pop the content[...] entry value */
2609 }
2610 }
2611
2612 __attribute_cold__
magnet_mainenv_metatable(lua_State * const L)2613 static void magnet_mainenv_metatable(lua_State * const L) {
2614 if (luaL_newmetatable(L, "li.mainenv")) { /* (sp += 1) */
2615 lua_pushglobaltable(L); /* (sp += 1) */
2616 lua_setfield(L, -2, "__index"); /* { __index = _G } (sp -= 1) */
2617 lua_pushboolean(L, 0); /* (sp += 1) */
2618 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2619 }
2620 }
2621
2622 static void
magnet_request_userdata_method(lua_State * const L,request_st ** const rr,const char * meta)2623 magnet_request_userdata_method (lua_State * const L, request_st ** const rr, const char *meta)
2624 {
2625 /*(meta is name of cached metatable; meta must start w/ "li." prefix)*/
2626 *(request_st ***)lua_newuserdata0(L, sizeof(request_st **)) = rr;
2627 #ifdef __COVERITY__ /* shut up coverity; read the comment below */
2628 if (luaL_newmetatable(L, meta)) { }
2629 #else
2630 luaL_newmetatable(L, meta); /*(should not fail; init'd in script setup)*/
2631 #endif
2632 lua_setmetatable(L, -2);
2633 lua_setfield(L, -2, meta+3); /*(meta+3 to skip over "li." prefix)*/
2634 }
2635
2636 static void
magnet_request_table(lua_State * const L,request_st ** const rr)2637 magnet_request_table (lua_State * const L, request_st ** const rr)
2638 {
2639 /* r table
2640 *
2641 * r.req_header[] HTTP request headers
2642 * r.req_attr[] HTTP request attributes / components (strings)
2643 * r.req_item[] HTTP request items (struct members, statistics)
2644 * r.req_env[] HTTP request environment variables
2645 * r.req_body.* HTTP request body accessors
2646 * r.req_body.bytes_in HTTP request body chunkqueue bytes_in
2647 * r.req_body.bytes_out HTTP request body chunkqueue bytes_out
2648 * r.resp_header[] HTTP response headers
2649 * r.resp_body.* HTTP response body accessors
2650 * r.resp_body.len HTTP response body length
2651 * r.resp_body.add() HTTP response body add (string or table)
2652 * r.resp_body.set() HTTP response body set (string or table)
2653 * r.resp_body.bytes_in HTTP response body chunkqueue bytes_in
2654 * r.resp_body.bytes_out HTTP response body chunkqueue bytes_out
2655 */
2656 lua_createtable(L, 0, 7); /* (sp += 1) */
2657
2658 /* userdata methods share ptr-ptr to external object userdata rr
2659 * (similar functionality to that provided w/ luaL_setfuncs() in lua 5.2+)*/
2660 magnet_request_userdata_method(L, rr, "li.req_header"); /* req_header */
2661 magnet_request_userdata_method(L, rr, "li.req_attr"); /* req_attr */
2662 magnet_request_userdata_method(L, rr, "li.req_item"); /* req_item */
2663 magnet_request_userdata_method(L, rr, "li.req_env"); /* req_env */
2664 magnet_request_userdata_method(L, rr, "li.resp_header");/* resp_header */
2665 magnet_request_userdata_method(L, rr, "li.resp_body"); /* resp_body */
2666 magnet_request_userdata_method(L, rr, "li.req_body"); /* req_body */
2667
2668 lua_createtable(L, 0, 2); /* metatable for r table (sp += 1) */
2669 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */
2670 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2671 lua_pushboolean(L, 0); /* (sp += 1) */
2672 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2673 lua_setmetatable(L, -2); /* tie the metatable to r (sp -= 1) */
2674 }
2675
2676 static int
magnet_request_iter(lua_State * L)2677 magnet_request_iter (lua_State *L)
2678 {
2679 /* upvalue 1: (connection *) in linked list
2680 * upvalue 2: index into h2con->r[]
2681 * upvalue 3: request userdata
2682 * upvalue 4: request table (references r in userdata) */
2683 connection *con = lua_touserdata(L, lua_upvalueindex(1));
2684
2685 /* skip over HTTP/2 connections with no active requests */
2686 while (con && con->h2 && 0 == con->h2->rused)
2687 con = con->next;
2688 if (NULL == con)
2689 return 0;
2690
2691 /* set (request_st *)r */
2692 int32_t i = -1;
2693 if (con->h2) {
2694 /* get index into h2con->r[] */
2695 i = lua_tointeger(L, lua_upvalueindex(2));
2696 /* set (request_st *)r in userdata */
2697 if (-1 == i)
2698 *(request_st **)lua_touserdata(L,lua_upvalueindex(3))=&con->request;
2699 else
2700 *(request_st **)lua_touserdata(L,lua_upvalueindex(3))=con->h2->r[i];
2701 /* step to next index into h2con->r[] */
2702 if ((uint32_t)++i == con->h2->rused)
2703 i = -1;
2704 lua_pushinteger(L, i);
2705 lua_replace(L, lua_upvalueindex(2));
2706 }
2707 else {
2708 /* set (request_st *) in userdata */
2709 *(request_st **)lua_touserdata(L, lua_upvalueindex(3)) = &con->request;
2710 }
2711
2712 if (-1 == i) {
2713 /* step to next connection */
2714 con = con->next;
2715 lua_pushlightuserdata(L, con);
2716 lua_replace(L, lua_upvalueindex(1));
2717 }
2718
2719 /* return request object (which references (request_st *)r in userdata) */
2720 lua_pushvalue(L, lua_upvalueindex(4));
2721 return 1;
2722 }
2723
2724 static int
magnet_irequests(lua_State * L)2725 magnet_irequests (lua_State *L)
2726 {
2727 /* NB: iterator request object *is invalid* outside of iteration
2728 * For efficiency, r is stored in userdata as upvalue to iteration
2729 * and is invalid (and may be cleaned up) outside of iterator loop.
2730 * A C pointer into userdata is stored in iterator request object methods.
2731 * The iterator request object is *reused* for each iteration loop and the
2732 * upvalue to the userdata is changed each iteration to point to next r.
2733 * The iterator request object *must not* be saved for use outside
2734 * the iteration loop. Extract data that must be saved and store data
2735 * in a persistent object if data is to be used outside iterator loop.
2736 * (Were it desirable to produce a persistent request object for use outside
2737 * the iteration loop, a future enhancement would be to add a method such
2738 * as lighty.server.irequests_clone(r) which creates a new request object
2739 * pointing into a new userdata, and saving that new userdata in the new
2740 * request object table as '_r_userdata')
2741 * NB: iterator request objects should generally be treated read-only.
2742 * Modifications may in some cases be unsafe and cause lighttpd to crash. */
2743 request_st *r = magnet_get_request(L);
2744 lua_pushlightuserdata(L, r->con->srv->conns);
2745 lua_pushinteger(L, -1);
2746 request_st ** const r_userdata =
2747 (request_st **)lua_newuserdata0(L, sizeof(request_st *));
2748 magnet_request_table(L, r_userdata);
2749 lua_pushcclosure(L, magnet_request_iter, 4);
2750 return 1;
2751 }
2752
2753 static int
magnet_server_stats_get(lua_State * L)2754 magnet_server_stats_get (lua_State *L)
2755 {
2756 size_t klen;
2757 const char * const k = luaL_checklstring(L, 2, &klen);
2758 const request_st * const r = magnet_get_request(L);
2759 const server * const srv = r->con->srv;
2760 switch (klen) {
2761 case 6:
2762 if (0 == memcmp(k, "uptime", 6)) {
2763 lua_pushinteger(L, (lua_Integer)(log_epoch_secs - srv->startup_ts));
2764 return 1;
2765 }
2766 break;
2767 case 7:
2768 if (0 == memcmp(k, "version", 7)) {
2769 lua_pushlstring(L, BUF_PTR_LEN(srv->default_server_tag));
2770 return 1;
2771 }
2772 break;
2773 case 12:
2774 /*(could calculate from irequests: ++count on remote-addr/port change)*/
2775 if (0 == memcmp(k, "clients_open", 12)) {
2776 lua_pushinteger(L, (lua_Integer)
2777 (srv->srvconf.max_conns - srv->lim_conns));
2778 return 1;
2779 }
2780 break;
2781 default:
2782 break;
2783 }
2784 return luaL_error(L, "server.stats['%s'] invalid", k);
2785 }
2786
2787
2788 __attribute_cold__
2789 static void
magnet_req_header_metatable(lua_State * const L)2790 magnet_req_header_metatable (lua_State * const L)
2791 {
2792 if (luaL_newmetatable(L, "li.req_header") == 0) /* (sp += 1) */
2793 return;
2794
2795 lua_pushcfunction(L, magnet_reqhdr_get); /* (sp += 1) */
2796 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2797 lua_pushcfunction(L, magnet_reqhdr_set); /* (sp += 1) */
2798 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2799 lua_pushcfunction(L, magnet_reqhdr_pairs); /* (sp += 1) */
2800 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */
2801 lua_pushboolean(L, 0); /* (sp += 1) */
2802 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2803 }
2804
2805 __attribute_cold__
2806 static void
magnet_req_attr_metatable(lua_State * const L)2807 magnet_req_attr_metatable (lua_State * const L)
2808 {
2809 if (luaL_newmetatable(L, "li.req_attr") == 0) /* (sp += 1) */
2810 return;
2811
2812 lua_pushcfunction(L, magnet_env_get); /* (sp += 1) */
2813 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2814 lua_pushcfunction(L, magnet_env_set); /* (sp += 1) */
2815 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2816 lua_pushcfunction(L, magnet_env_pairs); /* (sp += 1) */
2817 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */
2818 lua_pushboolean(L, 0); /* (sp += 1) */
2819 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2820 }
2821
2822 __attribute_cold__
2823 static void
magnet_req_item_metatable(lua_State * const L)2824 magnet_req_item_metatable (lua_State * const L)
2825 {
2826 if (luaL_newmetatable(L, "li.req_item") == 0) /* (sp += 1) */
2827 return;
2828
2829 lua_pushcfunction(L, magnet_req_item_get); /* (sp += 1) */
2830 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2831 lua_pushcfunction(L, magnet_req_item_set); /* (sp += 1) */
2832 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2833 lua_pushboolean(L, 0); /* (sp += 1) */
2834 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2835 }
2836
2837 __attribute_cold__
2838 static void
magnet_req_env_metatable(lua_State * const L)2839 magnet_req_env_metatable (lua_State * const L)
2840 {
2841 if (luaL_newmetatable(L, "li.req_env") == 0) /* (sp += 1) */
2842 return;
2843
2844 lua_pushcfunction(L, magnet_envvar_get); /* (sp += 1) */
2845 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2846 lua_pushcfunction(L, magnet_envvar_set); /* (sp += 1) */
2847 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2848 lua_pushcfunction(L, magnet_envvar_pairs); /* (sp += 1) */
2849 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */
2850 lua_pushboolean(L, 0); /* (sp += 1) */
2851 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2852 }
2853
2854 __attribute_cold__
2855 static void
magnet_resp_header_metatable(lua_State * const L)2856 magnet_resp_header_metatable (lua_State * const L)
2857 {
2858 if (luaL_newmetatable(L, "li.resp_header") == 0) /* (sp += 1) */
2859 return;
2860
2861 lua_pushcfunction(L, magnet_resphdr_get); /* (sp += 1) */
2862 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2863 lua_pushcfunction(L, magnet_resphdr_set); /* (sp += 1) */
2864 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2865 lua_pushcfunction(L, magnet_resphdr_pairs); /* (sp += 1) */
2866 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */
2867 lua_pushboolean(L, 0); /* (sp += 1) */
2868 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2869 }
2870
2871 __attribute_cold__
2872 static void
magnet_resp_body_metatable(lua_State * const L)2873 magnet_resp_body_metatable (lua_State * const L)
2874 {
2875 if (luaL_newmetatable(L, "li.resp_body") == 0) /* (sp += 1) */
2876 return;
2877
2878 lua_pushcfunction(L, magnet_respbody); /* (sp += 1) */
2879 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2880 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */
2881 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2882 lua_pushboolean(L, 0); /* (sp += 1) */
2883 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2884 }
2885
2886 __attribute_cold__
2887 static void
magnet_req_body_metatable(lua_State * const L)2888 magnet_req_body_metatable (lua_State * const L)
2889 {
2890 if (luaL_newmetatable(L, "li.req_body") == 0) /* (sp += 1) */
2891 return;
2892
2893 lua_pushcfunction(L, magnet_reqbody); /* (sp += 1) */
2894 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2895 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */
2896 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2897 lua_pushboolean(L, 0); /* (sp += 1) */
2898 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2899 }
2900
2901 __attribute_cold__
magnet_atpanic(lua_State * L)2902 static int magnet_atpanic(lua_State *L) {
2903 request_st * const r = magnet_get_request(L);
2904 log_error(r->conf.errh, __FILE__, __LINE__, "(lua-atpanic) %s",
2905 lua_isstring(L, 1) ? lua_tostring(L, 1) : "");
2906 /*longjmp(exceptionjmp, 1);*//*(must be init with setjmp() elsewhere)*/
2907 return 0;
2908 }
2909
2910 __attribute_cold__
magnet_print(lua_State * L)2911 static int magnet_print(lua_State *L) {
2912 const_buffer cb = magnet_checkconstbuffer(L, 1);
2913 request_st * const r = magnet_get_request(L);
2914 log_error(r->conf.errh, __FILE__, __LINE__, "(lua-print) %s", cb.ptr);
2915 return 0;
2916 }
2917
2918 __attribute_cold__
2919 static void
magnet_plugin_stats_table(lua_State * const L)2920 magnet_plugin_stats_table (lua_State * const L)
2921 {
2922 lua_createtable(L, 0, 0); /* {} (sp += 1) */
2923 lua_createtable(L, 0, 4); /* metatable for plugin_stats (sp += 1) */
2924 lua_pushcfunction(L, magnet_plugin_stats_get); /* (sp += 1) */
2925 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2926 lua_pushcfunction(L, magnet_plugin_stats_set); /* (sp += 1) */
2927 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2928 lua_pushcfunction(L, magnet_plugin_stats_pairs); /* (sp += 1) */
2929 lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */
2930 lua_pushboolean(L, 0); /* (sp += 1) */
2931 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2932 lua_setmetatable(L, -2); /* (sp -= 1) */
2933 }
2934
2935 __attribute_cold__
2936 static void
magnet_server_stats_table(lua_State * const L)2937 magnet_server_stats_table (lua_State * const L)
2938 {
2939 lua_createtable(L, 0, 0); /* {} (sp += 1) */
2940 lua_createtable(L, 0, 3); /* metatable for stats (sp += 1) */
2941 lua_pushcfunction(L, magnet_server_stats_get); /* (sp += 1) */
2942 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
2943 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */
2944 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2945 lua_pushboolean(L, 0); /* (sp += 1) */
2946 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2947 lua_setmetatable(L, -2); /* (sp -= 1) */
2948 }
2949
2950 __attribute_cold__
2951 static void
magnet_server_table(lua_State * const L)2952 magnet_server_table (lua_State * const L)
2953 {
2954 lua_createtable(L, 0, 3); /* {} (sp += 1) */
2955 lua_pushcfunction(L, magnet_irequests); /* (sp += 1) */
2956 lua_setfield(L, -2, "irequests"); /* iterate over requests (sp -= 1) */
2957 magnet_plugin_stats_table(L); /* (sp += 1) */
2958 lua_setfield(L, -2, "plugin_stats"); /* (sp -= 1) */
2959 magnet_server_stats_table(L); /* (sp += 1) */
2960 lua_setfield(L, -2, "stats"); /* (sp -= 1) */
2961 lua_createtable(L, 0, 2); /* metatable for server table (sp += 1) */
2962 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */
2963 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
2964 lua_pushboolean(L, 0); /* (sp += 1) */
2965 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
2966 lua_setmetatable(L, -2); /* tie the metatable to server (sp -= 1) */
2967 }
2968
2969 __attribute_cold__
2970 static void
magnet_script_setup_global_state(lua_State * const L)2971 magnet_script_setup_global_state (lua_State * const L)
2972 {
2973 lua_atpanic(L, magnet_atpanic);
2974
2975 lua_pushglobaltable(L); /* (sp += 1) */
2976 /* override default print() function */
2977 lua_pushcfunction(L, magnet_print); /* (sp += 1) */
2978 lua_setfield(L, -2, "print"); /* (sp -= 1) */
2979 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
2980 /* override default pairs() function to our __pairs capable version */
2981 lua_getglobal(L, "pairs"); /* push original pairs() (sp += 1) */
2982 lua_pushcclosure(L, magnet_pairs, 1); /* (sp -= 1; sp += 1) */
2983 lua_setfield(L, -2, "pairs"); /* (sp -= 1) */
2984 #endif
2985 lua_pop(L, 1); /* pop global table */ /* (sp -= 1) */
2986
2987 magnet_req_header_metatable(L); /* init for mem locality (sp += 1) */
2988 magnet_req_attr_metatable(L); /* init for mem locality (sp += 1) */
2989 magnet_req_item_metatable(L); /* init for mem locality (sp += 1) */
2990 magnet_req_env_metatable(L); /* init for mem locality (sp += 1) */
2991 magnet_resp_header_metatable(L); /* init for mem locality (sp += 1) */
2992 magnet_resp_body_metatable(L); /* init for mem locality (sp += 1) */
2993 magnet_req_body_metatable(L); /* init for mem locality (sp += 1) */
2994 magnet_stat_metatable(L); /* init table for mem locality (sp += 1) */
2995 magnet_readdir_metatable(L); /* init table for mem locality (sp += 1) */
2996 lua_pop(L, 9); /* pop init'd metatables (sp -= 9) */
2997 }
2998
2999 __attribute_cold__
3000 static void
magnet_init_lighty_table(lua_State * const L,request_st ** rr,const int result_ndx)3001 magnet_init_lighty_table (lua_State * const L, request_st **rr,
3002 const int result_ndx)
3003 {
3004 /* lighty table
3005 *
3006 * lighty.r.* HTTP request object methods
3007 * lighty.c.* lighttpd C methods callable from lua
3008 * lighty.server.* lighttpd server object methods
3009 *
3010 * (older interface)
3011 *
3012 * lighty.request[] HTTP request headers
3013 * lighty.req_env[] environment variables
3014 * lighty.env[] lighttpd request metadata,
3015 * various url components,
3016 * physical file paths;
3017 * might contain nil values
3018 *
3019 * lighty.header[] (script) HTTP response headers
3020 * lighty.content[] (script) HTTP response body (table of string/file)
3021 *
3022 * lighty.status[] lighttpd status counters
3023 */
3024
3025 /*(adjust the preallocation if more entries are added)*/
3026 lua_createtable(L, 0, 9); /* lighty.* (returned on stack) (sp += 1) */
3027
3028 magnet_request_table(L, rr); /* lighty.r (sp += 1) */
3029 lua_setfield(L, -2, "r"); /* lighty.r = {} (sp -= 1) */
3030
3031 magnet_server_table(L); /* lighty.server (sp += 1) */
3032 lua_setfield(L, -2, "server"); /* server = {} (sp -= 1) */
3033
3034 /* compatibility with previous mod_magnet interfaces in top of lighty.* */
3035 lua_getfield(L, -1, "r"); /* (sp += 1) */
3036 /* alias lighty.request -> lighty.r.req_header */
3037 lua_getfield(L, -1, "req_header"); /* (sp += 1) */
3038 lua_setfield(L, -3, "request"); /* request = {} (sp -= 1) */
3039 /* alias lighty.env -> lighty.r.req_attr */
3040 lua_getfield(L, -1, "req_attr"); /* (sp += 1) */
3041 lua_setfield(L, -3, "env"); /* env = {} (sp -= 1) */
3042 /* alias lighty.req_env -> lighty.r.req_env */
3043 lua_getfield(L, -1, "req_env"); /* (sp += 1) */
3044 lua_setfield(L, -3, "req_env"); /* req_env = {} (sp -= 1) */
3045 lua_pop(L, 1); /* (sp -= 1) */
3046
3047 /* alias lighty.server.stats -> lighty.status */
3048 lua_getfield(L, -1, "server"); /* (sp += 1) */
3049 lua_getfield(L, -1, "plugin_stats"); /* (sp += 1) */
3050 lua_setfield(L, -3, "status"); /* status = {} (sp -= 1) */
3051 lua_pop(L, 1); /* (sp -= 1) */
3052
3053 lua_pushinteger(L, MAGNET_RESTART_REQUEST);
3054 lua_setfield(L, -2, "RESTART_REQUEST");
3055
3056 /* alias lighty.c.stat -> lighty.stat */
3057 lua_pushcfunction(L, magnet_stat); /* (sp += 1) */
3058 lua_setfield(L, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */
3059
3060 static const luaL_Reg cmethods[] = {
3061 { "stat", magnet_stat }
3062 ,{ "time", magnet_time }
3063 ,{ "hrtime", magnet_hrtime }
3064 ,{ "rand", magnet_rand }
3065 ,{ "md", magnet_md_once } /* message digest */
3066 ,{ "hmac", magnet_hmac_once } /* HMAC */
3067 ,{ "digest_eq", magnet_digest_eq } /* timing-safe eq fixed len */
3068 ,{ "secret_eq", magnet_secret_eq } /* timing-safe eq variable len */
3069 ,{ "b64urldec", magnet_b64urldec } /* validate and decode base64url */
3070 ,{ "b64urlenc", magnet_b64urlenc } /* base64url encode, no padding */
3071 ,{ "b64dec", magnet_b64stddec } /* validate and decode base64 */
3072 ,{ "b64enc", magnet_b64stdenc } /* base64 encode, no padding */
3073 ,{ "hexdec", magnet_hexdec } /* validate and decode hex str */
3074 ,{ "hexenc", magnet_hexenc } /* uc; lc w/ lua s = s:lower() */
3075 ,{ "xmlenc", magnet_xmlenc } /* xml-encode/html-encode: <>&'\" */
3076 ,{ "urldec", magnet_urldec } /* url-decode (path) */
3077 ,{ "urlenc", magnet_urlenc } /* url-encode (path) */
3078 ,{ "urldec_query", magnet_urldec_query } /* url-decode query-string */
3079 ,{ "urlenc_query", magnet_urlenc_query } /* url-encode query-string */
3080 ,{ "urlenc_normalize", magnet_urlenc_normalize }/* url-enc normalization */
3081 ,{ "fspath_simplify", magnet_fspath_simplify } /* simplify fspath */
3082 ,{ "cookie_tokens", magnet_cookie_tokens } /* parse cookie tokens */
3083 ,{ "header_tokens", magnet_header_tokens } /* parse header tokens seq */
3084 ,{ "readdir", magnet_readdir } /* dir walk */
3085 ,{ "quoteddec", magnet_quoteddec } /* quoted-string decode */
3086 ,{ "quotedenc", magnet_quotedenc } /* quoted-string encode */
3087 ,{ "bsdec", magnet_bsdec } /* backspace-escape decode */
3088 ,{ "bsenc", magnet_bsenc_default } /* backspace-escape encode */
3089 ,{ "bsenc_json", magnet_bsenc_json } /* backspace-escape encode json */
3090 ,{ NULL, NULL }
3091 };
3092
3093 lua_createtable(L, 0, sizeof(cmethods)/sizeof(luaL_Reg)-1);/*(sp += 1) */
3094 luaL_setfuncs(L, cmethods, 0);
3095 lua_createtable(L, 0, 2); /* metatable for c table (sp += 1) */
3096 lua_pushcfunction(L, magnet_newindex_readonly); /* (sp += 1) */
3097 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
3098 lua_pushboolean(L, 0); /* (sp += 1) */
3099 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
3100 lua_setmetatable(L, -2); /* tie the metatable to c (sp -= 1) */
3101 lua_setfield(L, -2, "c"); /* c = {} (sp -= 1) */
3102
3103 /* lighty.* table is read-only;
3104 * provide alternative scratch table for legacy API, historical (mis)use */
3105 lua_createtable(L, 0, 3); /* metatable for lighty table (sp += 1) */
3106 lua_pushvalue(L, result_ndx); /* (sp += 1) */
3107 lua_pushcclosure(L, magnet_lighty_result_get, 1);/* (sp -= 1; sp += 1) */
3108 lua_setfield(L, -2, "__index"); /* (sp -= 1) */
3109 lua_pushvalue(L, result_ndx); /* (sp += 1) */
3110 lua_pushcclosure(L, magnet_lighty_result_set, 1);/* (sp -= 1; sp += 1) */
3111 lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */
3112 lua_pushboolean(L, 0); /* (sp += 1) */
3113 lua_setfield(L, -2, "__metatable"); /* protect metatable (sp -= 1) */
3114 lua_setmetatable(L, -2); /* tie the metatable to lighty (sp -= 1) */
3115
3116 /* lighty table (returned on stack) */
3117 }
3118
magnet_clear_table(lua_State * const L,int ndx)3119 static void magnet_clear_table(lua_State * const L, int ndx) {
3120 /*(avoid lua_absindex() func call since expecting empty result tables for
3121 * legacy interfaces to lighty.header and .content, though not script-env)*/
3122 /*ndx = lua_absindex(ndx);*//*(lua 5.2+)*/
3123 if (ndx < 0) --ndx;
3124 for (lua_pushnil(L); lua_next(L, ndx); ) {
3125 lua_pop(L, 1);
3126 lua_pushvalue(L, -1);
3127 lua_pushnil(L);
3128 lua_rawset(L, ndx < 0 ? ndx - 2 : ndx);
3129 }
3130 }
3131
3132 __attribute_cold__
magnet_traceback(lua_State * L)3133 static int magnet_traceback(lua_State *L) {
3134 if (!lua_isstring(L, 1)) /* 'message' not a string? */
3135 return 1; /* keep it intact */
3136 if (lua_getglobal_and_type(L, "debug") != LUA_TTABLE) {
3137 lua_pop(L, 1);
3138 return 1;
3139 }
3140 if (lua_getfield_and_type(L, -1, "traceback") != LUA_TFUNCTION) {
3141 lua_pop(L, 2);
3142 return 1;
3143 }
3144 lua_pushvalue(L, 1); /* pass error message */
3145 lua_pushinteger(L, 2); /* skip this function and traceback */
3146 lua_call(L, 2, 1); /* call debug.traceback */
3147 return 1;
3148 }
3149
3150 __attribute_cold__
3151 __attribute_noinline__
3152 static int
magnet_script_setup(request_st * const r,plugin_data * const p,script * const sc)3153 magnet_script_setup (request_st * const r, plugin_data * const p, script * const sc)
3154 {
3155 lua_State * const L = sc->L;
3156 const int func_ndx = 1;
3157 if (lua_isfunction(L, func_ndx)) {
3158 /* initial setup for global lua_State */
3159 magnet_script_setup_global_state(L);
3160 /*force_assert(lua_gettop(L) == 1);*/ /* errfunc_ndx = 2 */
3161 lua_pushcfunction(L, magnet_traceback);/*errfunc*//* (sp += 1) */
3162 /* create empty table for script environment (reused)
3163 * setmetatable({}, {__index = _G})
3164 * if a function symbol is not defined in our env,
3165 * __index will lookup in the global env. */
3166 lua_createtable(L, 0, 1); /* env_ndx = 3 */ /* (sp += 1) */
3167 magnet_mainenv_metatable(L); /* (sp += 1) */
3168 lua_setmetatable(L, -2); /* (sp -= 1) */
3169 /* set script env in first upvalue (_ENV upvalue) for func */
3170 lua_pushvalue(L, -1); /* (sp += 1) */
3171 magnet_setfenv_mainfn(L, func_ndx); /* (sp -= 1) */
3172 /* result table (modifiable) (for mod_magnet legacy API) */
3173 /* (prefer lighty.r.resp_header(), lighty.r.resp_body()) */
3174 lua_createtable(L, 0, 2); /* (result_ndx = 4) */ /* (sp += 1) */
3175 const int result_ndx = 4;
3176 /* shared userdata (ud_ndx = 5) */
3177 request_st ** const r_userdata = /* (sp += 1) */
3178 (request_st **)lua_newuserdata0(L, sizeof(request_st *));
3179 /* insert lighty table (lighty_table_ndx = 6) */
3180 magnet_init_lighty_table(L,r_userdata,result_ndx);/* (sp += 1) */
3181 return 1;
3182 }
3183 else {
3184 if (lua_isstring(L, func_ndx))
3185 log_error(r->conf.errh, __FILE__, __LINE__,
3186 "loading script %s failed: %s", sc->name.ptr,
3187 lua_tostring(L, func_ndx));
3188 else /*(lua_gettop(L) == 0)*/
3189 log_perror(r->conf.errh, __FILE__, __LINE__,
3190 "loading script %s failed", sc->name.ptr);
3191 lua_settop(L, 0);
3192
3193 if (p->conf.stage >= 0) { /*(before response_start)*/
3194 r->http_status = 500;
3195 r->handler_module = NULL;
3196 }
3197
3198 return 0;
3199 }
3200 }
3201
3202 static handler_t
magnet_attract(request_st * const r,plugin_data * const p,script * const sc)3203 magnet_attract (request_st * const r, plugin_data * const p, script * const sc)
3204 {
3205 lua_State * const L = sc->L;
3206 const int func_ndx = 1;
3207 const int errfunc_ndx = 2;
3208 const int env_ndx = 3;
3209 const int result_ndx = 4;
3210 const int ud_ndx = 5;
3211 const int lighty_table_ndx = 6;
3212
3213 if (__builtin_expect( (lua_gettop(L) != lighty_table_ndx), 0)) {
3214 if (!magnet_script_setup(r, p, sc))
3215 return HANDLER_FINISHED;
3216 }
3217
3218 /* set r in global state for L for out-of-band access to r
3219 * (r-conf.errh and others) */
3220 magnet_set_request(L, r);
3221 /* set r in userdata shared by lighty.r object methods */
3222 *(request_st **)lua_touserdata(L, ud_ndx) = r;
3223
3224 /* add lighty table to script-env
3225 * script-env is cleared at the end of each script run */
3226 lua_pushvalue(L, lighty_table_ndx); /* (sp += 1) */
3227 lua_setfield(L, env_ndx, "lighty"); /* lighty.* (sp -= 1) */
3228
3229 /* push script func; pcall will consume the func value */
3230 lua_pushvalue(L, func_ndx); /* (sp += 1) */
3231 int ret = lua_pcall(L, 0, 1, errfunc_ndx); /* (sp -= 1; sp += 1) */
3232
3233 handler_t result = HANDLER_GO_ON;
3234 if (0 != ret) {
3235 size_t errlen;
3236 const char * const err = lua_tolstring(L, -1, &errlen);
3237 log_error_multiline(r->conf.errh, __FILE__, __LINE__,
3238 err, errlen, "lua: ");
3239 /*lua_pop(L, 1);*/ /* pop error msg */ /* defer to later */
3240 if (p->conf.stage >= 0) { /*(before response_start)*/
3241 r->http_status = 500;
3242 r->handler_module = NULL;
3243 result = HANDLER_FINISHED;
3244 }
3245 }
3246 else do {
3247 /*(luaL_optinteger might raise error, which we want to avoid)*/
3248 /*lua_return_value = (int) luaL_optinteger(L, -1, -1);*/
3249 int isnum = 1;
3250 int lua_return_value = lua_isnil(L, -1)
3251 ? 0
3252 : (int) lua_tointegerx(L, -1, &isnum);
3253 if (!isnum) {
3254 log_error(r->conf.errh, __FILE__, __LINE__,
3255 "lua_pcall(): unexpected non-integer return type: %s",
3256 luaL_typename(L, -1));
3257 break;
3258 }
3259 /*lua_pop(L, 1);*/ /* pop return value */ /* defer to later */
3260 /*force_assert(lua_istable(sc->L, -1));*/
3261
3262 if (lua_getfield_and_type(L, result_ndx, "header") == LUA_TTABLE) {
3263 magnet_copy_response_header(L, r); /* deprecated legacy API */
3264 }
3265 /*lua_pop(L, 1);*//* defer to later */
3266
3267 if (lua_return_value >= 200) {
3268 r->http_status = lua_return_value;
3269 /*(note: body may already have been set via lighty.r.resp_body.*)*/
3270 if (lua_getfield_and_type(L, result_ndx, "content") == LUA_TTABLE) {
3271 magnet_attach_content(L, r); /* deprecated legacy API */
3272 }
3273 /*lua_pop(L, 1);*//* defer to later */
3274 if (!chunkqueue_is_empty(&r->write_queue)) {
3275 r->handler_module = p->self;
3276 }
3277 r->resp_body_finished = 1;
3278 result = HANDLER_FINISHED;
3279 } else if (lua_return_value >= 100) {
3280 /*(skip for response-start; send response as-is w/ added headers)*/
3281 if (p->conf.stage < 0) break;
3282 /*(custom lua code should not return 101 Switching Protocols)*/
3283 r->http_status = lua_return_value;
3284 result = http_response_send_1xx(r)
3285 ? HANDLER_GO_ON
3286 : HANDLER_ERROR;
3287 } else if (MAGNET_RESTART_REQUEST == lua_return_value) {
3288 /*(could detect restart loops in same way as is done in mod_rewrite,
3289 * but using r->env means that we do not need to reset plugin state
3290 * at end of every request, as is done in mod_rewrite. mod_rewrite
3291 * always restarts the request processing (if request is rewritten),
3292 * whereas mod_magnet can be used in many other ways)*/
3293 buffer *vb =
3294 http_header_env_get(r, CONST_STR_LEN("_L_MAGNET_RESTART"));
3295 if (NULL == vb) {
3296 vb =
3297 http_header_env_set_ptr(r,CONST_STR_LEN("_L_MAGNET_RESTART"));
3298 buffer_append_char(vb, '0');
3299 }
3300 result = HANDLER_COMEBACK;
3301 if (++*vb->ptr-'0' >= 10) {
3302 log_error(r->conf.errh, __FILE__, __LINE__,
3303 "too many request restarts (infinite loop?) for %s",
3304 sc->name.ptr);
3305 result = HANDLER_ERROR;
3306 }
3307 }
3308 } while (0);
3309 magnet_clear_table(L, env_ndx);
3310 magnet_clear_table(L, result_ndx);
3311 /* reset stack to reuse stack up to lighty table; pop the excess */
3312 lua_settop(L, lighty_table_ndx); /*(handle deferred lua_pop()s)*/
3313 return result;
3314 }
3315
magnet_attract_array(request_st * const r,plugin_data * const p,int stage)3316 static handler_t magnet_attract_array(request_st * const r, plugin_data * const p, int stage) {
3317 mod_magnet_patch_config(r, p);
3318 p->conf.stage = stage;
3319
3320 script * const *scripts;
3321 switch (stage) {
3322 case 1: scripts = p->conf.url_raw; break;
3323 case 0: scripts = p->conf.physical_path; break;
3324 case -1: scripts = p->conf.response_start; break;
3325 default: scripts = NULL; break;
3326 }
3327 if (NULL == scripts) return HANDLER_GO_ON; /* no scripts set */
3328
3329 /*(always check at least mtime and size to trigger script reload)*/
3330 const int etag_flags = r->conf.etag_flags | ETAG_USE_MTIME | ETAG_USE_SIZE;
3331 int req_env_inited = 0;
3332
3333 /* execute scripts sequentially while HANDLER_GO_ON */
3334 handler_t rc = HANDLER_GO_ON;
3335 do {
3336 script_cache_check_script(*scripts, etag_flags);
3337 if ((*scripts)->req_env_init && !req_env_inited) {
3338 /*(request env init is deferred until needed)*/
3339 req_env_inited = 1;
3340 r->con->srv->request_env(r);
3341 }
3342 rc = magnet_attract(r, p, *scripts);
3343 } while (rc == HANDLER_GO_ON && *++scripts);
3344
3345 if (r->error_handler_saved_status) {
3346 /* retrieve (possibly modified) REDIRECT_STATUS and store as number */
3347 int x;
3348 const buffer * const vb = http_header_env_get(r, CONST_STR_LEN("REDIRECT_STATUS"));
3349 if (vb && (x = http_header_str_to_code(vb->ptr)) != -1)
3350 r->error_handler_saved_status =
3351 r->error_handler_saved_status > 0 ? (int)x : -(int)x;
3352 }
3353
3354 return rc;
3355 }
3356
URIHANDLER_FUNC(mod_magnet_uri_handler)3357 URIHANDLER_FUNC(mod_magnet_uri_handler) {
3358 return magnet_attract_array(r, p_d, 1);
3359 }
3360
URIHANDLER_FUNC(mod_magnet_physical)3361 URIHANDLER_FUNC(mod_magnet_physical) {
3362 return magnet_attract_array(r, p_d, 0);
3363 }
3364
URIHANDLER_FUNC(mod_magnet_response_start)3365 URIHANDLER_FUNC(mod_magnet_response_start) {
3366 return magnet_attract_array(r, p_d, -1);
3367 }
3368
SUBREQUEST_FUNC(mod_magnet_handle_subrequest)3369 SUBREQUEST_FUNC(mod_magnet_handle_subrequest) {
3370 /* read entire request body from network and then restart request */
3371 UNUSED(p_d);
3372
3373 if (r->state == CON_STATE_READ_POST) {
3374 /*(streaming flags were removed when magnet installed this handler)*/
3375 handler_t rc = r->con->reqbody_read(r);
3376 if (rc != HANDLER_GO_ON) return rc;
3377 if (r->state == CON_STATE_READ_POST)
3378 return HANDLER_WAIT_FOR_EVENT;
3379 }
3380
3381 buffer_clear(&r->physical.path);
3382 r->handler_module = NULL;
3383 return HANDLER_COMEBACK;
3384 }
3385
3386
3387 __attribute_cold__
3388 int mod_magnet_plugin_init(plugin *p);
mod_magnet_plugin_init(plugin * p)3389 int mod_magnet_plugin_init(plugin *p) {
3390 p->version = LIGHTTPD_VERSION_ID;
3391 p->name = "magnet";
3392
3393 p->init = mod_magnet_init;
3394 p->handle_uri_clean = mod_magnet_uri_handler;
3395 p->handle_physical = mod_magnet_physical;
3396 p->handle_response_start = mod_magnet_response_start;
3397 p->handle_subrequest = mod_magnet_handle_subrequest;
3398 p->set_defaults = mod_magnet_set_defaults;
3399 p->cleanup = mod_magnet_free;
3400
3401 return 0;
3402 }
3403