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