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 (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 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 && !(r->resp_htags & HTTP_HEADER_UPGRADE)) { 419 chunkqueue *cq = r->reqbody_queue; 420 hctx->conf.upgrade = 0; 421 if (cq->bytes_out == (off_t)r->reqbody_length) { 422 cgi_connection_close_fdtocgi(hctx); /*(closes hctx->fdtocgi)*/ 423 } 424 } 425 426 return HANDLER_GO_ON; 427 } 428 429 430 static int cgi_recv_response(request_st * const r, handler_ctx * const hctx) { 431 switch (http_response_read(r, &hctx->opts, 432 hctx->response, hctx->fdn)) { 433 default: 434 return HANDLER_GO_ON; 435 case HANDLER_ERROR: 436 http_response_backend_error(r); 437 /* fall through */ 438 case HANDLER_FINISHED: 439 cgi_connection_close(hctx); 440 return HANDLER_FINISHED; 441 case HANDLER_COMEBACK: 442 /* flag for mod_cgi_handle_subrequest() */ 443 hctx->conf.local_redir = 2; 444 buffer_clear(hctx->response); 445 return HANDLER_COMEBACK; 446 } 447 } 448 449 450 static handler_t cgi_handle_fdevent(void *ctx, int revents) { 451 handler_ctx *hctx = ctx; 452 request_st * const r = hctx->r; 453 454 joblist_append(r->con); 455 456 if (revents & FDEVENT_IN) { 457 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 458 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 459 } 460 461 /* perhaps this issue is already handled */ 462 if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { 463 if (r->resp_body_started) { 464 /* drain any remaining data from kernel pipe buffers 465 * even if (r->conf.stream_response_body 466 * & FDEVENT_STREAM_RESPONSE_BUFMIN) 467 * since event loop will spin on fd FDEVENT_HUP event 468 * until unregistered. */ 469 handler_t rc; 470 const unsigned short flags = r->conf.stream_response_body; 471 r->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; 472 r->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; 473 do { 474 rc = cgi_recv_response(r,hctx); /*(might invalidate hctx)*/ 475 } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ 476 r->conf.stream_response_body = flags; 477 return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */ 478 } else if (!buffer_string_is_empty(hctx->response)) { 479 /* unfinished header package which is a body in reality */ 480 r->resp_body_started = 1; 481 if (0 != http_chunk_append_buffer(r, hctx->response)) { 482 cgi_connection_close(hctx); 483 return HANDLER_ERROR; 484 } 485 if (0 == r->http_status) r->http_status = 200; /* OK */ 486 } 487 cgi_connection_close(hctx); 488 } else if (revents & FDEVENT_ERR) { 489 /* kill all connections to the cgi process */ 490 cgi_connection_close(hctx); 491 return HANDLER_ERROR; 492 } 493 494 return HANDLER_FINISHED; 495 } 496 497 498 static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { 499 env_accum *env = venv; 500 char *dst; 501 502 if (!key || !val) return -1; 503 504 if (env->size - env->used < key_len + val_len + 2) { 505 if (0 == env->size) env->size = 4096; 506 do { env->size *= 2; } while (env->size - env->used < key_len + val_len + 2); 507 env->ptr = realloc(env->ptr, env->size); 508 force_assert(env->ptr); 509 } 510 511 dst = env->ptr + env->used; 512 memcpy(dst, key, key_len); 513 dst[key_len] = '='; 514 memcpy(dst + key_len + 1, val, val_len); 515 dst[key_len + 1 + val_len] = '\0'; 516 517 if (env->osize == env->oused) { 518 env->osize += 16; 519 env->offsets = realloc(env->offsets, env->osize * sizeof(*env->offsets)); 520 force_assert(env->offsets); 521 } 522 env->offsets[env->oused++] = env->used; 523 env->used += key_len + val_len + 2; 524 525 return 0; 526 } 527 528 /*(improved from network_write_mmap.c)*/ 529 static off_t mmap_align_offset(off_t start) { 530 static off_t pagemask = 0; 531 if (0 == pagemask) { 532 long pagesize = sysconf(_SC_PAGESIZE); 533 if (-1 == pagesize) pagesize = 4096; 534 pagemask = ~((off_t)pagesize - 1); /* pagesize always power-of-2 */ 535 } 536 return (start & pagemask); 537 } 538 539 /* returns: 0: continue, -1: fatal error, -2: connection reset */ 540 /* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes), 541 * also mmaps and sends complete chunk instead of only small parts - the files 542 * are supposed to be temp files with reasonable chunk sizes. 543 * 544 * Also always use mmap; the files are "trusted", as we created them. 545 */ 546 static ssize_t cgi_write_file_chunk_mmap(request_st * const r, int fd, chunkqueue *cq) { 547 chunk* const c = cq->first; 548 off_t offset, toSend, file_end; 549 ssize_t wr; 550 size_t mmap_offset, mmap_avail; 551 char *data = NULL; 552 553 force_assert(NULL != c); 554 force_assert(FILE_CHUNK == c->type); 555 force_assert(c->offset >= 0 && c->offset <= c->file.length); 556 557 offset = c->file.start + c->offset; 558 toSend = c->file.length - c->offset; 559 file_end = c->file.start + c->file.length; /* offset to file end in this chunk */ 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 /* (re)mmap the buffer if range is not covered completely */ 575 if (MAP_FAILED == c->file.mmap.start 576 || offset < c->file.mmap.offset 577 || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) { 578 579 if (MAP_FAILED != c->file.mmap.start) { 580 munmap(c->file.mmap.start, c->file.mmap.length); 581 c->file.mmap.start = MAP_FAILED; 582 } 583 584 c->file.mmap.offset = mmap_align_offset(offset); 585 c->file.mmap.length = file_end - c->file.mmap.offset; 586 587 if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) { 588 if (toSend > 65536) toSend = 65536; 589 data = malloc(toSend); 590 force_assert(data); 591 if (-1 == lseek(c->file.fd, offset, SEEK_SET) 592 || 0 >= (toSend = read(c->file.fd, data, toSend))) { 593 if (-1 == toSend) { 594 log_perror(r->conf.errh, __FILE__, __LINE__, 595 "lseek/read %s %d %lld failed:", 596 c->mem->ptr, c->file.fd, (long long)offset); 597 } else { /*(0 == toSend)*/ 598 log_error(r->conf.errh, __FILE__, __LINE__, 599 "unexpected EOF (input truncated?): %s %d %lld", 600 c->mem->ptr, c->file.fd, (long long)offset); 601 } 602 free(data); 603 return -1; 604 } 605 } 606 } 607 608 if (MAP_FAILED != c->file.mmap.start) { 609 force_assert(offset >= c->file.mmap.offset); 610 mmap_offset = offset - c->file.mmap.offset; 611 force_assert(c->file.mmap.length > mmap_offset); 612 mmap_avail = c->file.mmap.length - mmap_offset; 613 force_assert(toSend <= (off_t) mmap_avail); 614 615 data = c->file.mmap.start + mmap_offset; 616 } 617 618 wr = write(fd, data, toSend); 619 620 if (MAP_FAILED == c->file.mmap.start) free(data); 621 622 if (wr < 0) { 623 switch (errno) { 624 case EAGAIN: 625 case EINTR: 626 return 0; 627 case EPIPE: 628 case ECONNRESET: 629 return -2; 630 default: 631 log_perror(r->conf.errh, __FILE__, __LINE__, 632 "write %d failed", fd); 633 return -1; 634 } 635 } 636 637 chunkqueue_mark_written(cq, wr); 638 return wr; 639 } 640 641 static int cgi_write_request(handler_ctx *hctx, int fd) { 642 request_st * const r = hctx->r; 643 chunkqueue *cq = r->reqbody_queue; 644 chunk *c; 645 646 /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. 647 * solution: if this is still a problem on windows, then substitute 648 * socketpair() for pipe() and closesocket() for close() on windows. 649 */ 650 651 for (c = cq->first; c; c = cq->first) { 652 ssize_t wr = -1; 653 654 switch(c->type) { 655 case FILE_CHUNK: 656 wr = cgi_write_file_chunk_mmap(r, fd, cq); 657 break; 658 659 case MEM_CHUNK: 660 if ((wr = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) { 661 switch(errno) { 662 case EAGAIN: 663 case EINTR: 664 /* ignore and try again */ 665 wr = 0; 666 break; 667 case EPIPE: 668 case ECONNRESET: 669 /* connection closed */ 670 wr = -2; 671 break; 672 default: 673 /* fatal error */ 674 log_perror(r->conf.errh, __FILE__, __LINE__, "write() failed"); 675 wr = -1; 676 break; 677 } 678 } else if (wr > 0) { 679 chunkqueue_mark_written(cq, wr); 680 } 681 break; 682 } 683 684 if (0 == wr) break; /*(might block)*/ 685 686 switch (wr) { 687 case -1: 688 /* fatal error */ 689 return -1; 690 case -2: 691 /* connection reset */ 692 log_error(r->conf.errh, __FILE__, __LINE__, 693 "failed to send post data to cgi, connection closed by CGI"); 694 /* skip all remaining data */ 695 chunkqueue_mark_written(cq, chunkqueue_length(cq)); 696 break; 697 default: 698 break; 699 } 700 } 701 702 if (cq->bytes_out == (off_t)r->reqbody_length && !hctx->conf.upgrade) { 703 /* sent all request body input */ 704 /* close connection to the cgi-script */ 705 if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ 706 --r->con->srv->cur_fds; 707 if (close(fd)) { 708 log_perror(r->conf.errh, __FILE__, __LINE__, "cgi stdin close %d failed", fd); 709 } 710 } else { 711 cgi_connection_close_fdtocgi(hctx); /*(closes only hctx->fdtocgi)*/ 712 } 713 } else { 714 off_t cqlen = cq->bytes_in - cq->bytes_out; 715 if (cq->bytes_in != r->reqbody_length && cqlen < 65536 - 16384) { 716 /*(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ 717 if (!(r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { 718 r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; 719 r->con->is_readable = 1; /* trigger optimistic read from client */ 720 } 721 } 722 struct fdevents * const ev = hctx->ev; 723 if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ 724 hctx->fdtocgi = fd; 725 hctx->fdntocgi = fdevent_register(ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); 726 } 727 if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ 728 if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) { 729 fdevent_fdnode_event_set(ev, hctx->fdntocgi, 0); 730 } 731 } else { 732 /* more request body remains to be sent to CGI so register for fdevents */ 733 fdevent_fdnode_event_set(ev, hctx->fdntocgi, FDEVENT_OUT); 734 } 735 } 736 737 return 0; 738 } 739 740 static const struct stat * cgi_stat(buffer *path) { 741 /* CGI might be executable even if it is not readable */ 742 const stat_cache_entry * const sce = stat_cache_get_entry(path); 743 return sce ? &sce->st : NULL; 744 } 745 746 static int cgi_create_env(request_st * const r, plugin_data * const p, handler_ctx * const hctx, buffer * const cgi_handler) { 747 char *args[3]; 748 int to_cgi_fds[2]; 749 int from_cgi_fds[2]; 750 int dfd = -1; 751 UNUSED(p); 752 753 if (!buffer_string_is_empty(cgi_handler)) { 754 if (NULL == cgi_stat(cgi_handler)) { 755 log_perror(r->conf.errh, __FILE__, __LINE__, 756 "stat for cgi-handler %s", cgi_handler->ptr); 757 return -1; 758 } 759 } 760 761 if (pipe_cloexec(to_cgi_fds)) { 762 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 763 return -1; 764 } 765 if (pipe_cloexec(from_cgi_fds)) { 766 close(to_cgi_fds[0]); 767 close(to_cgi_fds[1]); 768 log_perror(r->conf.errh, __FILE__, __LINE__, "pipe failed"); 769 return -1; 770 } 771 772 { 773 size_t i = 0; 774 http_cgi_opts opts = { 0, 0, NULL, NULL }; 775 env_accum *env = &p->env; 776 env->used = 0; 777 env->oused = 0; 778 779 /* create environment */ 780 781 http_cgi_headers(r, &opts, cgi_env_add, env); 782 783 /* for valgrind */ 784 if (p->env.ld_preload) { 785 cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload)); 786 } 787 if (p->env.ld_library_path) { 788 cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path)); 789 } 790 #ifdef __CYGWIN__ 791 /* CYGWIN needs SYSTEMROOT */ 792 if (p->env.systemroot) { 793 cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot)); 794 } 795 #endif 796 797 if (env->esize <= env->oused) { 798 env->esize = (env->oused + 1 + 0xf) & ~(0xfuL); 799 env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr)); 800 force_assert(env->eptr); 801 } 802 for (i = 0; i < env->oused; ++i) { 803 env->eptr[i] = env->ptr + env->offsets[i]; 804 } 805 env->eptr[env->oused] = NULL; 806 807 /* set up args */ 808 i = 0; 809 810 if (!buffer_string_is_empty(cgi_handler)) { 811 args[i++] = cgi_handler->ptr; 812 } 813 args[i++] = r->physical.path.ptr; 814 args[i ] = NULL; 815 } 816 817 dfd = fdevent_open_dirname(r->physical.path.ptr, r->conf.follow_symlink); 818 if (-1 == dfd) { 819 log_perror(r->conf.errh, __FILE__, __LINE__, "open dirname %s failed", r->physical.path.ptr); 820 } 821 822 int serrh_fd = r->conf.serrh ? r->conf.serrh->errorlog_fd : -1; 823 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; 824 825 if (-1 == hctx->pid) { 826 /* log error with errno prior to calling close() (might change errno) */ 827 log_perror(r->conf.errh, __FILE__, __LINE__, "fork failed"); 828 if (-1 != dfd) close(dfd); 829 close(from_cgi_fds[0]); 830 close(from_cgi_fds[1]); 831 close(to_cgi_fds[0]); 832 close(to_cgi_fds[1]); 833 return -1; 834 } else { 835 if (-1 != dfd) close(dfd); 836 close(from_cgi_fds[1]); 837 close(to_cgi_fds[0]); 838 839 hctx->fd = from_cgi_fds[0]; 840 841 cgi_pid_add(p, hctx->pid, hctx); 842 843 ++r->con->srv->cur_fds; 844 845 if (0 == r->reqbody_length) { 846 close(to_cgi_fds[1]); 847 } else { 848 /* there is content to send */ 849 if (-1 == fdevent_fcntl_set_nb(to_cgi_fds[1])) { 850 log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed"); 851 close(to_cgi_fds[1]); 852 cgi_connection_close(hctx); 853 return -1; 854 } 855 856 if (0 != cgi_write_request(hctx, to_cgi_fds[1])) { 857 close(to_cgi_fds[1]); 858 cgi_connection_close(hctx); 859 return -1; 860 } 861 862 ++r->con->srv->cur_fds; 863 } 864 865 struct fdevents * const ev = hctx->ev; 866 hctx->fdn = fdevent_register(ev, hctx->fd, cgi_handle_fdevent, hctx); 867 if (-1 == fdevent_fcntl_set_nb(hctx->fd)) { 868 log_perror(r->conf.errh, __FILE__, __LINE__, "fcntl failed"); 869 cgi_connection_close(hctx); 870 return -1; 871 } 872 fdevent_fdnode_event_set(ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); 873 874 return 0; 875 } 876 } 877 878 URIHANDLER_FUNC(cgi_is_handled) { 879 plugin_data *p = p_d; 880 const struct stat *st; 881 data_string *ds; 882 883 if (NULL != r->handler_module) return HANDLER_GO_ON; 884 if (buffer_is_empty(&r->physical.path)) return HANDLER_GO_ON; 885 886 mod_cgi_patch_config(r, p); 887 if (NULL == p->conf.cgi) return HANDLER_GO_ON; 888 889 ds = (data_string *)array_match_key_suffix(p->conf.cgi, &r->physical.path); 890 if (NULL == ds) return HANDLER_GO_ON; 891 892 st = cgi_stat(&r->physical.path); 893 if (NULL == st) return HANDLER_GO_ON; 894 895 if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; 896 if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; 897 898 { 899 handler_ctx *hctx = cgi_handler_ctx_init(); 900 hctx->ev = r->con->srv->ev; 901 hctx->r = r; 902 hctx->plugin_data = p; 903 hctx->cgi_handler = &ds->value; 904 memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); 905 hctx->conf.upgrade = 906 hctx->conf.upgrade 907 && r->http_version == HTTP_VERSION_1_1 908 && NULL != http_header_request_get(r, HTTP_HEADER_UPGRADE, CONST_STR_LEN("Upgrade")); 909 hctx->opts.fdfmt = S_IFIFO; 910 hctx->opts.backend = BACKEND_CGI; 911 hctx->opts.authorizer = 0; 912 hctx->opts.local_redir = hctx->conf.local_redir; 913 hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow; 914 hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot; 915 hctx->opts.pdata = hctx; 916 hctx->opts.headers = cgi_response_headers; 917 r->plugin_ctx[p->id] = hctx; 918 r->handler_module = p->self; 919 } 920 921 return HANDLER_GO_ON; 922 } 923 924 __attribute_cold__ 925 __attribute_noinline__ 926 static handler_t mod_cgi_local_redir(request_st * const r) { 927 /* must be called from mod_cgi_handle_subrequest() so that HANDLER_COMEBACK 928 * return value propagates back through connection_state_machine() */ 929 http_response_reset(r); /*(includes r->http_status = 0)*/ 930 plugins_call_handle_request_reset(r); 931 /*cgi_connection_close(hctx);*//*(already cleaned up and hctx is now invalid)*/ 932 return HANDLER_COMEBACK; 933 } 934 935 /* 936 * - HANDLER_GO_ON : not our job 937 * - HANDLER_FINISHED: got response 938 * - HANDLER_WAIT_FOR_EVENT: waiting for response 939 */ 940 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { 941 plugin_data * const p = p_d; 942 handler_ctx * const hctx = r->plugin_ctx[p->id]; 943 if (NULL == hctx) return HANDLER_GO_ON; 944 945 if (2 == hctx->conf.local_redir) return mod_cgi_local_redir(r); 946 947 if ((r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) 948 && r->resp_body_started) { 949 if (chunkqueue_length(r->write_queue) > 65536 - 4096) { 950 fdevent_fdnode_event_clr(hctx->ev, hctx->fdn, FDEVENT_IN); 951 } else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) { 952 /* optimistic read from backend */ 953 handler_t rc = cgi_recv_response(r, hctx); /*(might invalidate hctx)*/ 954 if (rc == HANDLER_COMEBACK) mod_cgi_local_redir(r); 955 if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ 956 fdevent_fdnode_event_add(hctx->ev, hctx->fdn, FDEVENT_IN); 957 } 958 } 959 960 chunkqueue * const cq = r->reqbody_queue; 961 962 if (cq->bytes_in != (off_t)r->reqbody_length) { 963 /*(64k - 4k to attempt to avoid temporary files 964 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ 965 if (cq->bytes_in - cq->bytes_out > 65536 - 4096 966 && (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ 967 r->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; 968 if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT; 969 } else { 970 handler_t rc = r->con->reqbody_read(r); 971 if (!chunkqueue_is_empty(cq)) { 972 if (fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT) { 973 return (rc == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : rc; 974 } 975 } 976 if (rc != HANDLER_GO_ON) return rc; 977 978 /* CGI environment requires that Content-Length be set. 979 * Send 411 Length Required if Content-Length missing. 980 * (occurs here if client sends Transfer-Encoding: chunked 981 * and module is flagged to stream request body to backend) */ 982 if (-1 == r->reqbody_length) { 983 return (r->conf.stream_request_body & FDEVENT_STREAM_REQUEST) 984 ? http_response_reqbody_read_error(r, 411) 985 : HANDLER_WAIT_FOR_EVENT; 986 } 987 } 988 } 989 990 if (-1 == hctx->fd) { 991 if (cgi_create_env(r, p, hctx, hctx->cgi_handler)) { 992 r->http_status = 500; 993 r->handler_module = NULL; 994 995 return HANDLER_FINISHED; 996 } 997 } else if (!chunkqueue_is_empty(cq)) { 998 if (0 != cgi_write_request(hctx, hctx->fdtocgi)) { 999 cgi_connection_close(hctx); 1000 return HANDLER_ERROR; 1001 } 1002 } 1003 1004 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ 1005 return HANDLER_WAIT_FOR_EVENT; 1006 } 1007 1008 1009 static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { 1010 plugin_data *p = (plugin_data *)p_d; 1011 for (size_t i = 0; i < p->cgi_pid.used; ++i) { 1012 handler_ctx *hctx; 1013 if (pid != p->cgi_pid.ptr[i].pid) continue; 1014 1015 hctx = (handler_ctx *)p->cgi_pid.ptr[i].ctx; 1016 if (hctx) hctx->pid = -1; 1017 cgi_pid_del(p, i); 1018 1019 if (WIFEXITED(status)) { 1020 /* (skip logging (non-zero) CGI exit; might be very noisy) */ 1021 } 1022 else if (WIFSIGNALED(status)) { 1023 /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/ 1024 if (WTERMSIG(status) != SIGTERM || NULL != hctx) { 1025 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1026 log_error(errh, __FILE__, __LINE__, 1027 "CGI pid %d died with signal %d", pid, WTERMSIG(status)); 1028 } 1029 } 1030 else { 1031 log_error_st *errh = hctx ? hctx->r->conf.errh : srv->errh; 1032 log_error(errh, __FILE__, __LINE__, 1033 "CGI pid %d ended unexpectedly", pid); 1034 } 1035 1036 return HANDLER_FINISHED; 1037 } 1038 1039 return HANDLER_GO_ON; 1040 } 1041 1042 1043 int mod_cgi_plugin_init(plugin *p); 1044 int mod_cgi_plugin_init(plugin *p) { 1045 p->version = LIGHTTPD_VERSION_ID; 1046 p->name = "cgi"; 1047 1048 p->handle_request_reset = cgi_connection_close_callback; 1049 p->handle_subrequest_start = cgi_is_handled; 1050 p->handle_subrequest = mod_cgi_handle_subrequest; 1051 p->handle_waitpid = cgi_waitpid_cb; 1052 p->init = mod_cgi_init; 1053 p->cleanup = mod_cgi_free; 1054 p->set_defaults = mod_cgi_set_defaults; 1055 1056 return 0; 1057 } 1058