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