UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2016 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "cgi.h" 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 35 #include <sys/types.h> 36 #include <signal.h> 37 #include <sys/wait.h> 38 39 #include <cx/string.h> 40 41 #include "../util/util.h" 42 #include "../util/pblock.h" 43 #include "../daemon/netsite.h" 44 #include "../daemon/vfs.h" 45 #include "../util/io.h" 46 #include "../daemon/event.h" 47 48 #include "cgiutils.h" 49 50 #define CGI_VARS 32 51 52 #define CGI_RESPONSE_PARSER_BUFLEN 2048 53 #define CGI_RESPONSE_MAX_LINE_LENGTH 512 54 55 int send_cgi(pblock *pb, Session *sn, Request *rq) { 56 char *path = pblock_findkeyval(pb_key_path, rq->vars); 57 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers); 58 int64_t content_length = 0; 59 60 log_ereport(LOG_DEBUG, "cgi-send: path: %s req: %p content-length: %s", path, rq, ctlen); 61 62 if(ctlen) { 63 if(!util_strtoint(ctlen, &content_length)) { 64 log_ereport( 65 LOG_FAILURE, 66 "send-cgi: content-length header is not an integer"); 67 protocol_status(sn, rq, 400, NULL); 68 return REQ_ABORTED; 69 } 70 } 71 72 // using stat, not vfs_stat, because running scripts/executables works only 73 // with the sys fs 74 if(!vfs_is_sys(rq->vfs)) { 75 log_ereport(LOG_WARN, "send-cgi: VFS setting ignored"); 76 } 77 78 struct stat s; 79 if(stat(path, &s)) { 80 int statuscode = util_errno2status(errno); 81 protocol_status(sn, rq, statuscode, NULL); 82 return REQ_ABORTED; 83 } 84 if(S_ISDIR(s.st_mode)) { 85 protocol_status(sn, rq, 403, NULL); 86 return REQ_ABORTED; 87 } 88 89 param_free(pblock_remove("content-type", rq->srvhdrs)); 90 91 const char *args = pblock_findval("query", rq->reqpb); 92 char **argv = cgi_create_argv(path, NULL, args); 93 if(!argv) { 94 return REQ_ABORTED; 95 } 96 97 char **env = http_hdrs2env(rq->headers); 98 env = cgi_common_vars(sn, rq, env); 99 env = cgi_specific_vars(sn, rq, args, env, 1); 100 101 // event handler object for non-blocking io event handler 102 CGIHandler *handler = pool_malloc(sn->pool, sizeof(CGIHandler)); 103 if(!handler) { 104 return REQ_ABORTED; 105 } 106 ZERO(handler, sizeof(CGIHandler)); 107 handler->path = path; 108 109 int ret = cgi_start(&handler->process, path, argv, env); 110 if(ret != REQ_PROCEED) { 111 util_env_free(env); 112 cgi_free_argv(argv); 113 return ret; 114 } 115 log_ereport(LOG_DEBUG, "send-cgi: req: %p pid: %d", rq, (int)handler->process.pid); 116 117 util_env_free(env); 118 cgi_free_argv(argv); 119 120 char buf[4096]; // I/O buffer 121 ssize_t r; 122 123 if(content_length > 0) { 124 ssize_t n = 0; 125 while(n < content_length) { 126 r = netbuf_getbytes(sn->inbuf, buf, 4096); 127 if(r <= 0) { 128 log_ereport( 129 LOG_FAILURE, 130 "send-cgi: script: %s: cannot read request body", 131 path); 132 kill(handler->process.pid, SIGTERM); 133 cgi_close(&handler->process); 134 return REQ_ABORTED; 135 } 136 ssize_t w = write(handler->process.in[1], buf, r); 137 if(w <= 0) { 138 log_ereport( 139 LOG_FAILURE, 140 "send-cgi: script: %s: cannot send request body to cgi process", 141 path); 142 kill(handler->process.pid, SIGTERM); 143 cgi_close(&handler->process); 144 return REQ_ABORTED; 145 } 146 n += r; 147 } 148 } 149 system_close(handler->process.in[1]); 150 handler->process.in[1] = -1; 151 152 handler->parser = cgi_parser_new(sn, rq); 153 154 // set pipes non-blocking 155 int flags; 156 if ((flags = fcntl(handler->process.err[0], F_GETFL, 0)) == -1) { 157 flags = 0; 158 } 159 if (fcntl(handler->process.err[0], F_SETFL, flags | O_NONBLOCK) != 0) { 160 log_ereport(LOG_FAILURE, "cgi-bin: fcntl err[0] failed: %s", strerror(errno)); 161 } 162 if ((flags = fcntl(handler->process.out[0], F_GETFL, 0)) == -1) { 163 flags = 0; 164 } 165 if (fcntl(handler->process.out[0], F_SETFL, flags | O_NONBLOCK) != 0) { 166 log_ereport(LOG_FAILURE, "cgi-bin: fcntl out[0] failed: %s", strerror(errno)); 167 } 168 169 // create events for reading cgi's stdout/stderr 170 Event *readev = pool_malloc(sn->pool, sizeof(Event)); 171 ZERO(readev, sizeof(Event)); 172 readev->cookie = handler; 173 readev->fn = cgi_stdout_readevent; 174 readev->finish = cgi_event_finish; 175 176 Event *stderr_readev = pool_malloc(sn->pool, sizeof(Event)); 177 ZERO(stderr_readev, sizeof(Event)); 178 stderr_readev->cookie = handler; 179 stderr_readev->fn = cgi_stderr_readevent; 180 stderr_readev->finish = cgi_event_finish; 181 182 Event *writeev = pool_malloc(sn->pool, sizeof(Event)); 183 ZERO(writeev, sizeof(Event)); 184 writeev->cookie = handler; 185 writeev->fn = cgi_writeevent; 186 writeev->finish = cgi_event_finish; 187 188 handler->readev = readev; 189 handler->writeev = writeev; 190 191 net_setnonblock(sn->csd, 1); 192 193 // add poll events for cgi stdout/stderr and netout 194 int error = 0; 195 if(ev_pollin(sn->ev, handler->process.err[0], stderr_readev)) { 196 log_ereport(LOG_FAILURE, "send-cgi: stderr ev_pollin failed"); 197 error = 1; 198 } else { 199 handler->wait_read = TRUE; 200 handler->events++; 201 } 202 if(!error && ev_pollin(sn->ev, handler->process.out[0], readev)) { 203 log_ereport(LOG_FAILURE, "send-cgi: stdout ev_pollin failed"); 204 error = 1; 205 } else { 206 handler->events++; 207 } 208 209 // don't poll sn->csd yet, we wait until the first net_write fails 210 211 if(error) { 212 log_ereport(LOG_FAILURE, "cgi-send: initialization error: kill script: %s", path); 213 kill(handler->process.pid, SIGKILL); 214 cgi_parser_free(handler->parser); 215 cgi_close(&handler->process); 216 return REQ_ABORTED; 217 } 218 219 return REQ_PROCESSING; 220 } 221 222 /* 223 * Try to flush the CGIHandler write buffer 224 * 225 * When successful, cgi_try_write_flush() returns 0. If an error occurs, 226 * 1 is returned. 227 * 228 * If the error is not EAGAIN, handler->result is set to REQ_ABORTED. 229 */ 230 static int cgi_try_write_flush(CGIHandler *handler, Session *sn) { 231 ssize_t wr = 0; 232 while( 233 handler->writebuf_size - handler->writebuf_pos > 0 && 234 (wr = net_write( 235 sn->csd, 236 handler->writebuf + handler->writebuf_pos, 237 handler->writebuf_size - handler->writebuf_pos)) 238 > 0) 239 { 240 handler->writebuf_pos += wr; 241 handler->count_write += wr; 242 } 243 if(handler->writebuf_size - handler->writebuf_pos > 0) { 244 if(net_errno(sn->csd) != EAGAIN) { 245 handler->result = REQ_ABORTED; 246 log_ereport( 247 LOG_FAILURE, 248 "cgi pid %d %s: network error: %s", 249 (int)handler->process.pid, 250 handler->path, 251 strerror(net_errno(sn->csd))); 252 } 253 254 return 1; 255 } 256 return 0; 257 } 258 259 /* 260 * Try to write the buffer to sn->csd 261 * In case the socket is non-blocking and not all bytes could be written, 262 * the remaining bytes are copied to the CGIHandler write buffer. 263 * 264 * If an error occurs that is not EAGAIN, handler->result is set to 265 * REQ_ABORTED. 266 * 267 * Returns 0 if all bytes are successfully written, otherwise 1 268 */ 269 static int cgi_try_write(CGIHandler *handler, EventHandler *ev, Session *sn, char *buf, size_t size) { 270 271 size_t pos = 0; 272 ssize_t wr = 0; 273 while(size - pos > 0 && (wr = net_write(sn->csd, buf + pos, size - pos)) > 0) { 274 pos += wr; 275 handler->count_write += wr; 276 } 277 278 if(pos < size) { 279 if(net_errno(sn->csd) == EAGAIN) { 280 // copy remaining bytes to the write buffer 281 // we assume there are no remaining bytes in writebuf 282 size_t remaining = size-pos; 283 if(remaining > handler->writebuf_alloc) { 284 handler->writebuf_alloc = size > 4096 ? size : 4096; 285 handler->writebuf = pool_realloc(sn->pool, handler->writebuf, handler->writebuf_alloc); 286 if(!handler->writebuf) { 287 handler->result = REQ_ABORTED; 288 return 1; 289 } 290 } 291 memcpy(handler->writebuf, buf+pos, remaining); 292 handler->writebuf_size = remaining; 293 handler->writebuf_pos = 0; 294 } else { 295 handler->result = REQ_ABORTED; 296 log_ereport( 297 LOG_FAILURE, 298 "cgi pid %d %s: network error: %s", 299 (int)handler->process.pid, 300 handler->path, 301 strerror(net_errno(sn->csd))); 302 } 303 return 1; 304 } 305 306 return 0; 307 } 308 309 int cgi_stdout_readevent(EventHandler *ev, Event *event) { 310 CGIHandler *handler = event->cookie; 311 312 if(handler->debug_finished) { 313 log_ereport(LOG_DEBUG, "cgi-send: req: %p debug_finished: 1 cgi_stdout_readevent events: %d", handler->parser->rq, handler->events); 314 } 315 316 if(handler->cgi_eof || handler->result == REQ_ABORTED) { 317 // cgi_eof will be set to true by cgi_read_output 318 // if it is true here, the cgi handling was finished by cgi_writeevent 319 // in that case, cgi_writeevent will finish the request processing 320 // and nothing needs to be done here 321 log_ereport(LOG_DEBUG, "cgi-send: req: %p readevent cgi_eof = TRUE result: %d", handler->parser->rq, handler->result); 322 handler->wait_read = FALSE; 323 event->finish = NULL; 324 return 0; 325 } 326 327 event->finish = cgi_event_finish; 328 handler->writeev->finish = NULL; // TODO: maybe this can be removed 329 CgiIOResult ret = cgi_read_output(handler, ev, "readevent"); 330 switch(ret) { 331 case CGI_IO_COMPLETE: { 332 break; 333 } 334 case CGI_IO_NEED_READ: { 335 return 1; 336 } 337 case CGI_IO_NEED_WRITE: { 338 // writeev is only enabled, if needed 339 if(handler->poll_out) { 340 return 1; 341 } 342 if(event_pollout(ev, handler->parser->sn->csd, handler->writeev)) { 343 handler->result = REQ_ABORTED; 344 } else { 345 handler->poll_out = TRUE; 346 log_ereport(LOG_DEBUG, "cgi-send: req: %p enable poll out", handler->parser->rq); 347 return 1; // keep readevent active 348 } 349 } 350 case CGI_IO_ERROR: { 351 break; 352 } 353 } 354 355 handler->wait_read = FALSE; 356 return 0; 357 } 358 359 int cgi_writeevent(EventHandler *ev, Event *event) { 360 CGIHandler *handler = event->cookie; 361 362 if(handler->cgi_eof || handler->result == REQ_ABORTED) { 363 // same as in cgi_stdout_readevent 364 // request processing will be finished by the read event 365 log_ereport(LOG_DEBUG, "cgi-send: req: %p writeevent cgi_eof = TRUE result: %d", handler->parser->rq, handler->result); 366 handler->poll_out = FALSE; 367 event->finish = NULL; 368 return 0; 369 } 370 371 event->finish = cgi_event_finish; 372 handler->readev->finish = NULL; // TODO: maybe this can be removed 373 CgiIOResult ret = cgi_read_output(handler, ev, "writeevent"); 374 switch(ret) { 375 case CGI_IO_COMPLETE: { 376 break; 377 } 378 case CGI_IO_NEED_READ: { 379 return 1; 380 } 381 case CGI_IO_NEED_WRITE: { 382 return 1; 383 } 384 case CGI_IO_ERROR: { 385 break; 386 } 387 } 388 389 handler->poll_out = FALSE; 390 return 0; 391 } 392 393 394 395 CgiIOResult cgi_read_output(CGIHandler *handler, EventHandler *ev, const char *debug_log) { 396 CGIResponseParser *parser = handler->parser; 397 Session *sn = parser->sn; 398 Request *rq = parser->rq; 399 400 if(handler->result == REQ_ABORTED) { 401 return CGI_IO_ERROR; 402 } 403 404 // try to flush handler->writebuf 405 // if writebuf is empty, this does nothing and returns 0 406 if(cgi_try_write_flush(handler, sn)) { 407 if(handler->result == REQ_ABORTED) { 408 return CGI_IO_ERROR; 409 } else { 410 return CGI_IO_NEED_WRITE; 411 } 412 } 413 414 char buf[4096]; // I/O buffer 415 ssize_t r; 416 417 int ret = CGI_IO_COMPLETE; 418 handler->result = REQ_PROCEED; 419 while((r = read(handler->process.out[0], buf, 4096)) > 0) { 420 if(parser->cgiheader) { 421 size_t pos; 422 int ret = cgi_parse_response(parser, buf, r, &pos); 423 if(ret == -1) { 424 log_ereport( 425 LOG_FAILURE, 426 "broken cgi script response: path: %s", handler->path); 427 protocol_status(sn, rq, 500, NULL); 428 handler->result = REQ_ABORTED; 429 return CGI_IO_ERROR; 430 } else if(ret == 1) { 431 WS_ASSERT(pos <= r); 432 433 parser->response_length += r-pos; 434 435 parser->cgiheader = FALSE; 436 if(parser->status > 0) { 437 protocol_status(sn, rq, parser->status, parser->msg); 438 } 439 440 handler->response = http_create_response(sn, rq); 441 if(!handler->response) { 442 handler->result = REQ_ABORTED; 443 return CGI_IO_ERROR; 444 } 445 446 int send_response = http_send_response(handler->response); 447 if(send_response < 0) { 448 handler->result = REQ_ABORTED; 449 ret = CGI_IO_ERROR; 450 break; 451 } else if(send_response == 1) { 452 // EAGAIN 453 if(!handler->poll_out) { 454 if(event_pollout(ev, sn->csd, handler->writeev)) { 455 handler->result = REQ_ABORTED; 456 return CGI_IO_ERROR; 457 } 458 handler->poll_out = TRUE; 459 return CGI_IO_NEED_WRITE; 460 } 461 } else { 462 handler->response = NULL; 463 } 464 465 if(pos < r) { 466 if(cgi_try_write(handler, ev, sn, &buf[pos], r-pos)) { 467 return handler->result == REQ_ABORTED ? CGI_IO_ERROR : CGI_IO_NEED_WRITE; 468 } 469 } 470 } 471 } else { 472 parser->response_length += r; 473 if(cgi_try_write(handler, ev, sn, buf, r)) { 474 return handler->result == REQ_ABORTED ? CGI_IO_ERROR : CGI_IO_NEED_WRITE; 475 } 476 } 477 } 478 if(r < 0 && errno == EAGAIN) { 479 return CGI_IO_NEED_READ; 480 } 481 handler->cgi_eof = TRUE; 482 log_ereport(LOG_DEBUG, "cgi-send: req: %p pid: %d set cgi_eof : %s", rq, handler->process.pid, debug_log); 483 return ret; 484 } 485 486 int cgi_stderr_readevent(EventHandler *ev, Event *event) { 487 CGIHandler *handler = event->cookie; 488 pool_handle_t *pool = handler->parser->sn->pool; 489 490 char buf[4096]; 491 char *line = buf; 492 int line_start; 493 ssize_t r; 494 while((r = read(handler->process.err[0], buf, 4096)) > 0) { 495 line_start = 0; 496 int pos = 0; 497 // log stderr output lines 498 for(int i=0;i<r;i++) { 499 if(buf[i] == '\n') { 500 log_ereport( 501 LOG_INFORM, 502 "cgi pid %d %s stderr: %.*s%.*s", 503 (int)handler->process.pid, 504 handler->path, 505 (int)handler->stderr_tmplen, 506 handler->stderr_tmp, 507 i - line_start, 508 line + line_start); 509 line_start = i+1; 510 pos = i+1; 511 512 if(handler->stderr_tmp) { 513 handler->stderr_tmplen = 0; 514 } 515 } 516 } 517 518 // check for incomplete line 519 if(pos < r) { 520 int tmplen = r-pos; 521 if(handler->stderr_tmplen > 0) { 522 // append new text to the temp buffer 523 if(handler->stderr_tmplen + tmplen > handler->stderr_tmpalloc) { 524 handler->stderr_tmpalloc = handler->stderr_tmplen + tmplen; 525 handler->stderr_tmp = pool_realloc(pool, handler->stderr_tmp, handler->stderr_tmpalloc); 526 if(!handler->stderr_tmp) { 527 log_ereport(LOG_FAILURE, "send-cgi: cannot create tmp buffer for parsing stderr"); 528 handler->stderr_tmpalloc = 0; 529 handler->stderr_tmplen = 0; 530 continue; 531 } 532 } 533 memcpy(handler->stderr_tmp + handler->stderr_tmplen, line + line_start, tmplen); 534 handler->stderr_tmplen += tmplen; 535 } else { 536 if(handler->stderr_tmpalloc < tmplen) { 537 // tmp buffer too small or not allocated 538 handler->stderr_tmpalloc = tmplen < 256 ? 256 : tmplen; 539 if(handler->stderr_tmp) { 540 // free old tmp buf 541 // pool_realloc doesn't make sense here, because it 542 // is just free+malloc+memcpy and we don't need the 543 // memcpy part, because we are just reusing the buffer 544 // and the previous content doesn't matter 545 pool_free(pool, handler->stderr_tmp); 546 } 547 handler->stderr_tmp = pool_malloc(pool, handler->stderr_tmpalloc); 548 if(!handler->stderr_tmp) { 549 log_ereport(LOG_FAILURE, "send-cgi: cannot create tmp buffer for parsing stderr"); 550 handler->stderr_tmpalloc = 0; 551 handler->stderr_tmplen = 0; 552 continue; 553 } 554 } 555 memcpy(handler->stderr_tmp, line + line_start, tmplen); 556 handler->stderr_tmplen = tmplen; 557 } 558 } else { 559 handler->stderr_tmplen = 0; 560 } 561 } 562 563 564 if(r < 0 && errno == EAGAIN) { 565 return 1; 566 } 567 568 if(handler->stderr_tmp) { 569 pool_free(handler->parser->sn->pool, handler->stderr_tmp); 570 } 571 return 0; 572 } 573 574 int cgi_event_finish(EventHandler *ev, Event *event) { 575 CGIHandler *handler = event->cookie; 576 CGIResponseParser *parser = handler->parser; 577 Session *sn = parser->sn; 578 Request *rq = parser->rq; 579 580 char *event_fn = ""; 581 if(event->fn == cgi_stdout_readevent) { 582 event_fn = "stdout"; 583 } else if(event->fn == cgi_stderr_readevent) { 584 event_fn = "stderr"; 585 } else if(event->fn == cgi_writeevent) { 586 event_fn = "httpout"; 587 log_ereport(LOG_DEBUG, "cgi-send: req: %p finish: pid: %d", rq, handler->process.pid); 588 } 589 log_ereport(LOG_DEBUG, "cgi-send: req: %p finish: event: %d pollout: %d wait_read: %d cgi_eof: %d fn: %s", rq, handler->events, handler->poll_out, handler->wait_read, handler->cgi_eof, event_fn); 590 591 handler->debug_finished = TRUE; 592 if(event->fn != cgi_stderr_readevent) { 593 log_ereport(LOG_DEBUG, "cgi-send: req: %p finish set cgi_eof: %s", rq, event_fn); 594 handler->cgi_eof = TRUE; 595 } 596 597 if(handler->result == REQ_ABORTED && handler->process.pid != 0 && handler->cgi_kill == 0) { 598 log_ereport(LOG_FAILURE, "cgi-send: kill script: %s pid: %d", handler->path, (int)handler->process.pid); 599 if(kill(handler->process.pid, SIGTERM)) { 600 log_ereport(LOG_FAILURE, "cgi-send: pid: %d kill failed: %s", (int)handler->process.pid, strerror(errno)); 601 } else { 602 log_ereport(LOG_DEBUG, "cgi-send: finish: req: %p kill %d successful", rq, (int)handler->process.pid); 603 handler->cgi_kill = SIGTERM; 604 } 605 } 606 607 if(--handler->events > 0) { 608 return 0; 609 } 610 611 if(handler->poll_out) { 612 // write event registered, however it will not be activated anymore 613 // we can safely remove the event 614 log_ereport(LOG_DEBUG, "cgi-send: req: %p finish: remove-poll write", rq); 615 if(event_removepoll(ev, sn->csd)) { 616 log_ereport(LOG_FAILURE, "cgi_event_finish: event_removepoll: %s", strerror(errno)); 617 } 618 handler->poll_out = FALSE; 619 } 620 621 if(handler->wait_read) { 622 // read event registered, however it will not be activated anymore 623 // (currently unsure if this can happen) 624 log_ereport(LOG_DEBUG, "cgi-send: req: %p finish: remove-poll read", rq); 625 if(ev_remove_poll(ev, handler->process.out[0])) { 626 log_ereport(LOG_FAILURE, "cgi_event_finish: req: %p ev_remove_poll: %s", rq, strerror(errno)); 627 } 628 handler->wait_read = FALSE; 629 } 630 631 log_ereport(LOG_DEBUG, "cgi-send: req: %p cgi_close", rq); 632 633 int exit_code = cgi_close(&handler->process); 634 if(exit_code != 0) { 635 log_ereport(LOG_FAILURE, "send-cgi: script: %s exited with code %d", handler->path, exit_code); 636 handler->result = REQ_ABORTED; 637 } 638 639 cgi_parser_free(parser); 640 641 WSBool response_length_error = FALSE; 642 // check if content-length set by the cgi script matches the number 643 // of writes, that were written to the stream 644 // this ensures, that broken cgi scripts don't break the connection 645 char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); 646 if(ctlen_header) { 647 int64_t ctlenhdr; 648 if(util_strtoint(ctlen_header, &ctlenhdr)) { 649 if(ctlenhdr != parser->response_length) { 650 log_ereport( 651 LOG_FAILURE, 652 "cgi-send: script: %s: content length mismatch", 653 handler->path); 654 response_length_error = TRUE; 655 } 656 } 657 } 658 // make sure we haven't lost any bytes 659 // should not happen unless the non-blocking IO code is buggy 660 if(handler->result != REQ_ABORTED && handler->parser->response_length != handler->count_write) { 661 log_ereport( 662 LOG_FAILURE, 663 "cgi-send: script: %s: IO error: cgi response length != http response length", 664 handler->path); 665 response_length_error = TRUE; 666 } 667 668 // if the response length is broken, we must close the connection 669 if(response_length_error) { 670 rq->rq_attr.keep_alive = 0; 671 handler->result = REQ_ABORTED; 672 } 673 674 net_setnonblock(sn->csd, 0); 675 676 // return to nsapi loop 677 log_ereport(LOG_DEBUG, "cgi-send: req: %p event-finish nsapi return", rq); 678 nsapi_function_return(sn, rq, handler->result); 679 return 0; 680 } 681 682 int cgi_start(CGIProcess *p, char *path, char *const argv[], char *const envp[]) { 683 if(pipe(p->in) || pipe(p->out) || pipe(p->err)) { 684 log_ereport( 685 LOG_FAILURE, 686 "send-cgi: cannot create pipe: %s", 687 strerror(errno)); 688 return REQ_ABORTED; 689 } 690 691 p->pid = fork(); 692 if(p->pid == 0) { 693 // child 694 695 // get script directory and script name 696 cxstring script = cx_str(path); 697 cxmutstr parent; 698 int len = strlen(path); 699 for(int i=len-1;i>=0;i--) { 700 if(path[i] == '/') { 701 script = cx_strn(path + i + 1, len - i); 702 parent = cx_strdup(cx_strn(path, i)); 703 if(chdir(parent.ptr)) { 704 perror("cgi_start: chdir"); 705 free(parent.ptr); 706 exit(-1); 707 } 708 free(parent.ptr); 709 break; 710 } 711 } 712 713 if(dup2(p->in[0], STDIN_FILENO) == -1) { 714 perror("cgi_start: dup2"); 715 exit(EXIT_FAILURE); 716 } 717 if(dup2(p->out[1], STDOUT_FILENO) == -1) { 718 perror("cgi_start: dup2"); 719 exit(EXIT_FAILURE); 720 } 721 if(dup2(p->err[1], STDERR_FILENO) == -1) { 722 perror("cgi_start: dup2"); 723 exit(EXIT_FAILURE); 724 } 725 726 // we need to close this unused pipe 727 // otherwise stdin cannot reach EOF 728 system_close(p->in[1]); 729 730 // execute program 731 exit(execve(script.ptr, argv, envp)); 732 } else { 733 // parent 734 system_close(p->out[1]); 735 system_close(p->err[1]); 736 p->out[1] = -1; 737 p->err[1] = -1; 738 } 739 740 return REQ_PROCEED; 741 } 742 743 int cgi_close(CGIProcess *p) { 744 if(p->in[0] != -1) { 745 system_close(p->in[0]); 746 } 747 if(p->in[1] != -1) { 748 system_close(p->in[1]); 749 } 750 if(p->out[0] != -1) { 751 system_close(p->out[0]); 752 } 753 if(p->out[1] != -1) { 754 system_close(p->out[1]); 755 } 756 if(p->err[0] != -1) { 757 system_close(p->err[0]); 758 } 759 if(p->err[1] != -1) { 760 system_close(p->err[1]); 761 } 762 763 // TODO: Because of WNOHANG, waitpid could fail and the process 764 // is still running. In that case, another waitpid call should 765 // be done later somewhere. 766 int status = -1; 767 if(waitpid(p->pid, &status, WNOHANG) == 0) { 768 log_ereport(LOG_DEBUG, "cgi_close: waitpid returned 0: pid: %d", (int)p->pid); 769 // cgi process still running 770 // workaround: sleep 1 sec and try again, if that fails again 771 sleep(1); 772 if(waitpid(p->pid, &status, WNOHANG) == 0) { 773 log_ereport(LOG_DEBUG, "cgi_close: waitpid returned 0 again: pid: %d", (int)p->pid); 774 } 775 } 776 777 return status; 778 } 779 780 CGIResponseParser* cgi_parser_new(Session *sn, Request *rq) { 781 CGIResponseParser* parser = pool_malloc(sn->pool, sizeof(CGIResponseParser)); 782 parser->sn = sn; 783 parser->rq = rq; 784 parser->status = 0; 785 parser->msg = NULL; 786 parser->response_length = 0; 787 parser->cgiheader = TRUE; 788 cxBufferInit(&parser->tmp, NULL, 64, pool_allocator(sn->pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); 789 return parser; 790 } 791 792 void cgi_parser_free(CGIResponseParser *parser) { 793 if(parser->tmp.space) { 794 cxBufferDestroy(&parser->tmp); 795 } 796 pool_free(parser->sn->pool, parser); 797 } 798 799 /* 800 * parses a cgi response line and adds the response header to rq->srvhdrs 801 * returns 0: incomplete line 802 * 1: successfully parsed lines 803 * 2: cgi response header complete (empty line) 804 * -1: error 805 */ 806 static int parse_lines(CGIResponseParser *parser, char *buf, size_t len, int *pos) { 807 CxAllocator *a = pool_allocator(parser->sn->pool); 808 cxmutstr name; 809 cxmutstr value; 810 WSBool space = TRUE; 811 int i; 812 813 int line_begin = 0; 814 int value_begin = 0; 815 for(i=0;i<len;i++) { 816 char c = buf[i]; 817 if(value_begin == line_begin && c == ':') { 818 name = cx_mutstrn(buf + line_begin, i - line_begin); 819 value_begin = i + 1; 820 } else if(c == '\n') { 821 if(value_begin == line_begin) { 822 if(space) { 823 *pos = i + 1; 824 return 2; 825 } else { 826 // line ends with content but without ':' -> error 827 return -1; 828 } 829 } 830 value = cx_mutstrn(buf + value_begin, i - value_begin); 831 832 cx_strlower(name); 833 name = cx_strdup_a(a, cx_strtrim((cxstring){name.ptr, name.length})); 834 value = cx_strtrim_m(value); 835 836 if(name.length == 0 || value.length == 0) { 837 return -1; 838 } 839 840 if(!cx_strcmp((cxstring){name.ptr, name.length}, (cxstring)CX_STR("status"))) { 841 cxmutstr codestr = value; 842 int j; 843 for(j=0;j<codestr.length;j++) { 844 if(!isdigit(codestr.ptr[j])) { 845 break; 846 } 847 if(j > 2) { 848 break; 849 } 850 } 851 codestr.ptr[j] = '\0'; 852 853 int64_t s = 0; 854 util_strtoint(codestr.ptr, &s); 855 parser->status = (int)s; 856 857 cxmutstr msg = cx_strtrim_m(cx_strsubs_m(value, j + 1)); 858 859 if(msg.length > 0) { 860 parser->msg = cx_strdup_pool(parser->sn->pool, msg).ptr; 861 } 862 } else { 863 pblock_nvlinsert( 864 name.ptr, 865 name.length, 866 value.ptr, 867 value.length, 868 parser->rq->srvhdrs); 869 } 870 871 line_begin = i+1; 872 value_begin = line_begin; 873 space = TRUE; 874 } else if(!isspace(c)) { 875 space = FALSE; 876 } 877 } 878 879 if(i < len) { 880 *pos = i; 881 return 0; 882 } 883 return 1; 884 } 885 886 /* 887 * returns -1: error 888 * 0: response header incomplete 889 * 1: complete 890 */ 891 int cgi_parse_response(CGIResponseParser *parser, char *buf, size_t len, size_t *bpos) { 892 *bpos = 0; 893 int pos = 0; 894 if(parser->tmp.pos > 0) { 895 // the tmp buffer contains an unfinished line 896 // fill up the buffer until the line is complete 897 WSBool nb = FALSE; 898 for(pos=0;pos<len;pos++) { 899 if(buf[pos] == '\n') { 900 nb = TRUE; 901 break; 902 } 903 } 904 cxBufferWrite(buf, 1, pos, &parser->tmp); 905 906 if(nb) { 907 // line complete 908 int npos; 909 int r = parse_lines(parser, parser->tmp.space, parser->tmp.pos, &npos); 910 switch(r) { 911 case -1: return -1; 912 case 0: return -1; 913 case 1: break; 914 case 2: { 915 *bpos = pos + 1; 916 return 1; 917 } 918 } 919 // reset tmp buffer 920 parser->tmp.pos = 0; 921 } else { 922 if(parser->tmp.pos > CGI_RESPONSE_MAX_LINE_LENGTH) { 923 return -1; 924 } 925 } 926 } 927 928 int npos = 0; 929 int r = parse_lines(parser, buf + pos, len - pos, &npos); 930 switch(r) { 931 default: return -1; 932 case 0: 933 case 1: { 934 int newlen = len - npos; 935 if(npos > 0) { 936 cxBufferWrite(buf + npos, 1, newlen, &parser->tmp); 937 } 938 return 0; 939 } 940 case 2: { 941 *bpos = pos + npos; 942 return 1; 943 } 944 } 945 } 946