1 #include "first.h" 2 3 #include "base.h" 4 #include "stat_cache.h" 5 #include "http_kv.h" 6 #include "log.h" 7 #include "connections.h" 8 #include "response.h" 9 #include "http_chunk.h" 10 #include "http_header.h" 11 12 #include "plugin.h" 13 14 #include <sys/types.h> 15 #include "sys-socket.h" 16 #ifdef HAVE_SYS_WAIT_H 17 #include <sys/wait.h> 18 #endif 19 20 #include <unistd.h> 21 #include <errno.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <fdevent.h> 25 26 #include <fcntl.h> 27 #include <signal.h> 28 29 static int pipe_cloexec(int pipefd[2]) { 30 #ifdef HAVE_PIPE2 31 if (0 == pipe2(pipefd, O_CLOEXEC)) return 0; 32 #endif 33 return 0 == pipe(pipefd) 34 #ifdef FD_CLOEXEC 35 && 0 == fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) 36 && 0 == fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) 37 #endif 38 ? 0 39 : -1; 40 } 41 42 typedef struct { 43 char *ptr; 44 size_t used; 45 size_t size; 46 size_t *offsets; 47 size_t osize; 48 size_t oused; 49 char **eptr; 50 size_t esize; 51 buffer *ld_preload; 52 buffer *ld_library_path; 53 #ifdef __CYGWIN__ 54 buffer *systemroot; 55 #endif 56 } env_accum; 57 58 typedef struct { 59 struct { pid_t pid; void *ctx; } *ptr; 60 size_t used; 61 size_t size; 62 } buffer_pid_t; 63 64 typedef struct { 65 const array *cgi; 66 unsigned short execute_x_only; 67 unsigned short local_redir; 68 unsigned short xsendfile_allow; 69 unsigned short upgrade; 70 const array *xsendfile_docroot; 71 } plugin_config; 72 73 typedef struct { 74 PLUGIN_DATA; 75 plugin_config defaults; 76 plugin_config conf; 77 buffer_pid_t cgi_pid; 78 env_accum env; 79 } plugin_data; 80 81 typedef struct { 82 pid_t pid; 83 int fd; 84 int fdtocgi; 85 fdnode *fdn; 86 fdnode *fdntocgi; 87 88 request_st *r; 89 struct fdevents *ev; /* dumb pointer */ 90 plugin_data *plugin_data; /* dumb pointer */ 91 92 buffer *response; 93 buffer *cgi_handler; /* dumb pointer */ 94 http_response_opts opts; 95 plugin_config conf; 96 } handler_ctx; 97 98 static handler_ctx * cgi_handler_ctx_init(void) { 99 handler_ctx *hctx = calloc(1, sizeof(*hctx)); 100 101 force_assert(hctx); 102 103 hctx->response = chunk_buffer_acquire(); 104 hctx->fd = -1; 105 hctx->fdtocgi = -1; 106 107 return hctx; 108 } 109 110 static void cgi_handler_ctx_free(handler_ctx *hctx) { 111 chunk_buffer_release(hctx->response); 112 free(hctx); 113 } 114 115 INIT_FUNC(mod_cgi_init) { 116 plugin_data *p; 117 const char *s; 118 119 p = calloc(1, sizeof(*p)); 120 121 force_assert(p); 122 123 /* for valgrind */ 124 s = getenv("LD_PRELOAD"); 125 if (s) p->env.ld_preload = buffer_init_string(s); 126 s = getenv("LD_LIBRARY_PATH"); 127 if (s) p->env.ld_library_path = buffer_init_string(s); 128 #ifdef __CYGWIN__ 129 /* CYGWIN needs SYSTEMROOT */ 130 s = getenv("SYSTEMROOT"); 131 if (s) p->env.systemroot = buffer_init_string(s); 132 #endif 133 134 return p; 135 } 136 137 138 FREE_FUNC(mod_cgi_free) { 139 plugin_data *p = p_d; 140 buffer_pid_t *bp = &(p->cgi_pid); 141 if (bp->ptr) free(bp->ptr); 142 free(p->env.ptr); 143 free(p->env.offsets); 144 free(p->env.eptr); 145 buffer_free(p->env.ld_preload); 146 buffer_free(p->env.ld_library_path); 147 #ifdef __CYGWIN__ 148 buffer_free(p->env.systemroot); 149 #endif 150 } 151 152 static void mod_cgi_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 153 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 154 case 0: /* cgi.assign */ 155 pconf->cgi = cpv->v.a; 156 break; 157 case 1: /* cgi.execute-x-only */ 158 pconf->execute_x_only = (unsigned short)cpv->v.u; 159 break; 160 case 2: /* cgi.x-sendfile */ 161 pconf->xsendfile_allow = (unsigned short)cpv->v.u; 162 break; 163 case 3: /* cgi.x-sendfile-docroot */ 164 pconf->xsendfile_docroot = cpv->v.a; 165 break; 166 case 4: /* cgi.local-redir */ 167 pconf->local_redir = (unsigned short)cpv->v.u; 168 break; 169 case 5: /* cgi.upgrade */ 170 pconf->upgrade = (unsigned short)cpv->v.u; 171 break; 172 default:/* should not happen */ 173 return; 174 } 175 } 176 177 static void mod_cgi_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 178 do { 179 mod_cgi_merge_config_cpv(pconf, cpv); 180 } while ((++cpv)->k_id != -1); 181 } 182 183 static void mod_cgi_patch_config(request_st * const r, plugin_data * const p) { 184 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 185 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 186 for (int i = 1, used = p->nconfig; i < used; ++i) { 187 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 188 mod_cgi_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 189 } 190 } 191 192 SETDEFAULTS_FUNC(mod_cgi_set_defaults) { 193 static const config_plugin_keys_t cpk[] = { 194 { CONST_STR_LEN("cgi.assign"), 195 T_CONFIG_ARRAY_KVSTRING, 196 T_CONFIG_SCOPE_CONNECTION } 197 ,{ CONST_STR_LEN("cgi.execute-x-only"), 198 T_CONFIG_BOOL, 199 T_CONFIG_SCOPE_CONNECTION } 200 ,{ CONST_STR_LEN("cgi.x-sendfile"), 201 T_CONFIG_BOOL, 202 T_CONFIG_SCOPE_CONNECTION } 203 ,{ CONST_STR_LEN("cgi.x-sendfile-docroot"), 204 T_CONFIG_ARRAY_VLIST, 205 T_CONFIG_SCOPE_CONNECTION } 206 ,{ CONST_STR_LEN("cgi.local-redir"), 207 T_CONFIG_BOOL, 208 T_CONFIG_SCOPE_CONNECTION } 209 ,{ CONST_STR_LEN("cgi.upgrade"), 210 T_CONFIG_BOOL, 211 T_CONFIG_SCOPE_CONNECTION } 212 ,{ NULL, 0, 213 T_CONFIG_UNSET, 214 T_CONFIG_SCOPE_UNSET } 215 }; 216 217 plugin_data * const p = p_d; 218 if (!config_plugin_values_init(srv, p, cpk, "mod_cgi")) 219 return HANDLER_ERROR; 220 221 /* process and validate config directives 222 * (init i to 0 if global context; to 1 to skip empty global context) */ 223 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 224 const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 225 for (; -1 != cpv->k_id; ++cpv) { 226 switch (cpv->k_id) { 227 case 0: /* cgi.assign */ 228 case 1: /* cgi.execute-x-only */ 229 case 2: /* cgi.x-sendfile */ 230 break; 231 case 3: /* cgi.x-sendfile-docroot */ 232 for (uint32_t j = 0; j < cpv->v.a->used; ++j) { 233 data_string *ds = (data_string *)cpv->v.a->data[j]; 234 if (ds->value.ptr[0] != '/') { 235 log_error(srv->errh, __FILE__, __LINE__, 236 "%s paths must begin with '/'; invalid: \"%s\"", 237 cpk[cpv->k_id].k, ds->value.ptr); 238 return HANDLER_ERROR; 239 } 240 buffer_path_simplify(&ds->value, &ds->value); 241 buffer_append_slash(&ds->value); 242 } 243 break; 244 case 4: /* cgi.local-redir */ 245 case 5: /* cgi.upgrade */ 246 break; 247 default:/* should not happen */ 248 break; 249 } 250 } 251 } 252 253 /* initialize p->defaults from global config context */ 254 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 255 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 256 if (-1 != cpv->k_id) 257 mod_cgi_merge_config(&p->defaults, cpv); 258 } 259 260 return HANDLER_GO_ON; 261 } 262 263 264 static void cgi_pid_add(plugin_data *p, pid_t pid, void *ctx) { 265 buffer_pid_t *bp = &(p->cgi_pid); 266 267 if (bp->used == bp->size) { 268 bp->size += 16; 269 bp->ptr = realloc(bp->ptr, sizeof(*bp->ptr) * bp->size); 270 force_assert(bp->ptr); 271 } 272 273 bp->ptr[bp->used].pid = pid; 274 bp->ptr[bp->used].ctx = ctx; 275 ++bp->used; 276 } 277 278 static void cgi_pid_kill(plugin_data *p, pid_t pid) { 279 buffer_pid_t *bp = &(p->cgi_pid); 280 for (size_t i = 0; i < bp->used; ++i) { 281 if (bp->ptr[i].pid == pid) { 282 bp->ptr[i].ctx = NULL; 283 kill(pid, SIGTERM); 284 return; 285 } 286 } 287 } 288 289 static void cgi_pid_del(plugin_data *p, size_t i) { 290 buffer_pid_t *bp = &(p->cgi_pid); 291 292 if (i != bp->used - 1) 293 bp->ptr[i] = bp->ptr[bp->used - 1]; 294 295 --bp->used; 296 } 297 298 299 static void cgi_connection_close_fdtocgi(handler_ctx *hctx) { 300 /*(closes only hctx->fdtocgi)*/ 301 struct fdevents * const ev = hctx->ev; 302 fdevent_fdnode_event_del(ev, hctx->fdntocgi); 303 /*fdevent_unregister(ev, hctx->fdtocgi);*//*(handled below)*/ 304 fdevent_sched_close(ev, hctx->fdtocgi, 0); 305 hctx->fdntocgi = NULL; 306 hctx->fdtocgi = -1; 307 } 308 309 static void cgi_connection_close(handler_ctx *hctx) { 310 /* the connection to the browser went away, but we still have a connection 311 * to the CGI script 312 * 313 * close cgi-connection 314 */ 315 316 if (hctx->fd != -1) { 317 struct fdevents * const ev = hctx->ev; 318 /* close connection to the cgi-script */ 319 fdevent_fdnode_event_del(ev, hctx->fdn); 320 /*fdevent_unregister(ev, hctx->fd);*//*(handled below)*/ 321 fdevent_sched_close(ev, hctx->fd, 0); 322 hctx->fdn = NULL; 323 } 324 325 if (hctx->fdtocgi != -1) { 326 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 327 } 328 329 plugin_data * const p = hctx->plugin_data; 330 331 if (hctx->pid > 0) { 332 cgi_pid_kill(p, hctx->pid); 333 } 334 335 request_st * const r = hctx->r; 336 337 r->plugin_ctx[p->id] = NULL; 338 339 cgi_handler_ctx_free(hctx); 340 341 /* finish response (if not already r->resp_body_started, r->resp_body_finished) */ 342 if (r->handler_module == p->self) { 343 http_response_backend_done(r); 344 } 345 } 346 347 static handler_t cgi_connection_close_callback(request_st * const r, void *p_d) { 348 plugin_data *p = p_d; 349 handler_ctx *hctx = r->plugin_ctx[p->id]; 350 if (hctx) cgi_connection_close(hctx); 351 352 return HANDLER_GO_ON; 353 } 354 355 356 static int cgi_write_request(handler_ctx *hctx, int fd); 357 358 359 static handler_t cgi_handle_fdevent_send (void *ctx, int revents) { 360 handler_ctx *hctx = ctx; 361 request_st * const r = hctx->r; 362 363 /*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/ 364 joblist_append(r->con); 365 366 if (revents & FDEVENT_OUT) { 367 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 368 cgi_connection_close(hctx); 369 return HANDLER_ERROR; 370 } 371 /* more request body to be sent to CGI */ 372 } 373 374 if (revents & FDEVENT_HUP) { 375 /* skip sending remaining data to CGI */ 376 if (r->reqbody_length) { 377 chunkqueue *cq = &r->reqbody_queue; 378 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 379 if (cq->bytes_in != (off_t)r->reqbody_length) { 380 r->keep_alive = 0; 381 } 382 } 383 384 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 385 } else if (revents & FDEVENT_ERR) { 386 /* kill all connections to the cgi process */ 387 #if 1 388 log_error(r->conf.errh, __FILE__, __LINE__, "cgi-FDEVENT_ERR"); 389 #endif 390 cgi_connection_close(hctx); 391 return HANDLER_ERROR; 392 } 393 394 return HANDLER_FINISHED; 395 } 396 397 398 static handler_t cgi_response_headers(request_st * const r, struct http_response_opts_t *opts) { 399 /* response headers just completed */ 400 handler_ctx *hctx = (handler_ctx *)opts->pdata; 401 402 if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 403 if (hctx->conf.upgrade && r->http_status == 101) { 404 /* 101 Switching Protocols; transition to transparent proxy */ 405 http_response_upgrade_read_body_unknown(r); 406 } 407 else { 408 light_bclr(r->resp_htags, HTTP_HEADER_UPGRADE); 409 #if 0 410 /* preserve prior questionable behavior; likely broken behavior 411 * anyway if backend thinks connection is being upgraded but client 412 * does not receive Connection: upgrade */ 413 http_header_response_unset(r, HTTP_HEADER_UPGRADE, 414 CONST_STR_LEN("Upgrade")); 415 #endif 416 } 417 } 418 419 if (hctx->conf.upgrade 420 && !light_btst(r->resp_htags, HTTP_HEADER_UPGRADE)) { 421 chunkqueue *cq = &r->reqbody_queue; 422 hctx->conf.upgrade = 0; 423 if (cq->bytes_out == (off_t)r->reqbody_length) { 424 cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/ 425 } 426 } 427 428 return HANDLER_GO_ON; 429 } 430 431 432 static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) { 433 switch (http_response_read(r, &hctx->opts, 434 hctx->response, hctx->fdn)) { 435 default: 436 return HANDLER_GO_ON; 437 case HANDLER_ERROR: 438 http_response_backend_error(r); 439 __attribute_fallthrough__ 440 case HANDLER_FINISHED: 441 cgi_connection_close(hctx); 442 return HANDLER_FINISHED; 443 case HANDLER_COMEBACK: 444 /* flag for mod_cgi_handle_subrequest() */ 445 hctx->conf.local_redir = 2; 446 buffer_clear(hctx->response); 447 return HANDLER_COMEBACK; 448 } 449 } 450 451 452 static handler_t cgi_handle_fdevent(void *ctx, int revents) { 453 handler_ctx *hctx = ctx; 454 request_st * const r = hctx->r; 455 456 joblist_append(r->con); 457 458 if (revents & FDEVENT_IN) { 459 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 460 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 461 } 462 463 /* perhaps this issue is already handled */ 464 if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { 465 if (r->resp_body_started) { 466 /* drain any remaining data from kernel pipe buffers 467 * even if (r->conf.stream_response_body 468 * & FDEVENT_STREAM_RESPONSE_BUFMIN) 469 * since event loop will spin on fd FDEVENT_HUP event 470 * until unregistered. */ 471 handler_t rc; 472 const unsigned short flags = r->conf.stream_response_body; 473 r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; 474 r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; 475 do { 476 rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/ 477 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ 478 r->conf.stream_response_body = flags; 479 return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */ 480 } else if (!buffer_string_is_empty(hctx->response)) { 481 /* unfinished header package which is a body in reality */ 482 r->resp_body_started = 1; 483 if (0 != http_chunk_append_buffer(r, hctx->response)) { 484 cgi_connection_close(hctx); 485 return HANDLER_ERROR; 486 } 487 if (0 == r->http_status) r->http_status = 200; /* OK */ 488 } 489 cgi_connection_close(hctx); 490 } else if (revents & FDEVENT_ERR) { 491 /* kill all connections to the cgi process */ 492 cgi_connection_close(hctx); 493 return HANDLER_ERROR; 494 } 495 496 return HANDLER_FINISHED; 497 } 498 499 500 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 501 env_accum *env = venv; 502 char *dst; 503 504 if (!key || (!val && val_len)) return -1; 505 506 if (env->size - env->used < key_len + val_len + 2) { 507 if (0 == env->size) env->size = 4096; 508 do { env->size *= 2; } while (env->size - env->used < key_len + val_len + 2); 509 env->ptr = realloc(env->ptr, env->size); 510 force_assert(env->ptr); 511 } 512 513 dst = env->ptr + env->used; 514 memcpy(dst, key, key_len); 515 dst[key_len] = '='; 516 memcpy(dst + key_len + 1, val, val_len); 517 dst[key_len + 1 + val_len] = '\0'; 518 519 if (env->osize == env->oused) { 520 env->osize += 16; 521 env->offsets = realloc(env->offsets, env->osize * sizeof(*env->offsets)); 522 force_assert(env->offsets); 523 } 524 env->offsets[env->oused++] = env->used; 525 env->used += key_len + val_len + 2; 526 527 return 0; 528 } 529 530 static int cgi_write_request(handler_ctx *hctx, int fd) { 531 request_st * const r = hctx->r; 532 chunkqueue *cq = &r->reqbody_queue; 533 chunk *c; 534 535 chunkqueue_remove_finished_chunks(cq); /* unnecessary? */ 536 537 /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. 538 * solution: if this is still a problem on windows, then substitute 539 * socketpair() for pipe() and closesocket() for close() on windows. 540 */ 541 542 for (c = cq->first; c; c = cq->first) { 543 ssize_t wr = chunkqueue_write_chunk_to_pipe(fd, cq, r->conf.errh); 544 if (wr > 0) { 545 chunkqueue_mark_written(cq, wr); 546 /* continue if wrote whole chunk or wrote 16k block 547 * (see chunkqueue_write_chunk_file_intermed()) */ 548 if (c != cq->first || wr == 16384) 549 continue; 550 /*(else partial write)*/ 551 } 552 else if (wr < 0) { 553 switch(errno) { 554 case EAGAIN: 555 case EINTR: 556 /* ignore and try again later */ 557 break; 558 case EPIPE: 559 case ECONNRESET: 560 /* connection closed */ 561 log_error(r->conf.errh, __FILE__, __LINE__, 562 "failed to send post data to cgi, connection closed by CGI"); 563 /* skip all remaining data */ 564 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 565 break; 566 default: 567 /* fatal error */ 568 log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed"); 569 return -1; 570 } 571 } 572 /*if (0 == wr) break;*/ /*(might block)*/ 573 break; 574 } 575 576 if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) { 577 /* sent all request body input */ 578 /* close connection to the cgi-script */ 579 if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ 580 --r->con->srv->cur_fds; 581 if (close(fd)) { 582 log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd); 583 } 584 } else { 585 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 586 } 587 } else { 588 off_t cqlen = chunkqueue_length(cq); 589 if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) { 590 /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 591 if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { 592 r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; 593 r->con->is_readable = 1; /* trigger optimistic read from client */ 594 } 595 } 596 struct fdevents * const ev = hctx->ev; 597 if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ 598 hctx->fdtocgi = fd; 599 hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); 600 } 601 if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ 602 if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) { 603 fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0); 604 } 605 } else { 606 /* more request body remains to be sent to CGI so register for fdevents */ 607 fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT); 608 } 609 } 610 611 return 0; 612 } 613 614 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) { 615 char *args[3]; 616 int to_cgi_fds[2]; 617 int from_cgi_fds[2]; 618 int dfd = -1; 619 UNUSED(p); 620 621 if (!buffer_string_is_empty(cgi_handler)) { 622 if (NULL == stat_cache_path_stat(cgi_handler)) { 623 log_perror(r->conf.errh, __FILE__, __LINE__, 624 "stat for cgi-handler %s", cgi_handler->ptr); 625 return -1; 626 } 627 } 628 629 if (pipe_cloexec(to_cgi_fds)) { 630 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 631 return -1; 632 } 633 if (pipe_cloexec(from_cgi_fds)) { 634 close(to_cgi_fds[0]); 635 close(to_cgi_fds[1]); 636 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 637 return -1; 638 } 639 640 { 641 size_t i = 0; 642 http_cgi_opts opts = { 0, 0, NULL, NULL }; 643 env_accum *env = &p->env; 644 env->used = 0; 645 env->oused = 0; 646 647 /* create environment */ 648 649 http_cgi_headers(r, &opts, cgi_env_add, env); 650 651 /* for valgrind */ 652 if (p->env.ld_preload) { 653 cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload)); 654 } 655 if (p->env.ld_library_path) { 656 cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path)); 657 } 658 #ifdef __CYGWIN__ 659 /* CYGWIN needs SYSTEMROOT */ 660 if (p->env.systemroot) { 661 cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot)); 662 } 663 #endif 664 665 if (env->esize <= env->oused) { 666 env->esize = (env->oused + 1 + 0xf) & ~(0xfuL); 667 env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr)); 668 force_assert(env->eptr); 669 } 670 for (i = 0; i < env->oused; ++i) { 671 env->eptr[i] = env->ptr + env->offsets[i]; 672 } 673 env->eptr[env->oused] = NULL; 674 675 /* set up args */ 676 i = 0; 677 678 if (!buffer_string_is_empty(cgi_handler)) { 679 args[i++] = cgi_handler->ptr; 680 } 681 args[i++] = r->physical.path.ptr; 682 args[i ] = NULL; 683 } 684 685 dfd = fdevent_open_dirname(r->physical.path.ptr, r->conf.follow_symlink); 686 if (-1 == dfd) { 687 log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr); 688 } 689 690 int serrh_fd = r->conf.serrh ? r->conf.serrh->errorlog_fd : -1; 691 hctx->pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, p->env.eptr, to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd) : -1; 692 693 if (-1 == hctx->pid) { 694 /* log error with errno prior to calling close() (might change errno) */ 695 log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed"); 696 if (-1 != dfd) close(dfd); 697 close(from_cgi_fds[0]); 698 close(from_cgi_fds[1]); 699 close(to_cgi_fds[0]); 700 close(to_cgi_fds[1]); 701 return -1; 702 } else { 703 if (-1 != dfd) close(dfd); 704 close(from_cgi_fds[1]); 705 close(to_cgi_fds[0]); 706 707 hctx->fd = from_cgi_fds[0]; 708 709 cgi_pid_add(p, hctx->pid, hctx); 710 711 if (0 == r->reqbody_length) { 712 close(to_cgi_fds[1]); 713 } 714 else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1]) 715 && 0 == cgi_write_request(hctx, to_cgi_fds[1])) { 716 ++r->con->srv->cur_fds; 717 } 718 else { 719 close(to_cgi_fds[1]); 720 /*(hctx->fd not yet registered with fdevent, so manually 721 * cleanup here; see fdevent_register() further below)*/ 722 close(hctx->fd); 723 hctx->fd = -1; 724 cgi_connection_close(hctx); 725 return -1; 726 } 727 728 ++r->con->srv->cur_fds; 729 730 struct fdevents * const ev = hctx->ev; 731 hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx); 732 if (-1 == fdevent_fcntl_set_nb(hctx->fd)) { 733 log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed"); 734 cgi_connection_close(hctx); 735 return -1; 736 } 737 fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); 738 739 return 0; 740 } 741 } 742 743 URIHANDLER_FUNC(cgi_is_handled) { 744 plugin_data *p = p_d; 745 const stat_cache_st *st; 746 data_string *ds; 747 748 if (NULL != r->handler_module) return HANDLER_GO_ON; 749 if (buffer_is_empty(&r->physical.path)) return HANDLER_GO_ON; 750 751 mod_cgi_patch_config(r, p); 752 if (NULL == p->conf.cgi) return HANDLER_GO_ON; 753 754 ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path); 755 if (NULL == ds) return HANDLER_GO_ON; 756 757 st = stat_cache_path_stat(&r->physical.path); 758 if (NULL == st) return HANDLER_GO_ON; 759 760 /* (aside: CGI might be executable even if it is not readable) */ 761 if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; 762 if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; 763 764 { 765 handler_ctx *hctx = cgi_handler_ctx_init(); 766 hctx->ev = r->con->srv->ev; 767 hctx->r = r; 768 hctx->plugin_data = p; 769 hctx->cgi_handler = &ds->value; 770 memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); 771 hctx->conf.upgrade = 772 hctx->conf.upgrade 773 && r->http_version == HTTP_VERSION_1_1 774 && light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE); 775 hctx->opts.fdfmt = S_IFIFO; 776 hctx->opts.backend = BACKEND_CGI; 777 hctx->opts.authorizer = 0; 778 hctx->opts.local_redir = hctx->conf.local_redir; 779 hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow; 780 hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot; 781 hctx->opts.pdata = hctx; 782 hctx->opts.headers = cgi_response_headers; 783 r->plugin_ctx[p->id] = hctx; 784 r->handler_module = p->self; 785 } 786 787 return HANDLER_GO_ON; 788 } 789 790 __attribute_cold__ 791 __attribute_noinline__ 792 static handler_t mod_cgi_local_redir(request_st * const r) { 793 /* must be called from mod_cgi_handle_subrequest() so that HANDLER_COMEBACK 794 * return value propagates back through connection_state_machine() */ 795 http_response_reset(r); /*(includes r->http_status = 0)*/ 796 plugins_call_handle_request_reset(r); 797 /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/ 798 return HANDLER_COMEBACK; 799 } 800 801 /* 802 * - HANDLER_GO_ON : not our job 803 * - HANDLER_FINISHED: got response 804 * - HANDLER_WAIT_FOR_EVENT: waiting for response 805 */ 806 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { 807 plugin_data * const p = p_d; 808 handler_ctx * const hctx = r->plugin_ctx[p->id]; 809 if (NULL == hctx) return HANDLER_GO_ON; 810 811 if (2 == hctx->conf.local_redir) return mod_cgi_local_redir(r); 812 813 if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 814 && r->resp_body_started) { 815 if (chunkqueue_length(&r->write_queue) > 65536 - 4096) { 816 fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN); 817 } else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) { 818 /* optimistic read from backend */ 819 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 820 if (rc == HANDLER_COMEBACK) mod_cgi_local_redir(r); 821 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 822 fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN); 823 } 824 } 825 826 chunkqueue * const cq = &r->reqbody_queue; 827 828 if (cq->bytes_in != (off_t)r->reqbody_length) { 829 /*(64k - 4k to attempt to avoid temporary files 830 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 831 if (chunkqueue_length(cq) > 65536 - 4096 832 && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ 833 r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 834 if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT; 835 } else { 836 handler_t rc = r->con->reqbody_read(r); 837 if (!chunkqueue_is_empty(cq)) { 838 if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) { 839 return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc; 840 } 841 } 842 if (rc != HANDLER_GO_ON) return rc; 843 844 /* CGI environment requires that Content-Length be set. 845 * Send 411 Length Required if Content-Length missing. 846 * (occurs here if client sends Transfer-Encoding: chunked 847 * and module is flagged to stream request body to backend) */ 848 if (-1 == r->reqbody_length) { 849 return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 850 ? http_response_reqbody_read_error(r, 411) 851 : HANDLER_WAIT_FOR_EVENT; 852 } 853 } 854 } 855 856 if (-1 == hctx->fd) { 857 if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) { 858 r->http_status = 500; 859 r->handler_module = NULL; 860 861 return HANDLER_FINISHED; 862 } 863 } else if (!chunkqueue_is_empty(cq)) { 864 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 865 cgi_connection_close(hctx); 866 return HANDLER_ERROR; 867 } 868 } 869 870 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ 871 return HANDLER_WAIT_FOR_EVENT; 872 } 873 874 875 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { 876 plugin_data *p = (plugin_data *)p_d; 877 for (size_t i = 0; i < p->cgi_pid.used; ++i) { 878 handler_ctx *hctx; 879 if (pid != p->cgi_pid.ptr[i].pid) continue; 880 881 hctx = (handler_ctx *)p->cgi_pid.ptr[i].ctx; 882 if (hctx) hctx->pid = -1; 883 cgi_pid_del(p, i); 884 885 if (WIFEXITED(status)) { 886 /* (skip logging (non-zero) CGI exit; might be very noisy) */ 887 } 888 else if (WIFSIGNALED(status)) { 889 /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/ 890 if (WTERMSIG(status) != SIGTERM || NULL != hctx) { 891 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 892 log_error(errh, __FILE__, __LINE__, 893 "CGI pid %d died with signal %d", pid, WTERMSIG(status)); 894 } 895 } 896 else { 897 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 898 log_error(errh, __FILE__, __LINE__, 899 "CGI pid %d ended unexpectedly", pid); 900 } 901 902 return HANDLER_FINISHED; 903 } 904 905 return HANDLER_GO_ON; 906 } 907 908 909 int mod_cgi_plugin_init(plugin *p); 910 int mod_cgi_plugin_init(plugin *p) { 911 p->version = LIGHTTPD_VERSION_ID; 912 p->name = "cgi"; 913 914 p->handle_request_reset = cgi_connection_close_callback; 915 p->handle_subrequest_start = cgi_is_handled; 916 p->handle_subrequest = mod_cgi_handle_subrequest; 917 p->handle_waitpid = cgi_waitpid_cb; 918 p->init = mod_cgi_init; 919 p->cleanup = mod_cgi_free; 920 p->set_defaults = mod_cgi_set_defaults; 921 922 return 0; 923 } 924