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