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 __attribute_fallthrough__ 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 && val_len)) 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 #ifndef SPLICE_F_NONBLOCK 530 /*(improved from network_write_mmap.c)*/ 531 static off_t mmap_align_offset(off_t start) { 532 static off_t pagemask = 0; 533 if (0 == pagemask) { 534 long pagesize = sysconf(_SC_PAGESIZE); 535 if (-1 == pagesize) pagesize = 4096; 536 pagemask = ~((off_t)pagesize - 1); /* pagesize always power-of-2 */ 537 } 538 return (start & pagemask); 539 } 540 #endif 541 542 /* returns: 0: continue, -1: fatal error, -2: connection reset */ 543 /* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes), 544 * also mmaps and sends complete chunk instead of only small parts - the files 545 * are supposed to be temp files with reasonable chunk sizes. 546 * 547 * Also always use mmap; the files are "trusted", as we created them. 548 */ 549 static ssize_t cgi_write_file_chunk_mmap(request_st * const r, int fd, chunkqueue *cq) { 550 chunk* const c = cq->first; 551 off_t offset, toSend; 552 ssize_t wr; 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->offset; 559 toSend = c->file.length - c->offset; 560 561 if (0 == toSend) { 562 chunkqueue_remove_finished_chunks(cq); 563 return 0; 564 } 565 566 /*(simplified from chunk.c:chunkqueue_open_file_chunk())*/ 567 if (-1 == c->file.fd) { 568 if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, r->conf.follow_symlink, O_RDONLY, 0))) { 569 log_perror(r->conf.errh, __FILE__, __LINE__, "open failed: %s", c->mem->ptr); 570 return -1; 571 } 572 } 573 574 #ifdef SPLICE_F_NONBLOCK 575 loff_t abs_offset = offset; 576 wr = splice(c->file.fd, &abs_offset, fd, NULL, 577 (size_t)toSend, SPLICE_F_NONBLOCK); 578 #else 579 size_t mmap_offset, mmap_avail; 580 char *data = NULL; 581 off_t file_end = c->file.length; /* offset to file end in this chunk */ 582 583 /* (re)mmap the buffer if range is not covered completely */ 584 if (MAP_FAILED == c->file.mmap.start 585 || offset < c->file.mmap.offset 586 || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) { 587 588 if (MAP_FAILED != c->file.mmap.start) { 589 munmap(c->file.mmap.start, c->file.mmap.length); 590 c->file.mmap.start = MAP_FAILED; 591 } 592 593 c->file.mmap.offset = mmap_align_offset(offset); 594 c->file.mmap.length = file_end - c->file.mmap.offset; 595 596 if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) { 597 if (toSend > 65536) toSend = 65536; 598 data = malloc(toSend); 599 force_assert(data); 600 if (-1 == lseek(c->file.fd, offset, SEEK_SET) 601 || 0 >= (toSend = read(c->file.fd, data, toSend))) { 602 if (-1 == toSend) { 603 log_perror(r->conf.errh, __FILE__, __LINE__, 604 "lseek/read %s %d %lld failed:", 605 c->mem->ptr, c->file.fd, (long long)offset); 606 } else { /*(0 == toSend)*/ 607 log_error(r->conf.errh, __FILE__, __LINE__, 608 "unexpected EOF (input truncated?): %s %d %lld", 609 c->mem->ptr, c->file.fd, (long long)offset); 610 } 611 free(data); 612 return -1; 613 } 614 } 615 } 616 617 if (MAP_FAILED != c->file.mmap.start) { 618 force_assert(offset >= c->file.mmap.offset); 619 mmap_offset = offset - c->file.mmap.offset; 620 force_assert(c->file.mmap.length > mmap_offset); 621 mmap_avail = c->file.mmap.length - mmap_offset; 622 force_assert(toSend <= (off_t) mmap_avail); 623 624 data = c->file.mmap.start + mmap_offset; 625 } 626 627 wr = write(fd, data, toSend); 628 629 if (MAP_FAILED == c->file.mmap.start) free(data); 630 #endif 631 632 if (wr < 0) { 633 switch (errno) { 634 case EAGAIN: 635 case EINTR: 636 return 0; 637 case EPIPE: 638 case ECONNRESET: 639 return -2; 640 default: 641 log_perror(r->conf.errh, __FILE__, __LINE__, 642 "write %d failed", fd); 643 return -1; 644 } 645 } 646 647 chunkqueue_mark_written(cq, wr); 648 return wr; 649 } 650 651 static int cgi_write_request(handler_ctx *hctx, int fd) { 652 request_st * const r = hctx->r; 653 chunkqueue *cq = &r->reqbody_queue; 654 chunk *c; 655 656 /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. 657 * solution: if this is still a problem on windows, then substitute 658 * socketpair() for pipe() and closesocket() for close() on windows. 659 */ 660 661 for (c = cq->first; c; c = cq->first) { 662 ssize_t wr = -1; 663 664 switch(c->type) { 665 case FILE_CHUNK: 666 wr = cgi_write_file_chunk_mmap(r, fd, cq); 667 break; 668 669 case MEM_CHUNK: 670 if ((wr = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) { 671 switch(errno) { 672 case EAGAIN: 673 case EINTR: 674 /* ignore and try again */ 675 wr = 0; 676 break; 677 case EPIPE: 678 case ECONNRESET: 679 /* connection closed */ 680 wr = -2; 681 break; 682 default: 683 /* fatal error */ 684 log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed"); 685 wr = -1; 686 break; 687 } 688 } else if (wr > 0) { 689 chunkqueue_mark_written(cq, wr); 690 } 691 break; 692 } 693 694 if (0 == wr) break; /*(might block)*/ 695 696 switch (wr) { 697 case -1: 698 /* fatal error */ 699 return -1; 700 case -2: 701 /* connection reset */ 702 log_error(r->conf.errh, __FILE__, __LINE__, 703 "failed to send post data to cgi, connection closed by CGI"); 704 /* skip all remaining data */ 705 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 706 break; 707 default: 708 break; 709 } 710 } 711 712 if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) { 713 /* sent all request body input */ 714 /* close connection to the cgi-script */ 715 if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ 716 --r->con->srv->cur_fds; 717 if (close(fd)) { 718 log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd); 719 } 720 } else { 721 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 722 } 723 } else { 724 off_t cqlen = chunkqueue_length(cq); 725 if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) { 726 /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 727 if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { 728 r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; 729 r->con->is_readable = 1; /* trigger optimistic read from client */ 730 } 731 } 732 struct fdevents * const ev = hctx->ev; 733 if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ 734 hctx->fdtocgi = fd; 735 hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); 736 } 737 if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ 738 if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) { 739 fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0); 740 } 741 } else { 742 /* more request body remains to be sent to CGI so register for fdevents */ 743 fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT); 744 } 745 } 746 747 return 0; 748 } 749 750 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) { 751 char *args[3]; 752 int to_cgi_fds[2]; 753 int from_cgi_fds[2]; 754 int dfd = -1; 755 UNUSED(p); 756 757 if (!buffer_string_is_empty(cgi_handler)) { 758 if (NULL == stat_cache_path_stat(cgi_handler)) { 759 log_perror(r->conf.errh, __FILE__, __LINE__, 760 "stat for cgi-handler %s", cgi_handler->ptr); 761 return -1; 762 } 763 } 764 765 if (pipe_cloexec(to_cgi_fds)) { 766 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 767 return -1; 768 } 769 if (pipe_cloexec(from_cgi_fds)) { 770 close(to_cgi_fds[0]); 771 close(to_cgi_fds[1]); 772 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 773 return -1; 774 } 775 776 { 777 size_t i = 0; 778 http_cgi_opts opts = { 0, 0, NULL, NULL }; 779 env_accum *env = &p->env; 780 env->used = 0; 781 env->oused = 0; 782 783 /* create environment */ 784 785 http_cgi_headers(r, &opts, cgi_env_add, env); 786 787 /* for valgrind */ 788 if (p->env.ld_preload) { 789 cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload)); 790 } 791 if (p->env.ld_library_path) { 792 cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path)); 793 } 794 #ifdef __CYGWIN__ 795 /* CYGWIN needs SYSTEMROOT */ 796 if (p->env.systemroot) { 797 cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot)); 798 } 799 #endif 800 801 if (env->esize <= env->oused) { 802 env->esize = (env->oused + 1 + 0xf) & ~(0xfuL); 803 env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr)); 804 force_assert(env->eptr); 805 } 806 for (i = 0; i < env->oused; ++i) { 807 env->eptr[i] = env->ptr + env->offsets[i]; 808 } 809 env->eptr[env->oused] = NULL; 810 811 /* set up args */ 812 i = 0; 813 814 if (!buffer_string_is_empty(cgi_handler)) { 815 args[i++] = cgi_handler->ptr; 816 } 817 args[i++] = r->physical.path.ptr; 818 args[i ] = NULL; 819 } 820 821 dfd = fdevent_open_dirname(r->physical.path.ptr, r->conf.follow_symlink); 822 if (-1 == dfd) { 823 log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr); 824 } 825 826 int serrh_fd = r->conf.serrh ? r->conf.serrh->errorlog_fd : -1; 827 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; 828 829 if (-1 == hctx->pid) { 830 /* log error with errno prior to calling close() (might change errno) */ 831 log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed"); 832 if (-1 != dfd) close(dfd); 833 close(from_cgi_fds[0]); 834 close(from_cgi_fds[1]); 835 close(to_cgi_fds[0]); 836 close(to_cgi_fds[1]); 837 return -1; 838 } else { 839 if (-1 != dfd) close(dfd); 840 close(from_cgi_fds[1]); 841 close(to_cgi_fds[0]); 842 843 hctx->fd = from_cgi_fds[0]; 844 845 cgi_pid_add(p, hctx->pid, hctx); 846 847 if (0 == r->reqbody_length) { 848 close(to_cgi_fds[1]); 849 } 850 else if (0 == fdevent_fcntl_set_nb(to_cgi_fds[1]) 851 && 0 == cgi_write_request(hctx, to_cgi_fds[1])) { 852 ++r->con->srv->cur_fds; 853 } 854 else { 855 close(to_cgi_fds[1]); 856 /*(hctx->fd not yet registered with fdevent, so manually 857 * cleanup here; see fdevent_register() further below)*/ 858 close(hctx->fd); 859 hctx->fd = -1; 860 cgi_connection_close(hctx); 861 return -1; 862 } 863 864 ++r->con->srv->cur_fds; 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 stat_cache_st *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 = stat_cache_path_stat(&r->physical.path); 894 if (NULL == st) return HANDLER_GO_ON; 895 896 /* (aside: CGI might be executable even if it is not readable) */ 897 if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; 898 if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; 899 900 { 901 handler_ctx *hctx = cgi_handler_ctx_init(); 902 hctx->ev = r->con->srv->ev; 903 hctx->r = r; 904 hctx->plugin_data = p; 905 hctx->cgi_handler = &ds->value; 906 memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); 907 hctx->conf.upgrade = 908 hctx->conf.upgrade 909 && r->http_version == HTTP_VERSION_1_1 910 && light_btst(r->rqst_htags, HTTP_HEADER_UPGRADE); 911 hctx->opts.fdfmt = S_IFIFO; 912 hctx->opts.backend = BACKEND_CGI; 913 hctx->opts.authorizer = 0; 914 hctx->opts.local_redir = hctx->conf.local_redir; 915 hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow; 916 hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot; 917 hctx->opts.pdata = hctx; 918 hctx->opts.headers = cgi_response_headers; 919 r->plugin_ctx[p->id] = hctx; 920 r->handler_module = p->self; 921 } 922 923 return HANDLER_GO_ON; 924 } 925 926 __attribute_cold__ 927 __attribute_noinline__ 928 static handler_t mod_cgi_local_redir(request_st * const r) { 929 /* must be called from mod_cgi_handle_subrequest() so that HANDLER_COMEBACK 930 * return value propagates back through connection_state_machine() */ 931 http_response_reset(r); /*(includes r->http_status = 0)*/ 932 plugins_call_handle_request_reset(r); 933 /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/ 934 return HANDLER_COMEBACK; 935 } 936 937 /* 938 * - HANDLER_GO_ON : not our job 939 * - HANDLER_FINISHED: got response 940 * - HANDLER_WAIT_FOR_EVENT: waiting for response 941 */ 942 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { 943 plugin_data * const p = p_d; 944 handler_ctx * const hctx = r->plugin_ctx[p->id]; 945 if (NULL == hctx) return HANDLER_GO_ON; 946 947 if (2 == hctx->conf.local_redir) return mod_cgi_local_redir(r); 948 949 if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 950 && r->resp_body_started) { 951 if (chunkqueue_length(&r->write_queue) > 65536 - 4096) { 952 fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN); 953 } else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) { 954 /* optimistic read from backend */ 955 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 956 if (rc == HANDLER_COMEBACK) mod_cgi_local_redir(r); 957 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 958 fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN); 959 } 960 } 961 962 chunkqueue * const cq = &r->reqbody_queue; 963 964 if (cq->bytes_in != (off_t)r->reqbody_length) { 965 /*(64k - 4k to attempt to avoid temporary files 966 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 967 if (chunkqueue_length(cq) > 65536 - 4096 968 && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ 969 r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 970 if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT; 971 } else { 972 handler_t rc = r->con->reqbody_read(r); 973 if (!chunkqueue_is_empty(cq)) { 974 if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) { 975 return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc; 976 } 977 } 978 if (rc != HANDLER_GO_ON) return rc; 979 980 /* CGI environment requires that Content-Length be set. 981 * Send 411 Length Required if Content-Length missing. 982 * (occurs here if client sends Transfer-Encoding: chunked 983 * and module is flagged to stream request body to backend) */ 984 if (-1 == r->reqbody_length) { 985 return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 986 ? http_response_reqbody_read_error(r, 411) 987 : HANDLER_WAIT_FOR_EVENT; 988 } 989 } 990 } 991 992 if (-1 == hctx->fd) { 993 if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) { 994 r->http_status = 500; 995 r->handler_module = NULL; 996 997 return HANDLER_FINISHED; 998 } 999 } else if (!chunkqueue_is_empty(cq)) { 1000 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 1001 cgi_connection_close(hctx); 1002 return HANDLER_ERROR; 1003 } 1004 } 1005 1006 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ 1007 return HANDLER_WAIT_FOR_EVENT; 1008 } 1009 1010 1011 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { 1012 plugin_data *p = (plugin_data *)p_d; 1013 for (size_t i = 0; i < p->cgi_pid.used; ++i) { 1014 handler_ctx *hctx; 1015 if (pid != p->cgi_pid.ptr[i].pid) continue; 1016 1017 hctx = (handler_ctx *)p->cgi_pid.ptr[i].ctx; 1018 if (hctx) hctx->pid = -1; 1019 cgi_pid_del(p, i); 1020 1021 if (WIFEXITED(status)) { 1022 /* (skip logging (non-zero) CGI exit; might be very noisy) */ 1023 } 1024 else if (WIFSIGNALED(status)) { 1025 /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/ 1026 if (WTERMSIG(status) != SIGTERM || NULL != hctx) { 1027 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1028 log_error(errh, __FILE__, __LINE__, 1029 "CGI pid %d died with signal %d", pid, WTERMSIG(status)); 1030 } 1031 } 1032 else { 1033 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1034 log_error(errh, __FILE__, __LINE__, 1035 "CGI pid %d ended unexpectedly", pid); 1036 } 1037 1038 return HANDLER_FINISHED; 1039 } 1040 1041 return HANDLER_GO_ON; 1042 } 1043 1044 1045 int mod_cgi_plugin_init(plugin *p); 1046 int mod_cgi_plugin_init(plugin *p) { 1047 p->version = LIGHTTPD_VERSION_ID; 1048 p->name = "cgi"; 1049 1050 p->handle_request_reset = cgi_connection_close_callback; 1051 p->handle_subrequest_start = cgi_is_handled; 1052 p->handle_subrequest = mod_cgi_handle_subrequest; 1053 p->handle_waitpid = cgi_waitpid_cb; 1054 p->init = mod_cgi_init; 1055 p->cleanup = mod_cgi_free; 1056 p->set_defaults = mod_cgi_set_defaults; 1057 1058 return 0; 1059 } 1060