1 #include "first.h" 2 3 #include "base.h" 4 #include "stat_cache.h" 5 #include "http_kv.h" 6 #include "fdlog.h" 7 #include "log.h" 8 #include "response.h" 9 #include "http_cgi.h" 10 #include "http_chunk.h" 11 #include "http_header.h" 12 13 #include "plugin.h" 14 15 #include <sys/types.h> 16 #include "sys-socket.h" 17 #ifdef HAVE_SYS_WAIT_H 18 #include <sys/wait.h> 19 #endif 20 21 #include <unistd.h> 22 #include <errno.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <fdevent.h> 26 27 #include <fcntl.h> 28 #include <signal.h> 29 30 typedef struct { 31 uintptr_t *offsets; 32 size_t osize; 33 size_t oused; 34 buffer *b; 35 buffer *boffsets; 36 buffer *ld_preload; 37 buffer *ld_library_path; 38 #ifdef __CYGWIN__ 39 buffer *systemroot; 40 #endif 41 } env_accum; 42 43 typedef struct { 44 unix_time64_t read_timeout; 45 unix_time64_t write_timeout; 46 int signal_fin; 47 } cgi_limits; 48 49 typedef struct { 50 const array *cgi; 51 const cgi_limits *limits; 52 unsigned short execute_x_only; 53 unsigned short local_redir; 54 unsigned short xsendfile_allow; 55 unsigned short upgrade; 56 const array *xsendfile_docroot; 57 } plugin_config; 58 59 struct cgi_pid_t; 60 61 typedef struct { 62 PLUGIN_DATA; 63 plugin_config defaults; 64 plugin_config conf; 65 int tempfile_accum; 66 struct cgi_pid_t *cgi_pid; 67 env_accum env; 68 } plugin_data; 69 70 typedef struct { 71 struct cgi_pid_t *cgi_pid; 72 int fd; 73 int fdtocgi; 74 int rd_revents; 75 int wr_revents; 76 fdnode *fdn; 77 fdnode *fdntocgi; 78 79 request_st *r; 80 connection *con; /* dumb pointer */ 81 struct fdevents *ev; /* dumb pointer */ 82 plugin_data *plugin_data; /* dumb pointer */ 83 84 buffer *response; 85 unix_time64_t read_ts; 86 unix_time64_t write_ts; 87 buffer *cgi_handler; /* dumb pointer */ 88 http_response_opts opts; 89 plugin_config conf; 90 off_t orig_reqbody_length; 91 } handler_ctx; 92 93 typedef struct cgi_pid_t { 94 pid_t pid; 95 int signal_sent; 96 handler_ctx *hctx; 97 struct cgi_pid_t *next; 98 struct cgi_pid_t *prev; 99 } cgi_pid_t; 100 101 static handler_ctx * cgi_handler_ctx_init(void) { 102 handler_ctx *hctx = ck_calloc(1, sizeof(*hctx)); 103 hctx->response = chunk_buffer_acquire(); 104 hctx->fd = -1; 105 hctx->fdtocgi = -1; 106 return hctx; 107 } 108 109 static void cgi_handler_ctx_free(handler_ctx *hctx) { 110 chunk_buffer_release(hctx->response); 111 free(hctx); 112 } 113 114 INIT_FUNC(mod_cgi_init) { 115 plugin_data * const p = ck_calloc(1, sizeof(*p)); 116 const char *s; 117 118 /* for valgrind */ 119 s = getenv("LD_PRELOAD"); 120 if (s) buffer_copy_string((p->env.ld_preload = buffer_init()), s); 121 s = getenv("LD_LIBRARY_PATH"); 122 if (s) buffer_copy_string((p->env.ld_library_path = buffer_init()), s); 123 #ifdef __CYGWIN__ 124 /* CYGWIN needs SYSTEMROOT */ 125 s = getenv("SYSTEMROOT"); 126 if (s) buffer_copy_string((p->env.systemroot = buffer_init()), s); 127 #endif 128 129 return p; 130 } 131 132 133 FREE_FUNC(mod_cgi_free) { 134 plugin_data *p = p_d; 135 buffer_free(p->env.ld_preload); 136 buffer_free(p->env.ld_library_path); 137 #ifdef __CYGWIN__ 138 buffer_free(p->env.systemroot); 139 #endif 140 141 for (cgi_pid_t *cgi_pid = p->cgi_pid, *next; cgi_pid; cgi_pid = next) { 142 next = cgi_pid->next; 143 free(cgi_pid); 144 } 145 146 if (NULL == p->cvlist) return; 147 /* (init i to 0 if global context; to 1 to skip empty global context) */ 148 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 149 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 150 for (; -1 != cpv->k_id; ++cpv) { 151 if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 152 switch (cpv->k_id) { 153 case 6: /* cgi.limits */ 154 free(cpv->v.v); 155 break; 156 default: 157 break; 158 } 159 } 160 } 161 } 162 163 static void mod_cgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 164 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 165 case 0: /* cgi.assign */ 166 pconf->cgi = cpv->v.a; 167 break; 168 case 1: /* cgi.execute-x-only */ 169 pconf->execute_x_only = (unsigned short)cpv->v.u; 170 break; 171 case 2: /* cgi.x-sendfile */ 172 pconf->xsendfile_allow = (unsigned short)cpv->v.u; 173 break; 174 case 3: /* cgi.x-sendfile-docroot */ 175 pconf->xsendfile_docroot = cpv->v.a; 176 break; 177 case 4: /* cgi.local-redir */ 178 pconf->local_redir = (unsigned short)cpv->v.u; 179 break; 180 case 5: /* cgi.upgrade */ 181 pconf->upgrade = (unsigned short)cpv->v.u; 182 break; 183 case 6: /* cgi.limits */ 184 if (cpv->vtype != T_CONFIG_LOCAL) break; 185 pconf->limits = cpv->v.v; 186 break; 187 default:/* should not happen */ 188 return; 189 } 190 } 191 192 static void mod_cgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 193 do { 194 mod_cgi_merge_config_cpv(pconf, cpv); 195 } while ((++cpv)->k_id != -1); 196 } 197 198 static void mod_cgi_patch_config(request_st * const r, plugin_data * const p) { 199 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 200 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 201 for (int i = 1, used = p->nconfig; i < used; ++i) { 202 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 203 mod_cgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 204 } 205 } 206 207 __attribute_cold__ 208 __attribute_pure__ 209 static int mod_cgi_str_to_signal (const char *s, int default_sig) { 210 static const struct { const char *name; int sig; } sigs[] = { 211 { "HUP", SIGHUP } 212 ,{ "INT", SIGINT } 213 ,{ "QUIT", SIGQUIT } 214 ,{ "ILL", SIGILL } 215 ,{ "TRAP", SIGTRAP } 216 ,{ "ABRT", SIGABRT } 217 #ifdef SIGBUS 218 ,{ "BUS", SIGBUS } 219 #endif 220 ,{ "FPE", SIGFPE } 221 ,{ "KILL", SIGKILL } 222 #ifdef SIGUSR1 223 ,{ "USR1", SIGUSR1 } 224 #endif 225 ,{ "SEGV", SIGSEGV } 226 #ifdef SIGUSR2 227 ,{ "USR2", SIGUSR2 } 228 #endif 229 ,{ "PIPE", SIGPIPE } 230 ,{ "ALRM", SIGALRM } 231 ,{ "TERM", SIGTERM } 232 #ifdef SIGCHLD 233 ,{ "CHLD", SIGCHLD } 234 #endif 235 #ifdef SIGCONT 236 ,{ "CONT", SIGCONT } 237 #endif 238 #ifdef SIGURG 239 ,{ "URG", SIGURG } 240 #endif 241 #ifdef SIGXCPU 242 ,{ "XCPU", SIGXCPU } 243 #endif 244 #ifdef SIGXFSZ 245 ,{ "XFSZ", SIGXFSZ } 246 #endif 247 #ifdef SIGWINCH 248 ,{ "WINCH",SIGWINCH} 249 #endif 250 #ifdef SIGPOLL 251 ,{ "POLL", SIGPOLL } 252 #endif 253 #ifdef SIGIO 254 ,{ "IO", SIGIO } 255 #endif 256 }; 257 258 if (s[0] == 'S' && s[1] == 'I' && s[2] == 'G') s += 3; /*("SIG" prefix)*/ 259 for (uint32_t i = 0; i < sizeof(sigs)/sizeof(*sigs); ++i) { 260 if (0 == strcmp(s, sigs[i].name)) return sigs[i].sig; 261 } 262 return default_sig; 263 } 264 265 static cgi_limits * mod_cgi_parse_limits(const array * const a, log_error_st * const errh) { 266 cgi_limits * const limits = ck_calloc(1, sizeof(cgi_limits)); 267 for (uint32_t i = 0; i < a->used; ++i) { 268 const data_unset * const du = a->data[i]; 269 int32_t v = config_plugin_value_to_int32(du, -1); 270 if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("read-timeout"))) { 271 limits->read_timeout = (unix_time64_t)v; 272 continue; 273 } 274 if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("write-timeout"))) { 275 limits->write_timeout = (unix_time64_t)v; 276 continue; 277 } 278 if (buffer_eq_icase_slen(&du->key, CONST_STR_LEN("tcp-fin-propagate"))) { 279 if (-1 == v) { 280 v = SIGTERM; 281 if (du->type == TYPE_STRING) { 282 buffer * const vstr = &((data_string *)du)->value; 283 buffer_to_upper(vstr); 284 v = mod_cgi_str_to_signal(vstr->ptr, SIGTERM); 285 } 286 } 287 limits->signal_fin = v; 288 continue; 289 } 290 log_error(errh, __FILE__, __LINE__, 291 "unrecognized cgi.limits param: %s", du->key.ptr); 292 } 293 return limits; 294 } 295 296 SETDEFAULTS_FUNC(mod_cgi_set_defaults) { 297 static const config_plugin_keys_t cpk[] = { 298 { CONST_STR_LEN("cgi.assign"), 299 T_CONFIG_ARRAY_KVSTRING, 300 T_CONFIG_SCOPE_CONNECTION } 301 ,{ CONST_STR_LEN("cgi.execute-x-only"), 302 T_CONFIG_BOOL, 303 T_CONFIG_SCOPE_CONNECTION } 304 ,{ CONST_STR_LEN("cgi.x-sendfile"), 305 T_CONFIG_BOOL, 306 T_CONFIG_SCOPE_CONNECTION } 307 ,{ CONST_STR_LEN("cgi.x-sendfile-docroot"), 308 T_CONFIG_ARRAY_VLIST, 309 T_CONFIG_SCOPE_CONNECTION } 310 ,{ CONST_STR_LEN("cgi.local-redir"), 311 T_CONFIG_BOOL, 312 T_CONFIG_SCOPE_CONNECTION } 313 ,{ CONST_STR_LEN("cgi.upgrade"), 314 T_CONFIG_BOOL, 315 T_CONFIG_SCOPE_CONNECTION } 316 ,{ CONST_STR_LEN("cgi.limits"), 317 T_CONFIG_ARRAY_KVANY, 318 T_CONFIG_SCOPE_CONNECTION } 319 ,{ NULL, 0, 320 T_CONFIG_UNSET, 321 T_CONFIG_SCOPE_UNSET } 322 }; 323 324 plugin_data * const p = p_d; 325 if (!config_plugin_values_init(srv, p, cpk, "mod_cgi")) 326 return HANDLER_ERROR; 327 328 /* process and validate config directives 329 * (init i to 0 if global context; to 1 to skip empty global context) */ 330 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 331 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 332 for (; -1 != cpv->k_id; ++cpv) { 333 switch (cpv->k_id) { 334 case 0: /* cgi.assign */ 335 case 1: /* cgi.execute-x-only */ 336 case 2: /* cgi.x-sendfile */ 337 break; 338 case 3: /* cgi.x-sendfile-docroot */ 339 for (uint32_t j = 0; j < cpv->v.a->used; ++j) { 340 data_string *ds = (data_string *)cpv->v.a->data[j]; 341 if (ds->value.ptr[0] != '/') { 342 log_error(srv->errh, __FILE__, __LINE__, 343 "%s paths must begin with '/'; invalid: \"%s\"", 344 cpk[cpv->k_id].k, ds->value.ptr); 345 return HANDLER_ERROR; 346 } 347 buffer_path_simplify(&ds->value); 348 buffer_append_slash(&ds->value); 349 } 350 break; 351 case 4: /* cgi.local-redir */ 352 case 5: /* cgi.upgrade */ 353 break; 354 case 6: /* cgi.limits */ 355 cpv->v.v = mod_cgi_parse_limits(cpv->v.a, srv->errh); 356 if (NULL == cpv->v.v) return HANDLER_ERROR; 357 cpv->vtype = T_CONFIG_LOCAL; 358 break; 359 default:/* should not happen */ 360 break; 361 } 362 } 363 } 364 365 /* initialize p->defaults from global config context */ 366 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 367 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 368 if (-1 != cpv->k_id) 369 mod_cgi_merge_config(&p->defaults, cpv); 370 } 371 372 p->tempfile_accum = config_feature_bool(srv, "cgi.tempfile-accum", 1); 373 374 return HANDLER_GO_ON; 375 } 376 377 378 static cgi_pid_t * cgi_pid_add(plugin_data *p, pid_t pid, handler_ctx *hctx) { 379 cgi_pid_t *cgi_pid = ck_malloc(sizeof(cgi_pid_t)); 380 cgi_pid->pid = pid; 381 cgi_pid->signal_sent = 0; 382 cgi_pid->hctx = hctx; 383 cgi_pid->prev = NULL; 384 cgi_pid->next = p->cgi_pid; 385 p->cgi_pid = cgi_pid; 386 return cgi_pid; 387 } 388 389 static void cgi_pid_kill(cgi_pid_t *cgi_pid, int sig) { 390 cgi_pid->signal_sent = sig; /*(save last signal sent)*/ 391 kill(cgi_pid->pid, sig); 392 } 393 394 static void cgi_pid_del(plugin_data *p, cgi_pid_t *cgi_pid) { 395 if (cgi_pid->prev) 396 cgi_pid->prev->next = cgi_pid->next; 397 else 398 p->cgi_pid = cgi_pid->next; 399 400 if (cgi_pid->next) 401 cgi_pid->next->prev = cgi_pid->prev; 402 403 free(cgi_pid); 404 } 405 406 407 static void cgi_connection_close_fdtocgi(handler_ctx *hctx) { 408 /*(closes only hctx->fdtocgi)*/ 409 if (-1 == hctx->fdtocgi) return; 410 struct fdevents * const ev = hctx->ev; 411 fdevent_fdnode_event_del(ev, hctx->fdntocgi); 412 /*fdevent_unregister(ev, hctx->fdtocgi);*//*(handled below)*/ 413 fdevent_sched_close(ev, hctx->fdtocgi, 0); 414 hctx->fdntocgi = NULL; 415 hctx->fdtocgi = -1; 416 } 417 418 static void cgi_connection_close(handler_ctx *hctx) { 419 /* the connection to the browser went away, but we still have a connection 420 * to the CGI script 421 * 422 * close cgi-connection 423 */ 424 425 if (hctx->fd != -1) { 426 struct fdevents * const ev = hctx->ev; 427 /* close connection to the cgi-script */ 428 fdevent_fdnode_event_del(ev, hctx->fdn); 429 /*fdevent_unregister(ev, hctx->fd);*//*(handled below)*/ 430 fdevent_sched_close(ev, hctx->fd, 0); 431 hctx->fdn = NULL; 432 } 433 434 if (hctx->fdtocgi != -1) { 435 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 436 } 437 438 plugin_data * const p = hctx->plugin_data; 439 request_st * const r = hctx->r; 440 r->plugin_ctx[p->id] = NULL; 441 442 if (hctx->cgi_pid) { 443 cgi_pid_kill(hctx->cgi_pid, SIGTERM); 444 hctx->cgi_pid->hctx = NULL; 445 } 446 cgi_handler_ctx_free(hctx); 447 448 /* finish response (if not already r->resp_body_started, r->resp_body_finished) */ 449 if (r->handler_module == p->self) { 450 http_response_backend_done(r); 451 } 452 } 453 454 static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) { 455 handler_ctx *hctx = r->plugin_ctx[((plugin_data *)p_d)->id]; 456 if (hctx) { 457 chunkqueue_set_tempdirs(&r->reqbody_queue, /* reset sz */ 458 r->reqbody_queue.tempdirs, 0); 459 cgi_connection_close(hctx); 460 } 461 return HANDLER_GO_ON; 462 } 463 464 465 static int cgi_write_request(handler_ctx *hctx, int fd); 466 467 468 static handler_t cgi_handle_fdevent_send (void *ctx, int revents) { 469 handler_ctx *hctx = ctx; 470 hctx->wr_revents |= revents; 471 joblist_append(hctx->con); 472 return HANDLER_FINISHED; 473 } 474 475 476 static handler_t cgi_process_wr_revents (handler_ctx * const hctx, request_st * const r, int revents) { 477 if (revents & FDEVENT_OUT) { 478 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 479 cgi_connection_close(hctx); 480 return HANDLER_ERROR; 481 } 482 /* more request body to be sent to CGI */ 483 } 484 485 if (revents & FDEVENT_HUP) { 486 /* skip sending remaining data to CGI */ 487 if (r->reqbody_length) { 488 chunkqueue *cq = &r->reqbody_queue; 489 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 490 if (cq->bytes_in != (off_t)r->reqbody_length) { 491 r->keep_alive = 0; 492 } 493 } 494 495 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 496 } else if (revents & FDEVENT_ERR) { 497 /* kill all connections to the cgi process */ 498 #if 1 499 log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR"); 500 #endif 501 cgi_connection_close(hctx); 502 return HANDLER_ERROR; 503 } 504 505 return HANDLER_GO_ON; 506 } 507 508 509 static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) { 510 /* response headers just completed */ 511 handler_ctx *hctx = (handler_ctx *)opts->pdata; 512 513 if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 514 if (hctx->conf.upgrade && r->http_status == 101) { 515 /* 101 Switching Protocols; transition to transparent proxy */ 516 if (r->h2_connect_ext) { 517 r->http_status = 200; /* OK (response status for CONNECT) */ 518 http_header_response_unset(r, HTTP_HEADER_UPGRADE, 519 CONST_STR_LEN("Upgrade")); 520 http_header_response_unset(r, HTTP_HEADER_OTHER, 521 CONST_STR_LEN("Sec-WebSocket-Accept")); 522 } 523 http_response_upgrade_read_body_unknown(r); 524 } 525 else { 526 light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE); 527 #if 0 528 /* preserve prior questionable behavior; likely broken behavior 529 * anyway if backend thinks connection is being upgraded but client 530 * does not receive Connection: upgrade */ 531 http_header_response_unset(r, HTTP_HEADER_UPGRADE, 532 CONST_STR_LEN("Upgrade")); 533 #endif 534 } 535 } 536 else if (__builtin_expect( (r->h2_connect_ext != 0), 0) 537 && r->http_status < 300) { 538 /*(not handling other 1xx intermediate responses here; not expected)*/ 539 http_response_body_clear(r, 0); 540 r->handler_module = NULL; 541 r->http_status = 405; /* Method Not Allowed */ 542 return HANDLER_FINISHED; 543 } 544 545 if (hctx->conf.upgrade 546 && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 547 chunkqueue *cq = &r->reqbody_queue; 548 hctx->conf.upgrade = 0; 549 r->reqbody_length = hctx->orig_reqbody_length; 550 if (cq->bytes_out == (off_t)r->reqbody_length) { 551 cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/ 552 } 553 } 554 555 return HANDLER_GO_ON; 556 } 557 558 559 __attribute_cold__ 560 __attribute_noinline__ 561 static handler_t cgi_local_redir(request_st * const r, handler_ctx * const hctx) { 562 buffer_clear(hctx->response); 563 chunk_buffer_yield(hctx->response); 564 http_response_reset(r); /*(includes r->http_status = 0)*/ 565 r->con->srv->plugins_request_reset(r); 566 /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/ 567 return HANDLER_COMEBACK; 568 } 569 570 571 static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) { 572 const off_t bytes_in = r->write_queue.bytes_in; 573 switch (http_response_read(r, &hctx->opts, 574 hctx->response, hctx->fdn)) { 575 default: 576 if (r->write_queue.bytes_in > bytes_in) 577 hctx->read_ts = log_monotonic_secs; 578 return HANDLER_GO_ON; 579 case HANDLER_ERROR: 580 http_response_backend_error(r); 581 __attribute_fallthrough__ 582 case HANDLER_FINISHED: 583 cgi_connection_close(hctx); 584 return HANDLER_FINISHED; 585 case HANDLER_COMEBACK: 586 return cgi_local_redir(r, hctx); /* HANDLER_COMEBACK */ 587 } 588 } 589 590 591 static handler_t cgi_handle_fdevent(void *ctx, int revents) { 592 handler_ctx *hctx = ctx; 593 hctx->rd_revents |= revents; 594 joblist_append(hctx->con); 595 return HANDLER_FINISHED; 596 } 597 598 599 static handler_t cgi_process_rd_revents(handler_ctx * const hctx, request_st * const r, int revents) { 600 if (revents & FDEVENT_IN) { 601 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 602 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 603 } 604 605 /* perhaps this issue is already handled */ 606 if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { 607 if (r->resp_body_started) { 608 /* drain any remaining data from kernel pipe buffers 609 * even if (r->conf.stream_response_body 610 * & FDEVENT_STREAM_RESPONSE_BUFMIN) 611 * since event loop will spin on fd FDEVENT_HUP event 612 * until unregistered. */ 613 handler_t rc; 614 const unsigned short flags = r->conf.stream_response_body; 615 r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; 616 r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; 617 do { 618 rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/ 619 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ 620 r->conf.stream_response_body = flags; 621 return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */ 622 } else if (!buffer_is_blank(hctx->response)) { 623 /* unfinished header package which is a body in reality */ 624 r->resp_body_started = 1; 625 if (0 != http_chunk_append_buffer(r, hctx->response)) { 626 cgi_connection_close(hctx); 627 return HANDLER_ERROR; 628 } 629 if (0 == r->http_status) r->http_status = 200; /* OK */ 630 } 631 cgi_connection_close(hctx); 632 return HANDLER_FINISHED; 633 } else if (revents & FDEVENT_ERR) { 634 /* kill all connections to the cgi process */ 635 cgi_connection_close(hctx); 636 return HANDLER_ERROR; 637 } 638 639 return HANDLER_GO_ON; 640 } 641 642 643 __attribute_cold__ 644 __attribute_noinline__ 645 static void cgi_env_offset_resize(env_accum *env) { 646 chunk_buffer_prepare_append(env->boffsets, env->boffsets->size*2); 647 env->offsets = (uintptr_t *)(void *)env->boffsets->ptr; 648 env->osize = env->boffsets->size/sizeof(*env->offsets); 649 } 650 651 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 652 env_accum *env = venv; 653 654 if (!key || (!val && val_len)) return -1; 655 656 if (__builtin_expect( (env->osize == env->oused), 0)) 657 cgi_env_offset_resize(env); 658 env->offsets[env->oused++] = env->b->used-1; 659 660 char * const dst = buffer_extend(env->b, key_len + val_len + 2); 661 memcpy(dst, key, key_len); 662 dst[key_len] = '='; 663 if (val_len) memcpy(dst + key_len + 1, val, val_len); 664 dst[key_len + 1 + val_len] = '\0'; 665 666 return 0; 667 } 668 669 static int cgi_write_request(handler_ctx *hctx, int fd) { 670 request_st * const r = hctx->r; 671 chunkqueue *cq = &r->reqbody_queue; 672 chunk *c; 673 674 chunkqueue_remove_finished_chunks(cq); /* unnecessary? */ 675 676 /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. 677 */ 678 679 for (c = cq->first; c; c = cq->first) { 680 ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh); 681 if (wr > 0) { 682 hctx->write_ts = log_monotonic_secs; 683 chunkqueue_mark_written(cq, wr); 684 /* continue if wrote whole chunk or wrote 16k block 685 * (see chunkqueue_write_chunk_file_intermed()) */ 686 if (c != cq->first || wr == 16384) 687 continue; 688 /*(else partial write)*/ 689 } 690 else if (wr < 0) { 691 switch(errno) { 692 case EAGAIN: 693 case EINTR: 694 /* ignore and try again later */ 695 break; 696 case EPIPE: 697 case ECONNRESET: 698 /* connection closed */ 699 log_error(r->conf.errh, __FILE__, __LINE__, 700 "failed to send post data to cgi, connection closed by CGI"); 701 /* skip all remaining data */ 702 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 703 break; 704 default: 705 /* fatal error */ 706 log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed"); 707 return -1; 708 } 709 } 710 /*if (0 == wr) break;*/ /*(might block)*/ 711 break; 712 } 713 714 if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) { 715 /* sent all request body input */ 716 /* close connection to the cgi-script */ 717 if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ 718 --r->con->srv->cur_fds; 719 if (close(fd)) { 720 log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd); 721 } 722 } else { 723 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 724 } 725 } else { 726 off_t cqlen = chunkqueue_length(cq); 727 if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) { 728 /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 729 if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { 730 r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; 731 r->con->is_readable = 1; /* trigger optimistic read from client */ 732 } 733 } 734 struct fdevents * const ev = hctx->ev; 735 if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ 736 hctx->fdtocgi = fd; 737 hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); 738 } 739 if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ 740 if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) { 741 fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0); 742 } 743 } else { 744 /* more request body remains to be sent to CGI so register for fdevents */ 745 hctx->write_ts = log_monotonic_secs; 746 fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT); 747 } 748 } 749 750 return 0; 751 } 752 753 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) { 754 char *args[3]; 755 int to_cgi_fds[2]; 756 int from_cgi_fds[2]; 757 UNUSED(p); 758 759 if (!buffer_is_blank(cgi_handler)) { 760 if (NULL == stat_cache_path_stat(cgi_handler)) { 761 log_perror(r->conf.errh, __FILE__, __LINE__, 762 "stat for cgi-handler %s", cgi_handler->ptr); 763 return -1; 764 } 765 } 766 767 to_cgi_fds[0] = -1; 768 #ifndef __CYGWIN__ 769 if (0 == r->reqbody_length) { 770 /* future: might keep fd open in p->devnull for reuse 771 * and dup() here, or do not close() (later in this func) */ 772 to_cgi_fds[0] = fdevent_open_devnull(); 773 if (-1 == to_cgi_fds[0]) { 774 log_perror(r->conf.errh, __FILE__, __LINE__, "open /dev/null"); 775 return -1; 776 } 777 } 778 else if (!(r->conf.stream_request_body /*(if not streaming request body)*/ 779 & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN)) 780 && !hctx->conf.upgrade) { 781 chunkqueue * const cq = &r->reqbody_queue; 782 chunk * const c = cq->first; 783 if (c && c == cq->last && c->type == FILE_CHUNK && c->file.is_temp) { 784 /* request body in single tempfile if not streaming req body */ 785 if (-1 == c->file.fd 786 && 0 != chunkqueue_open_file_chunk(cq, r->conf.errh)) 787 return -1; 788 #ifdef __COVERITY__ 789 force_assert(-1 != c->file.fd); 790 #endif 791 if (-1 == lseek(c->file.fd, 0, SEEK_SET)) { 792 log_perror(r->conf.errh, __FILE__, __LINE__, 793 "lseek %s", c->mem->ptr); 794 return -1; 795 } 796 to_cgi_fds[0] = c->file.fd; 797 to_cgi_fds[1] = -1; 798 } 799 } 800 #endif 801 802 unsigned int bufsz_hint = 16384; 803 #ifdef _WIN32 804 if (r->reqbody_length <= 1048576 && r->reqbody_length > 0) 805 bufsz_hint = (unsigned int)r->reqbody_length; 806 #endif 807 if (-1 == to_cgi_fds[0] && fdevent_pipe_cloexec(to_cgi_fds, bufsz_hint)) { 808 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 809 return -1; 810 } 811 if (fdevent_pipe_cloexec(from_cgi_fds, bufsz_hint)) { 812 if (0 == r->reqbody_length) { 813 close(to_cgi_fds[0]); 814 } 815 else if (-1 != to_cgi_fds[1]) { 816 close(to_cgi_fds[0]); 817 close(to_cgi_fds[1]); 818 } 819 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 820 return -1; 821 } 822 823 env_accum * const env = &p->env; 824 env->b = chunk_buffer_acquire(); 825 env->boffsets = chunk_buffer_acquire(); 826 buffer_truncate(env->b, 0); 827 char **envp; 828 { 829 size_t i = 0; 830 http_cgi_opts opts = { 0, 0, NULL, NULL }; 831 env->offsets = (uintptr_t *)(void *)env->boffsets->ptr; 832 env->osize = env->boffsets->size/sizeof(*env->offsets); 833 env->oused = 0; 834 835 /* create environment */ 836 837 if (r->h2_connect_ext) { 838 /*(SERVER_PROTOCOL=HTTP/1.1 instead of HTTP/2.0)*/ 839 r->http_version = HTTP_VERSION_1_1; 840 r->http_method = HTTP_METHOD_GET; 841 } 842 if (hctx->conf.upgrade) { 843 r->reqbody_length = hctx->orig_reqbody_length; 844 if (r->reqbody_length < 0) 845 r->reqbody_length = 0; 846 } 847 848 http_cgi_headers(r, &opts, cgi_env_add, env); 849 850 if (hctx->conf.upgrade) 851 r->reqbody_length = -1; 852 if (r->h2_connect_ext) { 853 r->http_version = HTTP_VERSION_2; 854 r->http_method = HTTP_METHOD_CONNECT; 855 /* https://datatracker.ietf.org/doc/html/rfc6455#section-4.1 856 * 7. The request MUST include a header field with the name 857 * |Sec-WebSocket-Key|. The value of this header field MUST be a 858 * nonce consisting of a randomly selected 16-byte value that has 859 * been base64-encoded (see Section 4 of [RFC4648]). The nonce 860 * MUST be selected randomly for each connection. 861 * Note: Sec-WebSocket-Key is not used in RFC8441; 862 * include Sec-WebSocket-Key for HTTP/1.1 compatibility; 863 * !!not random!! base64-encoded "0000000000000000" */ 864 if (!http_header_request_get(r, HTTP_HEADER_OTHER, 865 CONST_STR_LEN("Sec-WebSocket-Key"))) 866 cgi_env_add(env, CONST_STR_LEN("HTTP_SEC_WEBSOCKET_KEY"), 867 CONST_STR_LEN("MDAwMDAwMDAwMDAwMDAwMA==")); 868 /*(Upgrade and Connection should not exist for HTTP/2 request)*/ 869 cgi_env_add(env, CONST_STR_LEN("HTTP_UPGRADE"), CONST_STR_LEN("websocket")); 870 cgi_env_add(env, CONST_STR_LEN("HTTP_CONNECTION"), CONST_STR_LEN("upgrade")); 871 } 872 873 /* for valgrind */ 874 if (p->env.ld_preload) { 875 cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), BUF_PTR_LEN(p->env.ld_preload)); 876 } 877 if (p->env.ld_library_path) { 878 cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), BUF_PTR_LEN(p->env.ld_library_path)); 879 } 880 #ifdef __CYGWIN__ 881 /* CYGWIN needs SYSTEMROOT */ 882 if (p->env.systemroot) { 883 cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), BUF_PTR_LEN(p->env.systemroot)); 884 } 885 #endif 886 887 /* adjust (uintptr_t) offsets to (char *) ptr 888 * (stored as offsets while accumulating in buffer, 889 * in case buffer is reallocated during env creation) */ 890 if (__builtin_expect( (env->osize == env->oused), 0)) 891 cgi_env_offset_resize(env); 892 envp = (char **)env->offsets; 893 envp[env->oused] = NULL; 894 const uintptr_t baseptr = (uintptr_t)env->b->ptr; 895 for (i = 0; i < env->oused; ++i) 896 envp[i] += baseptr; 897 898 /* set up args */ 899 i = 0; 900 901 if (!buffer_is_blank(cgi_handler)) { 902 args[i++] = cgi_handler->ptr; 903 } 904 args[i++] = r->physical.path.ptr; 905 args[i ] = NULL; 906 } 907 908 int dfd = fdevent_open_dirname(r->physical.path.ptr,r->conf.follow_symlink); 909 if (-1 == dfd) { 910 log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr); 911 } 912 913 int serrh_fd = r->conf.serrh ? r->conf.serrh->fd : -1; 914 pid_t pid = (dfd >= 0) 915 ? fdevent_fork_execve(args[0], args, envp, 916 to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd) 917 : -1; 918 919 chunk_buffer_release(env->boffsets); 920 chunk_buffer_release(env->b); 921 env->boffsets = NULL; 922 env->b = NULL; 923 924 if (-1 == pid) { 925 /* log error with errno prior to calling close() (might change errno) */ 926 log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed"); 927 if (-1 != dfd) close(dfd); 928 close(from_cgi_fds[0]); 929 close(from_cgi_fds[1]); 930 if (0 == r->reqbody_length) { 931 close(to_cgi_fds[0]); 932 } 933 else if (-1 != to_cgi_fds[1]) { 934 close(to_cgi_fds[0]); 935 close(to_cgi_fds[1]); 936 } 937 return -1; 938 } else { 939 if (-1 != dfd) close(dfd); 940 close(from_cgi_fds[1]); 941 942 hctx->fd = from_cgi_fds[0]; 943 hctx->cgi_pid = cgi_pid_add(p, pid, hctx); 944 945 if (0 == r->reqbody_length) { 946 close(to_cgi_fds[0]); 947 } 948 else if (-1 == to_cgi_fds[1]) { 949 chunkqueue * const cq = &r->reqbody_queue; 950 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 951 } 952 else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1]) 953 && 0 == cgi_write_request(hctx, to_cgi_fds[1])) { 954 close(to_cgi_fds[0]); 955 ++r->con->srv->cur_fds; 956 } 957 else { 958 close(to_cgi_fds[0]); 959 close(to_cgi_fds[1]); 960 /*(hctx->fd not yet registered with fdevent, so manually 961 * cleanup here; see fdevent_register() further below)*/ 962 close(hctx->fd); 963 hctx->fd = -1; 964 cgi_connection_close(hctx); 965 return -1; 966 } 967 968 ++r->con->srv->cur_fds; 969 970 struct fdevents * const ev = hctx->ev; 971 hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx); 972 if (-1 == fdevent_fcntl_set_nb(hctx->fd)) { 973 log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed"); 974 cgi_connection_close(hctx); 975 return -1; 976 } 977 hctx->read_ts = log_monotonic_secs; 978 fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); 979 980 return 0; 981 } 982 } 983 984 URIHANDLER_FUNC(cgi_is_handled) { 985 plugin_data *p = p_d; 986 const stat_cache_st *st; 987 data_string *ds; 988 989 if (NULL != r->handler_module) return HANDLER_GO_ON; 990 /* r->physical.path is non-empty for handle_subrequest_start */ 991 /*if (buffer_is_blank(&r->physical.path)) return HANDLER_GO_ON;*/ 992 993 mod_cgi_patch_config(r, p); 994 if (NULL == p->conf.cgi) return HANDLER_GO_ON; 995 996 ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path); 997 if (NULL == ds) return HANDLER_GO_ON; 998 999 /* r->tmp_sce is set in http_response_physical_path_check() and is valid 1000 * in handle_subrequest_start callback -- handle_subrequest_start callbacks 1001 * should not change r->physical.path (or should invalidate r->tmp_sce) */ 1002 st = r->tmp_sce && buffer_is_equal(&r->tmp_sce->name, &r->physical.path) 1003 ? &r->tmp_sce->st 1004 : stat_cache_path_stat(&r->physical.path); 1005 if (NULL == st) return HANDLER_GO_ON; 1006 1007 /* (aside: CGI might be executable even if it is not readable) */ 1008 if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; 1009 if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; 1010 1011 if (__builtin_expect( (r->h2_connect_ext != 0), 0) && !p->conf.upgrade) { 1012 r->http_status = 405; /* Method Not Allowed */ 1013 return HANDLER_FINISHED; 1014 } 1015 1016 if (r->reqbody_length 1017 && p->tempfile_accum 1018 && !(r->conf.stream_request_body /*(if not streaming request body)*/ 1019 & (FDEVENT_STREAM_REQUEST|FDEVENT_STREAM_REQUEST_BUFMIN))) { 1020 /* store request body in single tempfile if not streaming request body*/ 1021 r->reqbody_queue.upload_temp_file_size = 1022 (off_t)((1uLL << (sizeof(off_t)*8-1))-1); 1023 } 1024 1025 { 1026 handler_ctx *hctx = cgi_handler_ctx_init(); 1027 hctx->ev = r->con->srv->ev; 1028 hctx->r = r; 1029 hctx->con = r->con; 1030 hctx->plugin_data = p; 1031 hctx->cgi_handler = &ds->value; 1032 memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); 1033 if (__builtin_expect( (r->h2_connect_ext != 0), 0)) { 1034 } 1035 else if (!light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE)) 1036 hctx->conf.upgrade = 0; 1037 else if (!hctx->conf.upgrade || r->http_version != HTTP_VERSION_1_1) { 1038 hctx->conf.upgrade = 0; 1039 http_header_request_unset(r, HTTP_HEADER_UPGRADE, 1040 CONST_STR_LEN("Upgrade")); 1041 } 1042 if (hctx->conf.upgrade) { 1043 hctx->orig_reqbody_length = r->reqbody_length; 1044 r->reqbody_length = -1; 1045 } 1046 hctx->opts.max_per_read = 1047 !(r->conf.stream_response_body /*(if not streaming response body)*/ 1048 & (FDEVENT_STREAM_RESPONSE|FDEVENT_STREAM_RESPONSE_BUFMIN)) 1049 ? 262144 1050 : (r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 1051 ? 16384 /* FDEVENT_STREAM_RESPONSE_BUFMIN */ 1052 : 65536; /* FDEVENT_STREAM_RESPONSE */ 1053 hctx->opts.fdfmt = S_IFIFO; 1054 hctx->opts.backend = BACKEND_CGI; 1055 hctx->opts.authorizer = 0; 1056 hctx->opts.local_redir = hctx->conf.local_redir; 1057 hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow; 1058 hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot; 1059 hctx->opts.pdata = hctx; 1060 hctx->opts.headers = cgi_response_headers; 1061 r->plugin_ctx[p->id] = hctx; 1062 r->handler_module = p->self; 1063 } 1064 1065 return HANDLER_GO_ON; 1066 } 1067 1068 /* 1069 * - HANDLER_GO_ON : not our job 1070 * - HANDLER_FINISHED: got response 1071 * - HANDLER_WAIT_FOR_EVENT: waiting for response 1072 */ 1073 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { 1074 plugin_data * const p = p_d; 1075 handler_ctx * const hctx = r->plugin_ctx[p->id]; 1076 if (NULL == hctx) return HANDLER_GO_ON; 1077 1078 if (__builtin_expect( 1079 (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN), 0) 1080 && hctx->conf.limits && hctx->conf.limits->signal_fin) { 1081 /* XXX: consider setting r->http_status = 499 if (0 == r->http_status) 1082 * (499 is nginx custom status to indicate client closed connection) */ 1083 if (-1 == hctx->fd) return HANDLER_ERROR; /*(CGI not yet spawned)*/ 1084 if (hctx->cgi_pid) /* send signal to notify CGI about TCP FIN */ 1085 cgi_pid_kill(hctx->cgi_pid, hctx->conf.limits->signal_fin); 1086 } 1087 1088 const int rd_revents = hctx->rd_revents; 1089 const int wr_revents = hctx->wr_revents; 1090 if (rd_revents) { 1091 hctx->rd_revents = 0; 1092 handler_t rc = cgi_process_rd_revents(hctx, r, rd_revents); 1093 if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/ 1094 } 1095 if (wr_revents) { 1096 hctx->wr_revents = 0; 1097 handler_t rc = cgi_process_wr_revents(hctx, r, wr_revents); 1098 if (rc != HANDLER_GO_ON) return rc; /*(might invalidate hctx)*/ 1099 } 1100 1101 if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 1102 && r->resp_body_started) { 1103 if (chunkqueue_length(&r->write_queue) > 65536 - 4096) { 1104 fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN); 1105 } else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) { 1106 /* optimistic read from backend */ 1107 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 1108 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 1109 hctx->read_ts = log_monotonic_secs; 1110 fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN); 1111 } 1112 } 1113 1114 chunkqueue * const cq = &r->reqbody_queue; 1115 1116 if (cq->bytes_in != (off_t)r->reqbody_length) { 1117 /*(64k - 4k to attempt to avoid temporary files 1118 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 1119 if (chunkqueue_length(cq) > 65536 - 4096 1120 && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ 1121 r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 1122 } else { 1123 handler_t rc = r->con->reqbody_read(r); 1124 if (rc != HANDLER_GO_ON 1125 && !(hctx->conf.upgrade && -1 == hctx->fd 1126 && rc == HANDLER_WAIT_FOR_EVENT)) 1127 return rc; 1128 } 1129 } 1130 1131 if (-1 == hctx->fd) { 1132 /* CGI environment requires that Content-Length be set. 1133 * Send 411 Length Required if Content-Length missing. 1134 * (occurs here if client sends Transfer-Encoding: chunked 1135 * and module is flagged to stream request body to backend) */ 1136 if (-1 == r->reqbody_length && !hctx->conf.upgrade) { 1137 return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 1138 ? http_response_reqbody_read_error(r, 411) 1139 : HANDLER_WAIT_FOR_EVENT; 1140 } 1141 if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) { 1142 r->http_status = 500; 1143 r->handler_module = NULL; 1144 1145 return HANDLER_FINISHED; 1146 } 1147 } else if (!chunkqueue_is_empty(cq)) { 1148 if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) 1149 return HANDLER_WAIT_FOR_EVENT; 1150 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 1151 cgi_connection_close(hctx); 1152 return HANDLER_ERROR; 1153 } 1154 } 1155 1156 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ 1157 return HANDLER_WAIT_FOR_EVENT; 1158 } 1159 1160 1161 __attribute_cold__ 1162 __attribute_noinline__ 1163 static void cgi_trigger_hctx_timeout(handler_ctx * const hctx, const char * const msg) { 1164 request_st * const r = hctx->r; 1165 joblist_append(r->con); 1166 1167 log_error(r->conf.errh, __FILE__, __LINE__, 1168 "%s timeout on CGI: %s (pid: %lld)", 1169 msg, r->physical.path.ptr, (long long)hctx->cgi_pid->pid); 1170 1171 if (*msg == 'w') { /* "write" */ 1172 /* theoretically, response might be waiting on hctx->fdn pipe 1173 * if it arrived since we last checked for event, and if CGI 1174 * timeout out while reading (or did not read) request body */ 1175 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 1176 if (rc != HANDLER_GO_ON) return; /*(unless HANDLER_GO_ON)*/ 1177 } 1178 1179 if (0 == r->http_status) r->http_status = 504; /* Gateway Timeout */ 1180 cgi_connection_close(hctx); 1181 } 1182 1183 1184 static handler_t cgi_trigger_cb(server *srv, void *p_d) { 1185 UNUSED(srv); 1186 const unix_time64_t mono = log_monotonic_secs; 1187 plugin_data * const p = p_d; 1188 for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) { 1189 /*(hctx stays in cgi_pid list until process pid is reaped, 1190 * so p->cgi_pid[] is not modified during this loop)*/ 1191 handler_ctx * const hctx = cgi_pid->hctx; 1192 if (!hctx) continue; /*(already called cgi_pid_kill())*/ 1193 const cgi_limits * const limits = hctx->conf.limits; 1194 if (NULL == limits) continue; 1195 if (limits->read_timeout && hctx->fdn 1196 && (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN) 1197 && mono - hctx->read_ts > limits->read_timeout) { 1198 cgi_trigger_hctx_timeout(hctx, "read"); 1199 continue; 1200 } 1201 if (limits->write_timeout && hctx->fdntocgi 1202 && (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) 1203 && mono - hctx->write_ts > limits->write_timeout) { 1204 cgi_trigger_hctx_timeout(hctx, "write"); 1205 continue; 1206 } 1207 } 1208 return HANDLER_GO_ON; 1209 } 1210 1211 1212 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { 1213 /*(XXX: if supporting a large number of CGI, might use a different algorithm 1214 * instead of linked list, e.g. splaytree indexed with pid)*/ 1215 plugin_data *p = (plugin_data *)p_d; 1216 for (cgi_pid_t *cgi_pid = p->cgi_pid; cgi_pid; cgi_pid = cgi_pid->next) { 1217 if (pid != cgi_pid->pid) continue; 1218 1219 handler_ctx * const hctx = cgi_pid->hctx; 1220 if (hctx) hctx->cgi_pid = NULL; 1221 1222 if (WIFEXITED(status)) { 1223 /* (skip logging (non-zero) CGI exit; might be very noisy) */ 1224 } 1225 else if (WIFSIGNALED(status)) { 1226 /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/ 1227 if (WTERMSIG(status) != cgi_pid->signal_sent) { 1228 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1229 log_error(errh, __FILE__, __LINE__, 1230 "CGI pid %d died with signal %d", pid, WTERMSIG(status)); 1231 } 1232 } 1233 else { 1234 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1235 log_error(errh, __FILE__, __LINE__, 1236 "CGI pid %d ended unexpectedly", pid); 1237 } 1238 1239 cgi_pid_del(p, cgi_pid); 1240 return HANDLER_FINISHED; 1241 } 1242 1243 return HANDLER_GO_ON; 1244 } 1245 1246 1247 __attribute_cold__ 1248 int mod_cgi_plugin_init(plugin *p); 1249 int mod_cgi_plugin_init(plugin *p) { 1250 p->version = LIGHTTPD_VERSION_ID; 1251 p->name = "cgi"; 1252 1253 p->handle_request_reset = cgi_connection_close_callback; 1254 p->handle_subrequest_start = cgi_is_handled; 1255 p->handle_subrequest = mod_cgi_handle_subrequest; 1256 p->handle_trigger = cgi_trigger_cb; 1257 p->handle_waitpid = cgi_waitpid_cb; 1258 p->init = mod_cgi_init; 1259 p->cleanup = mod_cgi_free; 1260 p->set_defaults = mod_cgi_set_defaults; 1261 1262 return 0; 1263 } 1264