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