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