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