Annotation of embedaddon/lighttpd/src/mod_cgi.c, revision 1.1.1.3
1.1.1.3 ! misho 1: #include "first.h"
! 2:
1.1 misho 3: #include "server.h"
4: #include "stat_cache.h"
5: #include "keyvalue.h"
6: #include "log.h"
7: #include "connections.h"
8: #include "joblist.h"
1.1.1.3 ! misho 9: #include "response.h"
1.1 misho 10: #include "http_chunk.h"
1.1.1.3 ! misho 11: #include "network_backends.h"
1.1 misho 12:
13: #include "plugin.h"
14:
15: #include <sys/types.h>
1.1.1.3 ! misho 16: #include "sys-mmap.h"
1.1 misho 17:
18: #ifdef __WIN32
19: # include <winsock2.h>
20: #else
21: # include <sys/socket.h>
22: # include <sys/wait.h>
23: # include <netinet/in.h>
24: # include <arpa/inet.h>
25: #endif
26:
27: #include <unistd.h>
28: #include <errno.h>
29: #include <stdlib.h>
30: #include <string.h>
31: #include <fdevent.h>
32: #include <signal.h>
33: #include <ctype.h>
34: #include <assert.h>
35:
36: #include <stdio.h>
37: #include <fcntl.h>
38:
39: enum {EOL_UNSET, EOL_N, EOL_RN};
40:
41: typedef struct {
42: char **ptr;
43:
44: size_t size;
45: size_t used;
46: } char_array;
47:
48: typedef struct {
49: pid_t *ptr;
50: size_t used;
51: size_t size;
52: } buffer_pid_t;
53:
54: typedef struct {
55: array *cgi;
56: unsigned short execute_x_only;
1.1.1.3 ! misho 57: unsigned short xsendfile_allow;
! 58: array *xsendfile_docroot;
1.1 misho 59: } plugin_config;
60:
61: typedef struct {
62: PLUGIN_DATA;
63: buffer_pid_t cgi_pid;
64:
65: buffer *tmp_buf;
66: buffer *parse_response;
67:
68: plugin_config **config_storage;
69:
70: plugin_config conf;
71: } plugin_data;
72:
73: typedef struct {
74: pid_t pid;
75: int fd;
1.1.1.3 ! misho 76: int fdtocgi;
1.1 misho 77: int fde_ndx; /* index into the fd-event buffer */
1.1.1.3 ! misho 78: int fde_ndx_tocgi; /* index into the fd-event buffer */
1.1 misho 79:
80: connection *remote_conn; /* dumb pointer */
81: plugin_data *plugin_data; /* dumb pointer */
82:
83: buffer *response;
84: buffer *response_header;
85: } handler_ctx;
86:
87: static handler_ctx * cgi_handler_ctx_init(void) {
88: handler_ctx *hctx = calloc(1, sizeof(*hctx));
89:
1.1.1.2 misho 90: force_assert(hctx);
1.1 misho 91:
92: hctx->response = buffer_init();
93: hctx->response_header = buffer_init();
1.1.1.3 ! misho 94: hctx->fd = -1;
! 95: hctx->fdtocgi = -1;
1.1 misho 96:
97: return hctx;
98: }
99:
100: static void cgi_handler_ctx_free(handler_ctx *hctx) {
101: buffer_free(hctx->response);
102: buffer_free(hctx->response_header);
103:
104: free(hctx);
105: }
106:
1.1.1.3 ! misho 107: enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_COMEBACK, FDEVENT_HANDLED_ERROR};
1.1 misho 108:
109: INIT_FUNC(mod_cgi_init) {
110: plugin_data *p;
111:
112: p = calloc(1, sizeof(*p));
113:
1.1.1.2 misho 114: force_assert(p);
1.1 misho 115:
116: p->tmp_buf = buffer_init();
117: p->parse_response = buffer_init();
118:
119: return p;
120: }
121:
122:
123: FREE_FUNC(mod_cgi_free) {
124: plugin_data *p = p_d;
125: buffer_pid_t *r = &(p->cgi_pid);
126:
127: UNUSED(srv);
128:
129: if (p->config_storage) {
130: size_t i;
131: for (i = 0; i < srv->config_context->used; i++) {
132: plugin_config *s = p->config_storage[i];
133:
1.1.1.3 ! misho 134: if (NULL == s) continue;
! 135:
1.1 misho 136: array_free(s->cgi);
1.1.1.3 ! misho 137: array_free(s->xsendfile_docroot);
1.1 misho 138:
139: free(s);
140: }
141: free(p->config_storage);
142: }
143:
144:
145: if (r->ptr) free(r->ptr);
146:
147: buffer_free(p->tmp_buf);
148: buffer_free(p->parse_response);
149:
150: free(p);
151:
152: return HANDLER_GO_ON;
153: }
154:
155: SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
156: plugin_data *p = p_d;
157: size_t i = 0;
158:
159: config_values_t cv[] = {
160: { "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
161: { "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1.1.1.3 ! misho 162: { "cgi.x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
! 163: { "cgi.x-sendfile-docroot", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
1.1 misho 164: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
165: };
166:
167: if (!p) return HANDLER_ERROR;
168:
1.1.1.2 misho 169: p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1.1.3 ! misho 170: force_assert(p->config_storage);
1.1 misho 171:
172: for (i = 0; i < srv->config_context->used; i++) {
1.1.1.3 ! misho 173: data_config const* config = (data_config const*)srv->config_context->data[i];
1.1 misho 174: plugin_config *s;
175:
176: s = calloc(1, sizeof(plugin_config));
1.1.1.2 misho 177: force_assert(s);
1.1 misho 178:
179: s->cgi = array_init();
180: s->execute_x_only = 0;
1.1.1.3 ! misho 181: s->xsendfile_allow= 0;
! 182: s->xsendfile_docroot = array_init();
1.1 misho 183:
184: cv[0].destination = s->cgi;
185: cv[1].destination = &(s->execute_x_only);
1.1.1.3 ! misho 186: cv[2].destination = &(s->xsendfile_allow);
! 187: cv[3].destination = s->xsendfile_docroot;
1.1 misho 188:
189: p->config_storage[i] = s;
190:
1.1.1.3 ! misho 191: if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
1.1 misho 192: return HANDLER_ERROR;
193: }
1.1.1.3 ! misho 194:
! 195: if (s->xsendfile_docroot->used) {
! 196: size_t j;
! 197: for (j = 0; j < s->xsendfile_docroot->used; ++j) {
! 198: data_string *ds = (data_string *)s->xsendfile_docroot->data[j];
! 199: if (ds->type != TYPE_STRING) {
! 200: log_error_write(srv, __FILE__, __LINE__, "s",
! 201: "unexpected type for key cgi.x-sendfile-docroot; expected: cgi.x-sendfile-docroot = ( \"/allowed/path\", ... )");
! 202: return HANDLER_ERROR;
! 203: }
! 204: if (ds->value->ptr[0] != '/') {
! 205: log_error_write(srv, __FILE__, __LINE__, "SBs",
! 206: "cgi.x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
! 207: return HANDLER_ERROR;
! 208: }
! 209: buffer_path_simplify(ds->value, ds->value);
! 210: buffer_append_slash(ds->value);
! 211: }
! 212: }
1.1 misho 213: }
214:
215: return HANDLER_GO_ON;
216: }
217:
218:
219: static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
220: int m = -1;
221: size_t i;
222: buffer_pid_t *r = &(p->cgi_pid);
223:
224: UNUSED(srv);
225:
226: for (i = 0; i < r->used; i++) {
227: if (r->ptr[i] > m) m = r->ptr[i];
228: }
229:
230: if (r->size == 0) {
231: r->size = 16;
232: r->ptr = malloc(sizeof(*r->ptr) * r->size);
1.1.1.3 ! misho 233: force_assert(r->ptr);
1.1 misho 234: } else if (r->used == r->size) {
235: r->size += 16;
236: r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
1.1.1.3 ! misho 237: force_assert(r->ptr);
1.1 misho 238: }
239:
240: r->ptr[r->used++] = pid;
241:
242: return m;
243: }
244:
245: static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
246: size_t i;
247: buffer_pid_t *r = &(p->cgi_pid);
248:
249: UNUSED(srv);
250:
251: for (i = 0; i < r->used; i++) {
252: if (r->ptr[i] == pid) break;
253: }
254:
255: if (i != r->used) {
256: /* found */
257:
258: if (i != r->used - 1) {
259: r->ptr[i] = r->ptr[r->used - 1];
260: }
261: r->used--;
262: }
263:
264: return 0;
265: }
266:
267: static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
268: char *ns;
269: const char *s;
270: int line = 0;
271:
272: UNUSED(srv);
273:
1.1.1.3 ! misho 274: buffer_copy_buffer(p->parse_response, in);
1.1 misho 275:
276: for (s = p->parse_response->ptr;
277: NULL != (ns = strchr(s, '\n'));
278: s = ns + 1, line++) {
279: const char *key, *value;
280: int key_len;
281: data_string *ds;
282:
283: /* strip the \n */
284: ns[0] = '\0';
285:
286: if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
287:
288: if (line == 0 &&
289: 0 == strncmp(s, "HTTP/1.", 7)) {
290: /* non-parsed header ... we parse them anyway */
291:
292: if ((s[7] == '1' ||
293: s[7] == '0') &&
294: s[8] == ' ') {
295: int status;
296: /* after the space should be a status code for us */
297:
298: status = strtol(s+9, NULL, 10);
299:
300: if (status >= 100 &&
301: status < 1000) {
302: /* we expected 3 digits and didn't got them */
303: con->parsed_response |= HTTP_STATUS;
304: con->http_status = status;
305: }
306: }
307: } else {
308: /* parse the headers */
309: key = s;
310: if (NULL == (value = strchr(s, ':'))) {
311: /* we expect: "<key>: <value>\r\n" */
312: continue;
313: }
314:
315: key_len = value - key;
316: value += 1;
317:
318: /* skip LWS */
319: while (*value == ' ' || *value == '\t') value++;
320:
321: if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
322: ds = data_response_init();
323: }
324: buffer_copy_string_len(ds->key, key, key_len);
325: buffer_copy_string(ds->value, value);
326:
327: array_insert_unique(con->response.headers, (data_unset *)ds);
328:
329: switch(key_len) {
330: case 4:
331: if (0 == strncasecmp(key, "Date", key_len)) {
332: con->parsed_response |= HTTP_DATE;
333: }
334: break;
335: case 6:
336: if (0 == strncasecmp(key, "Status", key_len)) {
1.1.1.3 ! misho 337: int status = strtol(value, NULL, 10);
! 338: if (status >= 100 && status < 1000) {
! 339: con->http_status = status;
! 340: con->parsed_response |= HTTP_STATUS;
! 341: } else {
! 342: con->http_status = 502;
! 343: }
1.1 misho 344: }
345: break;
346: case 8:
347: if (0 == strncasecmp(key, "Location", key_len)) {
348: con->parsed_response |= HTTP_LOCATION;
349: }
350: break;
351: case 10:
352: if (0 == strncasecmp(key, "Connection", key_len)) {
353: con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
354: con->parsed_response |= HTTP_CONNECTION;
355: }
356: break;
357: case 14:
358: if (0 == strncasecmp(key, "Content-Length", key_len)) {
1.1.1.3 ! misho 359: con->response.content_length = strtoul(value, NULL, 10);
1.1 misho 360: con->parsed_response |= HTTP_CONTENT_LENGTH;
361: }
362: break;
363: default:
364: break;
365: }
366: }
367: }
368:
369: /* CGI/1.1 rev 03 - 7.2.1.2 */
370: if ((con->parsed_response & HTTP_LOCATION) &&
371: !(con->parsed_response & HTTP_STATUS)) {
372: con->http_status = 302;
373: }
374:
375: return 0;
376: }
377:
378:
379: static int cgi_demux_response(server *srv, handler_ctx *hctx) {
380: plugin_data *p = hctx->plugin_data;
381: connection *con = hctx->remote_conn;
382:
383: while(1) {
384: int n;
385: int toread;
386:
387: #if defined(__WIN32)
1.1.1.3 ! misho 388: buffer_string_prepare_copy(hctx->response, 4 * 1024);
1.1 misho 389: #else
1.1.1.3 ! misho 390: if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) {
! 391: buffer_string_prepare_copy(hctx->response, 4 * 1024);
1.1 misho 392: } else {
393: if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
1.1.1.3 ! misho 394: buffer_string_prepare_copy(hctx->response, toread);
1.1 misho 395: }
396: #endif
397:
398: if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
399: if (errno == EAGAIN || errno == EINTR) {
400: /* would block, wait for signal */
1.1.1.3 ! misho 401: fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1.1 misho 402: return FDEVENT_HANDLED_NOT_FINISHED;
403: }
404: /* error */
405: log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
406: return FDEVENT_HANDLED_ERROR;
407: }
408:
409: if (n == 0) {
410: /* read finished */
411: return FDEVENT_HANDLED_FINISHED;
412: }
413:
1.1.1.3 ! misho 414: buffer_commit(hctx->response, n);
1.1 misho 415:
416: /* split header from body */
417:
418: if (con->file_started == 0) {
419: int is_header = 0;
420: int is_header_end = 0;
421: size_t last_eol = 0;
1.1.1.3 ! misho 422: size_t i, header_len;
1.1 misho 423:
424: buffer_append_string_buffer(hctx->response_header, hctx->response);
425:
426: /**
427: * we have to handle a few cases:
428: *
429: * nph:
430: *
431: * HTTP/1.0 200 Ok\n
432: * Header: Value\n
433: * \n
434: *
435: * CGI:
436: * Header: Value\n
437: * Status: 200\n
438: * \n
439: *
440: * and different mixes of \n and \r\n combinations
441: *
442: * Some users also forget about CGI and just send a response and hope
443: * we handle it. No headers, no header-content seperator
444: *
445: */
446:
447: /* nph (non-parsed headers) */
448: if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
1.1.1.3 ! misho 449:
! 450: header_len = buffer_string_length(hctx->response_header);
! 451: for (i = 0; !is_header_end && i < header_len; i++) {
1.1 misho 452: char c = hctx->response_header->ptr[i];
453:
454: switch (c) {
455: case ':':
456: /* we found a colon
457: *
458: * looks like we have a normal header
459: */
460: is_header = 1;
461: break;
462: case '\n':
463: /* EOL */
464: if (is_header == 0) {
465: /* we got a EOL but we don't seem to got a HTTP header */
466:
467: is_header_end = 1;
468:
469: break;
470: }
471:
472: /**
473: * check if we saw a \n(\r)?\n sequence
474: */
475: if (last_eol > 0 &&
476: ((i - last_eol == 1) ||
477: (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
478: is_header_end = 1;
479: break;
480: }
481:
482: last_eol = i;
483:
484: break;
485: }
486: }
487:
488: if (is_header_end) {
489: if (!is_header) {
490: /* no header, but a body */
1.1.1.3 ! misho 491: if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) {
! 492: return FDEVENT_HANDLED_ERROR;
1.1 misho 493: }
494: } else {
495: const char *bstart;
496: size_t blen;
1.1.1.3 ! misho 497:
! 498: /* the body starts after the EOL */
! 499: bstart = hctx->response_header->ptr + i;
! 500: blen = header_len - i;
! 501:
1.1 misho 502: /**
503: * i still points to the char after the terminating EOL EOL
504: *
505: * put it on the last \n again
506: */
507: i--;
1.1.1.3 ! misho 508:
1.1 misho 509: /* string the last \r?\n */
510: if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
511: i--;
512: }
513:
1.1.1.3 ! misho 514: buffer_string_set_length(hctx->response_header, i);
! 515:
1.1 misho 516: /* parse the response header */
517: cgi_response_parse(srv, con, p, hctx->response_header);
518:
1.1.1.3 ! misho 519: if (con->http_status >= 300 && con->http_status < 400) {
! 520: /*(con->parsed_response & HTTP_LOCATION)*/
! 521: data_string *ds;
! 522: if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Location"))
! 523: && ds->value->ptr[0] == '/') {
! 524: if (++con->loops_per_request > 5) {
! 525: log_error_write(srv, __FILE__, __LINE__, "sb", "too many internal loops while processing request:", con->request.orig_uri);
! 526: con->http_status = 500; /* Internal Server Error */
! 527: con->mode = DIRECT;
! 528: return FDEVENT_HANDLED_FINISHED;
! 529: }
! 530:
! 531: buffer_copy_buffer(con->request.uri, ds->value);
! 532:
! 533: if (con->request.content_length) {
! 534: if ((off_t)con->request.content_length != chunkqueue_length(con->request_content_queue)) {
! 535: con->keep_alive = 0;
! 536: }
! 537: con->request.content_length = 0;
! 538: chunkqueue_reset(con->request_content_queue);
! 539: }
! 540:
! 541: if (con->http_status != 307 && con->http_status != 308) {
! 542: /* Note: request body (if any) sent to initial dynamic handler
! 543: * and is not available to the internal redirect */
! 544: con->request.http_method = HTTP_METHOD_GET;
! 545: }
! 546:
! 547: connection_response_reset(srv, con); /*(includes con->http_status = 0)*/
! 548:
! 549: con->mode = DIRECT;
! 550: return FDEVENT_HANDLED_COMEBACK;
! 551: }
! 552: }
! 553:
! 554: if (p->conf.xsendfile_allow) {
! 555: data_string *ds;
! 556: if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) {
! 557: http_response_xsendfile(srv, con, ds->value, p->conf.xsendfile_docroot);
! 558: return FDEVENT_HANDLED_FINISHED;
! 559: }
1.1 misho 560: }
561:
562: if (blen > 0) {
1.1.1.3 ! misho 563: if (0 != http_chunk_append_mem(srv, con, bstart, blen)) {
! 564: return FDEVENT_HANDLED_ERROR;
! 565: }
1.1 misho 566: }
567: }
568:
569: con->file_started = 1;
1.1.1.3 ! misho 570: } else {
! 571: /*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/
! 572: if (header_len > MAX_HTTP_REQUEST_HEADER) {
! 573: log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path);
! 574: con->http_status = 502; /* Bad Gateway */
! 575: con->mode = DIRECT;
! 576: return FDEVENT_HANDLED_FINISHED;
! 577: }
1.1 misho 578: }
579: } else {
1.1.1.3 ! misho 580: if (0 != http_chunk_append_buffer(srv, con, hctx->response)) {
! 581: return FDEVENT_HANDLED_ERROR;
! 582: }
! 583: if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
! 584: && chunkqueue_length(con->write_queue) > 65536 - 4096) {
! 585: if (!con->is_writable) {
! 586: /*(defer removal of FDEVENT_IN interest since
! 587: * connection_state_machine() might be able to send data
! 588: * immediately, unless !con->is_writable, where
! 589: * connection_state_machine() might not loop back to call
! 590: * mod_cgi_handle_subrequest())*/
! 591: fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
! 592: }
! 593: break;
! 594: }
1.1 misho 595: }
596:
597: #if 0
598: log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
599: #endif
600: }
601:
602: return FDEVENT_HANDLED_NOT_FINISHED;
603: }
604:
1.1.1.3 ! misho 605: static void cgi_connection_close_fdtocgi(server *srv, handler_ctx *hctx) {
! 606: /*(closes only hctx->fdtocgi)*/
! 607: fdevent_event_del(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi);
! 608: fdevent_unregister(srv->ev, hctx->fdtocgi);
1.1 misho 609:
1.1.1.3 ! misho 610: if (close(hctx->fdtocgi)) {
! 611: log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", hctx->fdtocgi, strerror(errno));
! 612: }
! 613: hctx->fdtocgi = -1;
! 614: }
1.1 misho 615:
1.1.1.3 ! misho 616: static void cgi_connection_close(server *srv, handler_ctx *hctx) {
! 617: int status;
! 618: pid_t pid;
! 619: plugin_data *p = hctx->plugin_data;
! 620: connection *con = hctx->remote_conn;
1.1 misho 621:
622: #ifndef __WIN32
623:
624: /* the connection to the browser went away, but we still have a connection
625: * to the CGI script
626: *
627: * close cgi-connection
628: */
629:
630: if (hctx->fd != -1) {
631: /* close connection to the cgi-script */
632: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
633: fdevent_unregister(srv->ev, hctx->fd);
634:
635: if (close(hctx->fd)) {
636: log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
637: }
1.1.1.3 ! misho 638: }
1.1 misho 639:
1.1.1.3 ! misho 640: if (hctx->fdtocgi != -1) {
! 641: cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
1.1 misho 642: }
643:
644: pid = hctx->pid;
645:
646: con->plugin_ctx[p->id] = NULL;
647:
648: cgi_handler_ctx_free(hctx);
649:
650: /* if waitpid hasn't been called by response.c yet, do it here */
651: if (pid) {
652: /* check if the CGI-script is already gone */
653: switch(waitpid(pid, &status, WNOHANG)) {
654: case 0:
655: /* not finished yet */
656: #if 0
657: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
658: #endif
659: break;
660: case -1:
661: /* */
662: if (errno == EINTR) break;
663:
664: /*
665: * errno == ECHILD happens if _subrequest catches the process-status before
666: * we have read the response of the cgi process
667: *
668: * -> catch status
669: * -> WAIT_FOR_EVENT
670: * -> read response
671: * -> we get here with waitpid == ECHILD
672: *
673: */
1.1.1.3 ! misho 674: if (errno != ECHILD) {
! 675: log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1.1 misho 676: }
1.1.1.3 ! misho 677: /* anyway: don't wait for it anymore */
! 678: pid = 0;
! 679: break;
! 680: default:
1.1 misho 681: if (WIFEXITED(status)) {
682: #if 0
683: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
684: #endif
685: } else {
686: log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
687: }
688:
1.1.1.3 ! misho 689: pid = 0;
! 690: break;
! 691: }
1.1 misho 692:
1.1.1.3 ! misho 693: if (pid) {
! 694: kill(pid, SIGTERM);
1.1 misho 695:
1.1.1.3 ! misho 696: /* cgi-script is still alive, queue the PID for removal */
! 697: cgi_pid_add(srv, p, pid);
! 698: }
1.1 misho 699: }
700: #endif
1.1.1.3 ! misho 701:
! 702: /* finish response (if not already con->file_started, con->file_finished) */
! 703: if (con->mode == p->id) {
! 704: http_response_backend_done(srv, con);
! 705: }
1.1 misho 706: }
707:
708: static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
709: plugin_data *p = p_d;
1.1.1.3 ! misho 710: handler_ctx *hctx = con->plugin_ctx[p->id];
! 711: if (hctx) cgi_connection_close(srv, hctx);
1.1 misho 712:
1.1.1.3 ! misho 713: return HANDLER_GO_ON;
1.1 misho 714: }
715:
716:
1.1.1.3 ! misho 717: static int cgi_write_request(server *srv, handler_ctx *hctx, int fd);
! 718:
! 719:
! 720: static handler_t cgi_handle_fdevent_send (server *srv, void *ctx, int revents) {
1.1 misho 721: handler_ctx *hctx = ctx;
722: connection *con = hctx->remote_conn;
723:
1.1.1.3 ! misho 724: /*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/
1.1 misho 725: joblist_append(srv, con);
726:
1.1.1.3 ! misho 727: if (revents & FDEVENT_OUT) {
! 728: if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) {
! 729: cgi_connection_close(srv, hctx);
! 730: return HANDLER_ERROR;
! 731: }
! 732: /* more request body to be sent to CGI */
! 733: }
! 734:
! 735: if (revents & FDEVENT_HUP) {
! 736: /* skip sending remaining data to CGI */
! 737: if (con->request.content_length) {
! 738: chunkqueue *cq = con->request_content_queue;
! 739: chunkqueue_mark_written(cq, chunkqueue_length(cq));
! 740: if (cq->bytes_in != (off_t)con->request.content_length) {
! 741: con->keep_alive = 0;
! 742: }
! 743: }
1.1 misho 744:
1.1.1.3 ! misho 745: cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
! 746: } else if (revents & FDEVENT_ERR) {
! 747: /* kill all connections to the cgi process */
! 748: #if 1
! 749: log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
! 750: #endif
! 751: cgi_connection_close(srv, hctx);
1.1 misho 752: return HANDLER_ERROR;
753: }
754:
1.1.1.3 ! misho 755: return HANDLER_FINISHED;
! 756: }
! 757:
! 758:
! 759: static int cgi_recv_response(server *srv, handler_ctx *hctx) {
1.1 misho 760: switch (cgi_demux_response(srv, hctx)) {
761: case FDEVENT_HANDLED_NOT_FINISHED:
762: break;
763: case FDEVENT_HANDLED_FINISHED:
764: /* we are done */
765:
766: #if 0
767: log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished");
768: #endif
769: cgi_connection_close(srv, hctx);
770:
771: /* if we get a IN|HUP and have read everything don't exec the close twice */
772: return HANDLER_FINISHED;
1.1.1.3 ! misho 773: case FDEVENT_HANDLED_COMEBACK:
! 774: cgi_connection_close(srv, hctx);
! 775: return HANDLER_COMEBACK;
1.1 misho 776: case FDEVENT_HANDLED_ERROR:
777: log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
1.1.1.3 ! misho 778:
! 779: cgi_connection_close(srv, hctx);
! 780: return HANDLER_FINISHED;
1.1 misho 781: }
782:
1.1.1.3 ! misho 783: return HANDLER_GO_ON;
! 784: }
! 785:
! 786:
! 787: static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
! 788: handler_ctx *hctx = ctx;
! 789: connection *con = hctx->remote_conn;
! 790:
! 791: joblist_append(srv, con);
! 792:
! 793: if (revents & FDEVENT_IN) {
! 794: handler_t rc = cgi_recv_response(srv, hctx);/*(might invalidate hctx)*/
! 795: if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
1.1 misho 796: }
797:
798: /* perhaps this issue is already handled */
799: if (revents & FDEVENT_HUP) {
1.1.1.3 ! misho 800: if (con->file_started) {
! 801: /* drain any remaining data from kernel pipe buffers
! 802: * even if (con->conf.stream_response_body
! 803: * & FDEVENT_STREAM_RESPONSE_BUFMIN)
! 804: * since event loop will spin on fd FDEVENT_HUP event
! 805: * until unregistered. */
! 806: handler_t rc;
! 807: do {
! 808: rc = cgi_recv_response(srv,hctx);/*(might invalidate hctx)*/
! 809: } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/
! 810: return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */
! 811: } else if (!buffer_string_is_empty(hctx->response_header)) {
! 812: /* unfinished header package which is a body in reality */
1.1 misho 813: con->file_started = 1;
1.1.1.3 ! misho 814: if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) {
! 815: cgi_connection_close(srv, hctx);
! 816: return HANDLER_ERROR;
! 817: }
1.1 misho 818: } else {
819: # if 0
1.1.1.3 ! misho 820: log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents);
1.1 misho 821: # endif
1.1.1.3 ! misho 822: }
1.1 misho 823: cgi_connection_close(srv, hctx);
824: } else if (revents & FDEVENT_ERR) {
825: /* kill all connections to the cgi process */
826: cgi_connection_close(srv, hctx);
827: #if 1
828: log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
829: #endif
830: return HANDLER_ERROR;
831: }
832:
833: return HANDLER_FINISHED;
834: }
835:
836:
837: static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
838: char *dst;
839:
840: if (!key || !val) return -1;
841:
842: dst = malloc(key_len + val_len + 2);
1.1.1.3 ! misho 843: force_assert(dst);
1.1 misho 844: memcpy(dst, key, key_len);
845: dst[key_len] = '=';
846: memcpy(dst + key_len + 1, val, val_len);
847: dst[key_len + 1 + val_len] = '\0';
848:
849: if (env->size == 0) {
850: env->size = 16;
851: env->ptr = malloc(env->size * sizeof(*env->ptr));
1.1.1.3 ! misho 852: force_assert(env->ptr);
1.1 misho 853: } else if (env->size == env->used) {
854: env->size += 16;
855: env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
1.1.1.3 ! misho 856: force_assert(env->ptr);
1.1 misho 857: }
858:
859: env->ptr[env->used++] = dst;
860:
861: return 0;
862: }
863:
1.1.1.3 ! misho 864: /* returns: 0: continue, -1: fatal error, -2: connection reset */
! 865: /* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes),
! 866: * also mmaps and sends complete chunk instead of only small parts - the files
! 867: * are supposed to be temp files with reasonable chunk sizes.
! 868: *
! 869: * Also always use mmap; the files are "trusted", as we created them.
! 870: */
! 871: static ssize_t cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) {
! 872: chunk* const c = cq->first;
! 873: off_t offset, toSend, file_end;
! 874: ssize_t r;
! 875: size_t mmap_offset, mmap_avail;
! 876: char *data;
! 877:
! 878: force_assert(NULL != c);
! 879: force_assert(FILE_CHUNK == c->type);
! 880: force_assert(c->offset >= 0 && c->offset <= c->file.length);
! 881:
! 882: offset = c->file.start + c->offset;
! 883: toSend = c->file.length - c->offset;
! 884: file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
! 885:
! 886: if (0 == toSend) {
! 887: chunkqueue_remove_finished_chunks(cq);
! 888: return 0;
! 889: }
! 890:
! 891: if (0 != network_open_file_chunk(srv, con, cq)) return -1;
! 892:
! 893: /* (re)mmap the buffer if range is not covered completely */
! 894: if (MAP_FAILED == c->file.mmap.start
! 895: || offset < c->file.mmap.offset
! 896: || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
! 897:
! 898: if (MAP_FAILED != c->file.mmap.start) {
! 899: munmap(c->file.mmap.start, c->file.mmap.length);
! 900: c->file.mmap.start = MAP_FAILED;
! 901: }
! 902:
! 903: c->file.mmap.offset = mmap_align_offset(offset);
! 904: c->file.mmap.length = file_end - c->file.mmap.offset;
! 905:
! 906: if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) {
! 907: if (toSend > 65536) toSend = 65536;
! 908: data = malloc(toSend);
! 909: force_assert(data);
! 910: if (-1 == lseek(c->file.fd, offset, SEEK_SET)
! 911: || 0 >= (toSend = read(c->file.fd, data, toSend))) {
! 912: if (-1 == toSend) {
! 913: log_error_write(srv, __FILE__, __LINE__, "ssbdo", "lseek/read failed:",
! 914: strerror(errno), c->file.name, c->file.fd, offset);
! 915: } else { /*(0 == toSend)*/
! 916: log_error_write(srv, __FILE__, __LINE__, "sbdo", "unexpected EOF (input truncated?):",
! 917: c->file.name, c->file.fd, offset);
! 918: }
! 919: free(data);
! 920: return -1;
! 921: }
! 922: }
! 923: }
! 924:
! 925: if (MAP_FAILED != c->file.mmap.start) {
! 926: force_assert(offset >= c->file.mmap.offset);
! 927: mmap_offset = offset - c->file.mmap.offset;
! 928: force_assert(c->file.mmap.length > mmap_offset);
! 929: mmap_avail = c->file.mmap.length - mmap_offset;
! 930: force_assert(toSend <= (off_t) mmap_avail);
! 931:
! 932: data = c->file.mmap.start + mmap_offset;
! 933: }
! 934:
! 935: r = write(fd, data, toSend);
! 936:
! 937: if (MAP_FAILED == c->file.mmap.start) free(data);
! 938:
! 939: if (r < 0) {
! 940: switch (errno) {
! 941: case EAGAIN:
! 942: case EINTR:
! 943: return 0;
! 944: case EPIPE:
! 945: case ECONNRESET:
! 946: return -2;
! 947: default:
! 948: log_error_write(srv, __FILE__, __LINE__, "ssd",
! 949: "write failed:", strerror(errno), fd);
! 950: return -1;
! 951: }
! 952: }
! 953:
! 954: if (r >= 0) {
! 955: chunkqueue_mark_written(cq, r);
! 956: }
! 957:
! 958: return r;
! 959: }
! 960:
! 961: static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) {
! 962: connection *con = hctx->remote_conn;
! 963: chunkqueue *cq = con->request_content_queue;
! 964: chunk *c;
! 965:
! 966: /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
! 967: * solution: if this is still a problem on windows, then substitute
! 968: * socketpair() for pipe() and closesocket() for close() on windows.
! 969: */
! 970:
! 971: for (c = cq->first; c; c = cq->first) {
! 972: ssize_t r = -1;
! 973:
! 974: switch(c->type) {
! 975: case FILE_CHUNK:
! 976: r = cgi_write_file_chunk_mmap(srv, con, fd, cq);
! 977: break;
! 978:
! 979: case MEM_CHUNK:
! 980: if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
! 981: switch(errno) {
! 982: case EAGAIN:
! 983: case EINTR:
! 984: /* ignore and try again */
! 985: r = 0;
! 986: break;
! 987: case EPIPE:
! 988: case ECONNRESET:
! 989: /* connection closed */
! 990: r = -2;
! 991: break;
! 992: default:
! 993: /* fatal error */
! 994: log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno));
! 995: r = -1;
! 996: break;
! 997: }
! 998: } else if (r > 0) {
! 999: chunkqueue_mark_written(cq, r);
! 1000: }
! 1001: break;
! 1002: }
! 1003:
! 1004: if (0 == r) break; /*(might block)*/
! 1005:
! 1006: switch (r) {
! 1007: case -1:
! 1008: /* fatal error */
! 1009: return -1;
! 1010: case -2:
! 1011: /* connection reset */
! 1012: log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI");
! 1013: /* skip all remaining data */
! 1014: chunkqueue_mark_written(cq, chunkqueue_length(cq));
! 1015: break;
! 1016: default:
! 1017: break;
! 1018: }
! 1019: }
! 1020:
! 1021: if (cq->bytes_out == (off_t)con->request.content_length) {
! 1022: /* sent all request body input */
! 1023: /* close connection to the cgi-script */
! 1024: if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/
! 1025: if (close(fd)) {
! 1026: log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", fd, strerror(errno));
! 1027: }
! 1028: } else {
! 1029: cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
! 1030: }
! 1031: } else {
! 1032: off_t cqlen = cq->bytes_in - cq->bytes_out;
! 1033: if (cq->bytes_in < (off_t)con->request.content_length && cqlen < 65536 - 16384) {
! 1034: /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
! 1035: if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
! 1036: con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
! 1037: con->is_readable = 1; /* trigger optimistic read from client */
! 1038: }
! 1039: }
! 1040: if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
! 1041: hctx->fdtocgi = fd;
! 1042: hctx->fde_ndx_tocgi = -1;
! 1043: fdevent_register(srv->ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
! 1044: }
! 1045: if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/
! 1046: if ((fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT)) {
! 1047: fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, 0);
! 1048: }
! 1049: } else {
! 1050: /* more request body remains to be sent to CGI so register for fdevents */
! 1051: fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, FDEVENT_OUT);
! 1052: }
! 1053: }
! 1054:
! 1055: return 0;
! 1056: }
! 1057:
! 1058: static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) {
1.1 misho 1059: pid_t pid;
1060:
1061: #ifdef HAVE_IPV6
1062: char b2[INET6_ADDRSTRLEN + 1];
1063: #endif
1064:
1065: int to_cgi_fds[2];
1066: int from_cgi_fds[2];
1067: struct stat st;
1068:
1069: #ifndef __WIN32
1070:
1.1.1.3 ! misho 1071: if (!buffer_string_is_empty(cgi_handler)) {
1.1 misho 1072: /* stat the exec file */
1073: if (-1 == (stat(cgi_handler->ptr, &st))) {
1074: log_error_write(srv, __FILE__, __LINE__, "sbss",
1075: "stat for cgi-handler", cgi_handler,
1076: "failed:", strerror(errno));
1077: return -1;
1078: }
1079: }
1080:
1081: if (pipe(to_cgi_fds)) {
1082: log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
1083: return -1;
1084: }
1085:
1086: if (pipe(from_cgi_fds)) {
1087: close(to_cgi_fds[0]);
1088: close(to_cgi_fds[1]);
1089: log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
1090: return -1;
1091: }
1092:
1093: /* fork, execve */
1094: switch (pid = fork()) {
1095: case 0: {
1096: /* child */
1097: char **args;
1098: int argc;
1099: int i = 0;
1.1.1.3 ! misho 1100: char buf[LI_ITOSTRING_LENGTH];
1.1 misho 1101: size_t n;
1102: char_array env;
1103: char *c;
1104: const char *s;
1105: server_socket *srv_sock = con->srv_socket;
1106:
1107: /* move stdout to from_cgi_fd[1] */
1108: close(STDOUT_FILENO);
1109: dup2(from_cgi_fds[1], STDOUT_FILENO);
1110: close(from_cgi_fds[1]);
1111: /* not needed */
1112: close(from_cgi_fds[0]);
1113:
1114: /* move the stdin to to_cgi_fd[0] */
1115: close(STDIN_FILENO);
1116: dup2(to_cgi_fds[0], STDIN_FILENO);
1117: close(to_cgi_fds[0]);
1118: /* not needed */
1119: close(to_cgi_fds[1]);
1120:
1121: /* create environment */
1122: env.ptr = NULL;
1123: env.size = 0;
1124: env.used = 0;
1125:
1.1.1.3 ! misho 1126: cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag));
1.1 misho 1127:
1.1.1.3 ! misho 1128: if (!buffer_string_is_empty(con->server_name)) {
! 1129: size_t len = buffer_string_length(con->server_name);
1.1 misho 1130:
1131: if (con->server_name->ptr[0] == '[') {
1132: const char *colon = strstr(con->server_name->ptr, "]:");
1133: if (colon) len = (colon + 1) - con->server_name->ptr;
1134: } else {
1135: const char *colon = strchr(con->server_name->ptr, ':');
1136: if (colon) len = colon - con->server_name->ptr;
1137: }
1138:
1139: cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
1140: } else {
1141: #ifdef HAVE_IPV6
1.1.1.3 ! misho 1142: s = inet_ntop(
! 1143: srv_sock->addr.plain.sa_family,
! 1144: srv_sock->addr.plain.sa_family == AF_INET6 ?
! 1145: (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
! 1146: (const void *) &(srv_sock->addr.ipv4.sin_addr),
! 1147: b2, sizeof(b2)-1);
1.1 misho 1148: #else
1149: s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
1150: #endif
1.1.1.3 ! misho 1151: force_assert(s);
1.1 misho 1152: cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
1153: }
1154: cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
1155:
1156: s = get_http_version_name(con->request.http_version);
1.1.1.3 ! misho 1157: force_assert(s);
1.1 misho 1158: cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
1159:
1.1.1.3 ! misho 1160: li_utostrn(buf, sizeof(buf),
1.1 misho 1161: #ifdef HAVE_IPV6
1162: ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
1163: #else
1164: ntohs(srv_sock->addr.ipv4.sin_port)
1165: #endif
1166: );
1167: cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
1168:
1169: switch (srv_sock->addr.plain.sa_family) {
1170: #ifdef HAVE_IPV6
1171: case AF_INET6:
1.1.1.3 ! misho 1172: s = inet_ntop(
! 1173: srv_sock->addr.plain.sa_family,
! 1174: (const void *) &(srv_sock->addr.ipv6.sin6_addr),
! 1175: b2, sizeof(b2)-1);
1.1 misho 1176: break;
1177: case AF_INET:
1.1.1.3 ! misho 1178: s = inet_ntop(
! 1179: srv_sock->addr.plain.sa_family,
! 1180: (const void *) &(srv_sock->addr.ipv4.sin_addr),
! 1181: b2, sizeof(b2)-1);
1.1 misho 1182: break;
1183: #else
1184: case AF_INET:
1185: s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
1186: break;
1187: #endif
1188: default:
1189: s = "";
1190: break;
1191: }
1.1.1.3 ! misho 1192: force_assert(s);
1.1 misho 1193: cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
1194:
1195: s = get_http_method_name(con->request.http_method);
1.1.1.3 ! misho 1196: force_assert(s);
1.1 misho 1197: cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
1198:
1.1.1.3 ! misho 1199: if (!buffer_string_is_empty(con->request.pathinfo)) {
1.1 misho 1200: cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
1201: }
1.1.1.3 ! misho 1202: if (!buffer_string_is_empty(con->uri.query)) {
1.1 misho 1203: cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
1.1.1.3 ! misho 1204: } else {
! 1205: cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN(""));
1.1 misho 1206: }
1.1.1.3 ! misho 1207: cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
! 1208: if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) {
! 1209: cgi_env_add(&env, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(con->request.uri));
! 1210: }
! 1211: /* set REDIRECT_STATUS for php compiled with --force-redirect
! 1212: * (if REDIRECT_STATUS has not already been set by error handler) */
! 1213: if (0 == con->error_handler_saved_status) {
! 1214: cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
1.1 misho 1215: }
1216:
1217:
1218: switch (con->dst_addr.plain.sa_family) {
1219: #ifdef HAVE_IPV6
1220: case AF_INET6:
1.1.1.3 ! misho 1221: s = inet_ntop(
! 1222: con->dst_addr.plain.sa_family,
! 1223: (const void *) &(con->dst_addr.ipv6.sin6_addr),
! 1224: b2, sizeof(b2)-1);
1.1 misho 1225: break;
1226: case AF_INET:
1.1.1.3 ! misho 1227: s = inet_ntop(
! 1228: con->dst_addr.plain.sa_family,
! 1229: (const void *) &(con->dst_addr.ipv4.sin_addr),
! 1230: b2, sizeof(b2)-1);
1.1 misho 1231: break;
1232: #else
1233: case AF_INET:
1234: s = inet_ntoa(con->dst_addr.ipv4.sin_addr);
1235: break;
1236: #endif
1237: default:
1238: s = "";
1239: break;
1240: }
1.1.1.3 ! misho 1241: force_assert(s);
1.1 misho 1242: cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
1243:
1.1.1.3 ! misho 1244: li_utostrn(buf, sizeof(buf),
1.1 misho 1245: #ifdef HAVE_IPV6
1246: ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
1247: #else
1248: ntohs(con->dst_addr.ipv4.sin_port)
1249: #endif
1250: );
1251: cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
1252:
1253: if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
1254: cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
1255: }
1256:
1.1.1.3 ! misho 1257: li_itostrn(buf, sizeof(buf), con->request.content_length);
1.1 misho 1258: cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
1259: cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
1260: cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
1261: cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
1262:
1263: /* for valgrind */
1264: if (NULL != (s = getenv("LD_PRELOAD"))) {
1265: cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
1266: }
1267:
1268: if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
1269: cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
1270: }
1271: #ifdef __CYGWIN__
1272: /* CYGWIN needs SYSTEMROOT */
1273: if (NULL != (s = getenv("SYSTEMROOT"))) {
1274: cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
1275: }
1276: #endif
1277:
1278: for (n = 0; n < con->request.headers->used; n++) {
1279: data_string *ds;
1280:
1281: ds = (data_string *)con->request.headers->data[n];
1282:
1.1.1.3 ! misho 1283: if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
! 1284: /* Do not emit HTTP_PROXY in environment.
! 1285: * Some executables use HTTP_PROXY to configure
! 1286: * outgoing proxy. See also https://httpoxy.org/ */
! 1287: if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) {
! 1288: continue;
1.1 misho 1289: }
1290:
1.1.1.3 ! misho 1291: buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 1);
1.1 misho 1292:
1293: cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1294: }
1295: }
1296:
1297: for (n = 0; n < con->environment->used; n++) {
1298: data_string *ds;
1299:
1300: ds = (data_string *)con->environment->data[n];
1301:
1.1.1.3 ! misho 1302: if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
! 1303: buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 0);
1.1 misho 1304:
1305: cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1306: }
1307: }
1308:
1309: if (env.size == env.used) {
1310: env.size += 16;
1311: env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
1312: }
1313:
1314: env.ptr[env.used] = NULL;
1315:
1316: /* set up args */
1317: argc = 3;
1318: args = malloc(sizeof(*args) * argc);
1.1.1.3 ! misho 1319: force_assert(args);
1.1 misho 1320: i = 0;
1321:
1.1.1.3 ! misho 1322: if (!buffer_string_is_empty(cgi_handler)) {
1.1 misho 1323: args[i++] = cgi_handler->ptr;
1324: }
1325: args[i++] = con->physical.path->ptr;
1326: args[i ] = NULL;
1327:
1328: /* search for the last / */
1329: if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
1.1.1.3 ! misho 1330: /* handle special case of file in root directory */
! 1331: const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr;
1.1 misho 1332:
1.1.1.3 ! misho 1333: /* temporarily shorten con->physical.path to directory without terminating '/' */
! 1334: *c = '\0';
1.1 misho 1335: /* change to the physical directory */
1.1.1.3 ! misho 1336: if (-1 == chdir(physdir)) {
1.1 misho 1337: log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
1338: }
1339: *c = '/';
1340: }
1341:
1342: /* we don't need the client socket */
1343: for (i = 3; i < 256; i++) {
1344: if (i != srv->errorlog_fd) close(i);
1345: }
1346:
1347: /* exec the cgi */
1348: execve(args[0], args, env.ptr);
1349:
1.1.1.3 ! misho 1350: /* most log files may have been closed/redirected by this point,
! 1351: * though stderr might still point to lighttpd.breakage.log */
! 1352: perror(args[0]);
! 1353: _exit(1);
1.1 misho 1354: }
1355: case -1:
1356: /* error */
1357: log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
1358: close(from_cgi_fds[0]);
1359: close(from_cgi_fds[1]);
1360: close(to_cgi_fds[0]);
1361: close(to_cgi_fds[1]);
1362: return -1;
1363: default: {
1.1.1.3 ! misho 1364: /* parent process */
1.1 misho 1365:
1366: close(from_cgi_fds[1]);
1367: close(to_cgi_fds[0]);
1368:
1.1.1.3 ! misho 1369: /* register PID and wait for them asynchronously */
1.1 misho 1370:
1.1.1.3 ! misho 1371: hctx->pid = pid;
! 1372: hctx->fd = from_cgi_fds[0];
! 1373: hctx->fde_ndx = -1;
1.1 misho 1374:
1.1.1.3 ! misho 1375: if (0 == con->request.content_length) {
! 1376: close(to_cgi_fds[1]);
! 1377: } else {
1.1 misho 1378: /* there is content to send */
1.1.1.3 ! misho 1379: if (-1 == fdevent_fcntl_set(srv->ev, to_cgi_fds[1])) {
! 1380: log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
! 1381: close(to_cgi_fds[1]);
! 1382: cgi_connection_close(srv, hctx);
! 1383: return -1;
! 1384: }
1.1 misho 1385:
1.1.1.3 ! misho 1386: if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) {
! 1387: close(to_cgi_fds[1]);
! 1388: cgi_connection_close(srv, hctx);
! 1389: return -1;
1.1 misho 1390: }
1391: }
1392:
1393: fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
1394: fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1395:
1396: if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
1397: log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
1.1.1.3 ! misho 1398: cgi_connection_close(srv, hctx);
1.1 misho 1399: return -1;
1400: }
1401:
1402: break;
1403: }
1404: }
1405:
1406: return 0;
1407: #else
1408: return -1;
1409: #endif
1410: }
1411:
1.1.1.3 ! misho 1412: static buffer * cgi_get_handler(array *a, buffer *fn) {
! 1413: size_t k, s_len = buffer_string_length(fn);
! 1414: for (k = 0; k < a->used; ++k) {
! 1415: data_string *ds = (data_string *)a->data[k];
! 1416: size_t ct_len = buffer_string_length(ds->key);
! 1417:
! 1418: if (buffer_is_empty(ds->key)) continue;
! 1419: if (s_len < ct_len) continue;
! 1420:
! 1421: if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
! 1422: return ds->value;
! 1423: }
! 1424: }
! 1425:
! 1426: return NULL;
! 1427: }
! 1428:
1.1 misho 1429: #define PATCH(x) \
1430: p->conf.x = s->x;
1431: static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
1432: size_t i, j;
1433: plugin_config *s = p->config_storage[0];
1434:
1435: PATCH(cgi);
1436: PATCH(execute_x_only);
1.1.1.3 ! misho 1437: PATCH(xsendfile_allow);
! 1438: PATCH(xsendfile_docroot);
1.1 misho 1439:
1440: /* skip the first, the global context */
1441: for (i = 1; i < srv->config_context->used; i++) {
1442: data_config *dc = (data_config *)srv->config_context->data[i];
1443: s = p->config_storage[i];
1444:
1445: /* condition didn't match */
1446: if (!config_check_cond(srv, con, dc)) continue;
1447:
1448: /* merge config */
1449: for (j = 0; j < dc->value->used; j++) {
1450: data_unset *du = dc->value->data[j];
1451:
1452: if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
1453: PATCH(cgi);
1454: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) {
1455: PATCH(execute_x_only);
1.1.1.3 ! misho 1456: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.x-sendfile"))) {
! 1457: PATCH(xsendfile_allow);
! 1458: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.x-sendfile-docroot"))) {
! 1459: PATCH(xsendfile_docroot);
1.1 misho 1460: }
1461: }
1462: }
1463:
1464: return 0;
1465: }
1466: #undef PATCH
1467:
1468: URIHANDLER_FUNC(cgi_is_handled) {
1469: plugin_data *p = p_d;
1470: buffer *fn = con->physical.path;
1471: stat_cache_entry *sce = NULL;
1472:
1473: if (con->mode != DIRECT) return HANDLER_GO_ON;
1474:
1.1.1.3 ! misho 1475: if (buffer_is_empty(fn)) return HANDLER_GO_ON;
1.1 misho 1476:
1477: mod_cgi_patch_connection(srv, con, p);
1478:
1479: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON;
1480: if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
1481: if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1482:
1.1.1.3 ! misho 1483: if (NULL != cgi_get_handler(p->conf.cgi, fn)) {
! 1484: handler_ctx *hctx = cgi_handler_ctx_init();
! 1485: hctx->remote_conn = con;
! 1486: hctx->plugin_data = p;
! 1487: con->plugin_ctx[p->id] = hctx;
! 1488: con->mode = p->id;
1.1 misho 1489: }
1490:
1491: return HANDLER_GO_ON;
1492: }
1493:
1494: TRIGGER_FUNC(cgi_trigger) {
1495: plugin_data *p = p_d;
1496: size_t ndx;
1497: /* the trigger handle only cares about lonely PID which we have to wait for */
1498: #ifndef __WIN32
1499:
1500: for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
1501: int status;
1502:
1503: switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
1504: case 0:
1505: /* not finished yet */
1506: #if 0
1507: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
1508: #endif
1509: break;
1510: case -1:
1511: if (errno == ECHILD) {
1512: /* someone else called waitpid... remove the pid to stop looping the error each time */
1513: log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid");
1514:
1515: cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1516: ndx--;
1517: continue;
1518: }
1519:
1520: log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1521:
1522: return HANDLER_ERROR;
1523: default:
1524:
1525: if (WIFEXITED(status)) {
1526: #if 0
1527: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
1528: #endif
1529: } else if (WIFSIGNALED(status)) {
1530: /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
1531: */
1532: if (WTERMSIG(status) != SIGTERM) {
1533: log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
1534: }
1535: } else {
1536: log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
1537: }
1538:
1539: cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1540: /* del modified the buffer structure
1541: * and copies the last entry to the current one
1542: * -> recheck the current index
1543: */
1544: ndx--;
1545: }
1546: }
1547: #endif
1548: return HANDLER_GO_ON;
1549: }
1550:
1551: /*
1552: * - HANDLER_GO_ON : not our job
1.1.1.3 ! misho 1553: * - HANDLER_FINISHED: got response
! 1554: * - HANDLER_WAIT_FOR_EVENT: waiting for response
1.1 misho 1555: */
1556: SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
1557: plugin_data *p = p_d;
1558: handler_ctx *hctx = con->plugin_ctx[p->id];
1.1.1.3 ! misho 1559: chunkqueue *cq = con->request_content_queue;
1.1 misho 1560:
1561: if (con->mode != p->id) return HANDLER_GO_ON;
1562: if (NULL == hctx) return HANDLER_GO_ON;
1563:
1.1.1.3 ! misho 1564: if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
! 1565: && con->file_started) {
! 1566: if (chunkqueue_length(con->write_queue) > 65536 - 4096) {
! 1567: fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
! 1568: } else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) {
! 1569: /* optimistic read from backend, which might re-enable FDEVENT_IN */
! 1570: handler_t rc = cgi_recv_response(srv, hctx); /*(might invalidate hctx)*/
! 1571: if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/
1.1 misho 1572: }
1.1.1.3 ! misho 1573: }
1.1 misho 1574:
1.1.1.3 ! misho 1575: if (cq->bytes_in != (off_t)con->request.content_length) {
! 1576: /*(64k - 4k to attempt to avoid temporary files
! 1577: * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
! 1578: if (cq->bytes_in - cq->bytes_out > 65536 - 4096
! 1579: && (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){
! 1580: con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
! 1581: if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT;
! 1582: } else {
! 1583: handler_t r = connection_handle_read_post_state(srv, con);
! 1584: if (!chunkqueue_is_empty(cq)) {
! 1585: if (fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT) {
! 1586: return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r;
! 1587: }
! 1588: }
! 1589: if (r != HANDLER_GO_ON) return r;
1.1 misho 1590: }
1.1.1.3 ! misho 1591: }
1.1 misho 1592:
1.1.1.3 ! misho 1593: if (-1 == hctx->fd) {
! 1594: buffer *handler = cgi_get_handler(p->conf.cgi, con->physical.path);
! 1595: if (!handler) return HANDLER_GO_ON; /*(should not happen; checked in cgi_is_handled())*/
! 1596: if (cgi_create_env(srv, con, p, hctx, handler)) {
! 1597: con->http_status = 500;
! 1598: con->mode = DIRECT;
1.1 misho 1599:
1.1.1.3 ! misho 1600: return HANDLER_FINISHED;
1.1 misho 1601: }
1.1.1.3 ! misho 1602: #if 0
! 1603: log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
! 1604: #endif
! 1605: } else if (!chunkqueue_is_empty(con->request_content_queue)) {
! 1606: if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) {
! 1607: cgi_connection_close(srv, hctx);
! 1608: return HANDLER_ERROR;
1.1 misho 1609: }
1610: }
1.1.1.3 ! misho 1611:
! 1612: /* if not done, wait for CGI to close stdout, so we read EOF on pipe */
! 1613: return HANDLER_WAIT_FOR_EVENT;
1.1 misho 1614: }
1615:
1616:
1617: int mod_cgi_plugin_init(plugin *p);
1618: int mod_cgi_plugin_init(plugin *p) {
1619: p->version = LIGHTTPD_VERSION_ID;
1620: p->name = buffer_init_string("cgi");
1621:
1622: p->connection_reset = cgi_connection_close_callback;
1623: p->handle_subrequest_start = cgi_is_handled;
1624: p->handle_subrequest = mod_cgi_handle_subrequest;
1625: p->handle_trigger = cgi_trigger;
1626: p->init = mod_cgi_init;
1627: p->cleanup = mod_cgi_free;
1628: p->set_defaults = mod_fastcgi_set_defaults;
1629:
1630: p->data = NULL;
1631:
1632: return 0;
1633: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>