1 #include "server.h"
2 #include "stat_cache.h"
3 #include "keyvalue.h"
4 #include "log.h"
5 #include "connections.h"
6 #include "joblist.h"
7 #include "http_chunk.h"
8 
9 #include "plugin.h"
10 
11 #include <sys/types.h>
12 
13 #ifdef __WIN32
14 # include <winsock2.h>
15 #else
16 # include <sys/socket.h>
17 # include <sys/wait.h>
18 # include <sys/mman.h>
19 # include <netinet/in.h>
20 # include <arpa/inet.h>
21 #endif
22 
23 #include <unistd.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <fdevent.h>
28 #include <signal.h>
29 #include <ctype.h>
30 #include <assert.h>
31 
32 #include <stdio.h>
33 #include <fcntl.h>
34 
35 #ifdef HAVE_SYS_FILIO_H
36 # include <sys/filio.h>
37 #endif
38 
39 #include "version.h"
40 
41 enum {EOL_UNSET, EOL_N, EOL_RN};
42 
43 typedef struct {
44 	char **ptr;
45 
46 	size_t size;
47 	size_t used;
48 } char_array;
49 
50 typedef struct {
51 	pid_t *ptr;
52 	size_t used;
53 	size_t size;
54 } buffer_pid_t;
55 
56 typedef struct {
57 	array *cgi;
58 	unsigned short execute_x_only;
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;
76 	int fde_ndx; /* index into the fd-event buffer */
77 
78 	connection *remote_conn;  /* dumb pointer */
79 	plugin_data *plugin_data; /* dumb pointer */
80 
81 	buffer *response;
82 	buffer *response_header;
83 } handler_ctx;
84 
cgi_handler_ctx_init(void)85 static handler_ctx * cgi_handler_ctx_init(void) {
86 	handler_ctx *hctx = calloc(1, sizeof(*hctx));
87 
88 	assert(hctx);
89 
90 	hctx->response = buffer_init();
91 	hctx->response_header = buffer_init();
92 
93 	return hctx;
94 }
95 
cgi_handler_ctx_free(handler_ctx * hctx)96 static void cgi_handler_ctx_free(handler_ctx *hctx) {
97 	buffer_free(hctx->response);
98 	buffer_free(hctx->response_header);
99 
100 	free(hctx);
101 }
102 
103 enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR};
104 
INIT_FUNC(mod_cgi_init)105 INIT_FUNC(mod_cgi_init) {
106 	plugin_data *p;
107 
108 	p = calloc(1, sizeof(*p));
109 
110 	assert(p);
111 
112 	p->tmp_buf = buffer_init();
113 	p->parse_response = buffer_init();
114 
115 	return p;
116 }
117 
118 
FREE_FUNC(mod_cgi_free)119 FREE_FUNC(mod_cgi_free) {
120 	plugin_data *p = p_d;
121 	buffer_pid_t *r = &(p->cgi_pid);
122 
123 	UNUSED(srv);
124 
125 	if (p->config_storage) {
126 		size_t i;
127 		for (i = 0; i < srv->config_context->used; i++) {
128 			plugin_config *s = p->config_storage[i];
129 
130 			array_free(s->cgi);
131 
132 			free(s);
133 		}
134 		free(p->config_storage);
135 	}
136 
137 
138 	if (r->ptr) free(r->ptr);
139 
140 	buffer_free(p->tmp_buf);
141 	buffer_free(p->parse_response);
142 
143 	free(p);
144 
145 	return HANDLER_GO_ON;
146 }
147 
SETDEFAULTS_FUNC(mod_fastcgi_set_defaults)148 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
149 	plugin_data *p = p_d;
150 	size_t i = 0;
151 
152 	config_values_t cv[] = {
153 		{ "cgi.assign",                  NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
154 		{ "cgi.execute-x-only",          NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },     /* 1 */
155 		{ NULL,                          NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
156 	};
157 
158 	if (!p) return HANDLER_ERROR;
159 
160 	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
161 
162 	for (i = 0; i < srv->config_context->used; i++) {
163 		plugin_config *s;
164 
165 		s = calloc(1, sizeof(plugin_config));
166 		assert(s);
167 
168 		s->cgi    = array_init();
169 		s->execute_x_only = 0;
170 
171 		cv[0].destination = s->cgi;
172 		cv[1].destination = &(s->execute_x_only);
173 
174 		p->config_storage[i] = s;
175 
176 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
177 			return HANDLER_ERROR;
178 		}
179 	}
180 
181 	return HANDLER_GO_ON;
182 }
183 
184 
cgi_pid_add(server * srv,plugin_data * p,pid_t pid)185 static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
186 	int m = -1;
187 	size_t i;
188 	buffer_pid_t *r = &(p->cgi_pid);
189 
190 	UNUSED(srv);
191 
192 	for (i = 0; i < r->used; i++) {
193 		if (r->ptr[i] > m) m = r->ptr[i];
194 	}
195 
196 	if (r->size == 0) {
197 		r->size = 16;
198 		r->ptr = malloc(sizeof(*r->ptr) * r->size);
199 	} else if (r->used == r->size) {
200 		r->size += 16;
201 		r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
202 	}
203 
204 	r->ptr[r->used++] = pid;
205 
206 	return m;
207 }
208 
cgi_pid_del(server * srv,plugin_data * p,pid_t pid)209 static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
210 	size_t i;
211 	buffer_pid_t *r = &(p->cgi_pid);
212 
213 	UNUSED(srv);
214 
215 	for (i = 0; i < r->used; i++) {
216 		if (r->ptr[i] == pid) break;
217 	}
218 
219 	if (i != r->used) {
220 		/* found */
221 
222 		if (i != r->used - 1) {
223 			r->ptr[i] = r->ptr[r->used - 1];
224 		}
225 		r->used--;
226 	}
227 
228 	return 0;
229 }
230 
cgi_response_parse(server * srv,connection * con,plugin_data * p,buffer * in)231 static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
232 	char *ns;
233 	const char *s;
234 	int line = 0;
235 
236 	UNUSED(srv);
237 
238 	buffer_copy_string_buffer(p->parse_response, in);
239 
240 	for (s = p->parse_response->ptr;
241 	     NULL != (ns = strchr(s, '\n'));
242 	     s = ns + 1, line++) {
243 		const char *key, *value;
244 		int key_len;
245 		data_string *ds;
246 
247 		/* strip the \n */
248 		ns[0] = '\0';
249 
250 		if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
251 
252 		if (line == 0 &&
253 		    0 == strncmp(s, "HTTP/1.", 7)) {
254 			/* non-parsed header ... we parse them anyway */
255 
256 			if ((s[7] == '1' ||
257 			     s[7] == '0') &&
258 			    s[8] == ' ') {
259 				int status;
260 				/* after the space should be a status code for us */
261 
262 				status = strtol(s+9, NULL, 10);
263 
264 				if (status >= 100 &&
265 				    status < 1000) {
266 					/* we expected 3 digits and didn't got them */
267 					con->parsed_response |= HTTP_STATUS;
268 					con->http_status = status;
269 				}
270 			}
271 		} else {
272 			/* parse the headers */
273 			key = s;
274 			if (NULL == (value = strchr(s, ':'))) {
275 				/* we expect: "<key>: <value>\r\n" */
276 				continue;
277 			}
278 
279 			key_len = value - key;
280 			value += 1;
281 
282 			/* skip LWS */
283 			while (*value == ' ' || *value == '\t') value++;
284 
285 			if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
286 				ds = data_response_init();
287 			}
288 			buffer_copy_string_len(ds->key, key, key_len);
289 			buffer_copy_string(ds->value, value);
290 
291 			array_insert_unique(con->response.headers, (data_unset *)ds);
292 
293 			switch(key_len) {
294 			case 4:
295 				if (0 == strncasecmp(key, "Date", key_len)) {
296 					con->parsed_response |= HTTP_DATE;
297 				}
298 				break;
299 			case 6:
300 				if (0 == strncasecmp(key, "Status", key_len)) {
301 					con->http_status = strtol(value, NULL, 10);
302 					con->parsed_response |= HTTP_STATUS;
303 				}
304 				break;
305 			case 8:
306 				if (0 == strncasecmp(key, "Location", key_len)) {
307 					con->parsed_response |= HTTP_LOCATION;
308 				}
309 				break;
310 			case 10:
311 				if (0 == strncasecmp(key, "Connection", key_len)) {
312 					con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
313 					con->parsed_response |= HTTP_CONNECTION;
314 				}
315 				break;
316 			case 14:
317 				if (0 == strncasecmp(key, "Content-Length", key_len)) {
318 					con->response.content_length = strtol(value, NULL, 10);
319 					con->parsed_response |= HTTP_CONTENT_LENGTH;
320 				}
321 				break;
322 			default:
323 				break;
324 			}
325 		}
326 	}
327 
328 	/* CGI/1.1 rev 03 - 7.2.1.2 */
329 	if ((con->parsed_response & HTTP_LOCATION) &&
330 	    !(con->parsed_response & HTTP_STATUS)) {
331 		con->http_status = 302;
332 	}
333 
334 	return 0;
335 }
336 
337 
cgi_demux_response(server * srv,handler_ctx * hctx)338 static int cgi_demux_response(server *srv, handler_ctx *hctx) {
339 	plugin_data *p    = hctx->plugin_data;
340 	connection  *con  = hctx->remote_conn;
341 
342 	while(1) {
343 		int n;
344 		int toread;
345 
346 #if defined(__WIN32)
347 		buffer_prepare_copy(hctx->response, 4 * 1024);
348 #else
349 		if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
350 			buffer_prepare_copy(hctx->response, 4 * 1024);
351 		} else {
352 			if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
353 			buffer_prepare_copy(hctx->response, toread + 1);
354 		}
355 #endif
356 
357 		if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
358 			if (errno == EAGAIN || errno == EINTR) {
359 				/* would block, wait for signal */
360 				return FDEVENT_HANDLED_NOT_FINISHED;
361 			}
362 			/* error */
363 			log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
364 			return FDEVENT_HANDLED_ERROR;
365 		}
366 
367 		if (n == 0) {
368 			/* read finished */
369 
370 			con->file_finished = 1;
371 
372 			/* send final chunk */
373 			http_chunk_append_mem(srv, con, NULL, 0);
374 			joblist_append(srv, con);
375 
376 			return FDEVENT_HANDLED_FINISHED;
377 		}
378 
379 		hctx->response->ptr[n] = '\0';
380 		hctx->response->used = n+1;
381 
382 		/* split header from body */
383 
384 		if (con->file_started == 0) {
385 			int is_header = 0;
386 			int is_header_end = 0;
387 			size_t last_eol = 0;
388 			size_t i;
389 
390 			buffer_append_string_buffer(hctx->response_header, hctx->response);
391 
392 			/**
393 			 * we have to handle a few cases:
394 			 *
395 			 * nph:
396 			 *
397 			 *   HTTP/1.0 200 Ok\n
398 			 *   Header: Value\n
399 			 *   \n
400 			 *
401 			 * CGI:
402 			 *   Header: Value\n
403 			 *   Status: 200\n
404 			 *   \n
405 			 *
406 			 * and different mixes of \n and \r\n combinations
407 			 *
408 			 * Some users also forget about CGI and just send a response and hope
409 			 * we handle it. No headers, no header-content seperator
410 			 *
411 			 */
412 
413 			/* nph (non-parsed headers) */
414 			if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
415 
416 			for (i = 0; !is_header_end && i < hctx->response_header->used - 1; i++) {
417 				char c = hctx->response_header->ptr[i];
418 
419 				switch (c) {
420 				case ':':
421 					/* we found a colon
422 					 *
423 					 * looks like we have a normal header
424 					 */
425 					is_header = 1;
426 					break;
427 				case '\n':
428 					/* EOL */
429 					if (is_header == 0) {
430 						/* we got a EOL but we don't seem to got a HTTP header */
431 
432 						is_header_end = 1;
433 
434 						break;
435 					}
436 
437 					/**
438 					 * check if we saw a \n(\r)?\n sequence
439 					 */
440 					if (last_eol > 0 &&
441 					    ((i - last_eol == 1) ||
442 					     (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
443 						is_header_end = 1;
444 						break;
445 					}
446 
447 					last_eol = i;
448 
449 					break;
450 				}
451 			}
452 
453 			if (is_header_end) {
454 				if (!is_header) {
455 					/* no header, but a body */
456 
457 					if (con->request.http_version == HTTP_VERSION_1_1) {
458 						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
459 					}
460 
461 					http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
462 					joblist_append(srv, con);
463 				} else {
464 					const char *bstart;
465 					size_t blen;
466 
467 					/**
468 					 * i still points to the char after the terminating EOL EOL
469 					 *
470 					 * put it on the last \n again
471 					 */
472 					i--;
473 
474 					/* the body starts after the EOL */
475 					bstart = hctx->response_header->ptr + (i + 1);
476 					blen = (hctx->response_header->used - 1) - (i + 1);
477 
478 					/* string the last \r?\n */
479 					if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
480 						i--;
481 					}
482 
483 					hctx->response_header->ptr[i] = '\0';
484 					hctx->response_header->used = i + 1; /* the string + \0 */
485 
486 					/* parse the response header */
487 					cgi_response_parse(srv, con, p, hctx->response_header);
488 
489 					/* enable chunked-transfer-encoding */
490 					if (con->request.http_version == HTTP_VERSION_1_1 &&
491 					    !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
492 						con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
493 					}
494 
495 					if (blen > 0) {
496 						http_chunk_append_mem(srv, con, bstart, blen + 1);
497 						joblist_append(srv, con);
498 					}
499 				}
500 
501 				con->file_started = 1;
502 			}
503 		} else {
504 			http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used);
505 			joblist_append(srv, con);
506 		}
507 
508 #if 0
509 		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
510 #endif
511 	}
512 
513 	return FDEVENT_HANDLED_NOT_FINISHED;
514 }
515 
cgi_connection_close(server * srv,handler_ctx * hctx)516 static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) {
517 	int status;
518 	pid_t pid;
519 	plugin_data *p;
520 	connection  *con;
521 
522 	if (NULL == hctx) return HANDLER_GO_ON;
523 
524 	p    = hctx->plugin_data;
525 	con  = hctx->remote_conn;
526 
527 	if (con->mode != p->id) return HANDLER_GO_ON;
528 
529 #ifndef __WIN32
530 
531 	/* the connection to the browser went away, but we still have a connection
532 	 * to the CGI script
533 	 *
534 	 * close cgi-connection
535 	 */
536 
537 	if (hctx->fd != -1) {
538 		/* close connection to the cgi-script */
539 		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
540 		fdevent_unregister(srv->ev, hctx->fd);
541 
542 		if (close(hctx->fd)) {
543 			log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
544 		}
545 
546 		hctx->fd = -1;
547 		hctx->fde_ndx = -1;
548 	}
549 
550 	pid = hctx->pid;
551 
552 	con->plugin_ctx[p->id] = NULL;
553 
554 	/* is this a good idea ? */
555 	cgi_handler_ctx_free(hctx);
556 
557 	/* if waitpid hasn't been called by response.c yet, do it here */
558 	if (pid) {
559 		/* check if the CGI-script is already gone */
560 		switch(waitpid(pid, &status, WNOHANG)) {
561 		case 0:
562 			/* not finished yet */
563 #if 0
564 			log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
565 #endif
566 			break;
567 		case -1:
568 			/* */
569 			if (errno == EINTR) break;
570 
571 			/*
572 			 * errno == ECHILD happens if _subrequest catches the process-status before
573 			 * we have read the response of the cgi process
574 			 *
575 			 * -> catch status
576 			 * -> WAIT_FOR_EVENT
577 			 * -> read response
578 			 * -> we get here with waitpid == ECHILD
579 			 *
580 			 */
581 			if (errno == ECHILD) return HANDLER_GO_ON;
582 
583 			log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
584 			return HANDLER_ERROR;
585 		default:
586 			/* Send an error if we haven't sent any data yet */
587 			if (0 == con->file_started) {
588 				connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
589 				con->http_status = 500;
590 				con->mode = DIRECT;
591 			} else {
592 				con->file_finished = 1;
593 			}
594 
595 			if (WIFEXITED(status)) {
596 #if 0
597 				log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
598 #endif
599 				return HANDLER_GO_ON;
600 			} else {
601 				log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
602 				return HANDLER_GO_ON;
603 			}
604 		}
605 
606 
607 		kill(pid, SIGTERM);
608 
609 		/* cgi-script is still alive, queue the PID for removal */
610 		cgi_pid_add(srv, p, pid);
611 	}
612 #endif
613 	return HANDLER_GO_ON;
614 }
615 
cgi_connection_close_callback(server * srv,connection * con,void * p_d)616 static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
617 	plugin_data *p = p_d;
618 
619 	return cgi_connection_close(srv, con->plugin_ctx[p->id]);
620 }
621 
622 
cgi_handle_fdevent(server * srv,void * ctx,int revents)623 static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
624 	handler_ctx *hctx = ctx;
625 	connection  *con  = hctx->remote_conn;
626 
627 	joblist_append(srv, con);
628 
629 	if (hctx->fd == -1) {
630 		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "invalid cgi-fd");
631 
632 		return HANDLER_ERROR;
633 	}
634 
635 	if (revents & FDEVENT_IN) {
636 		switch (cgi_demux_response(srv, hctx)) {
637 		case FDEVENT_HANDLED_NOT_FINISHED:
638 			break;
639 		case FDEVENT_HANDLED_FINISHED:
640 			/* we are done */
641 
642 #if 0
643 			log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished");
644 #endif
645 			cgi_connection_close(srv, hctx);
646 
647 			/* if we get a IN|HUP and have read everything don't exec the close twice */
648 			return HANDLER_FINISHED;
649 		case FDEVENT_HANDLED_ERROR:
650 			/* Send an error if we haven't sent any data yet */
651 			if (0 == con->file_started) {
652 				connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
653 				con->http_status = 500;
654 				con->mode = DIRECT;
655 			} else {
656 				con->file_finished = 1;
657 			}
658 
659 			log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
660 			break;
661 		}
662 	}
663 
664 	if (revents & FDEVENT_OUT) {
665 		/* nothing to do */
666 	}
667 
668 	/* perhaps this issue is already handled */
669 	if (revents & FDEVENT_HUP) {
670 		/* check if we still have a unfinished header package which is a body in reality */
671 		if (con->file_started == 0 &&
672 		    hctx->response_header->used) {
673 			con->file_started = 1;
674 			http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
675 			joblist_append(srv, con);
676 		}
677 
678 		if (con->file_finished == 0) {
679 			http_chunk_append_mem(srv, con, NULL, 0);
680 			joblist_append(srv, con);
681 		}
682 
683 		con->file_finished = 1;
684 
685 		if (chunkqueue_is_empty(con->write_queue)) {
686 			/* there is nothing left to write */
687 			connection_set_state(srv, con, CON_STATE_RESPONSE_END);
688 		} else {
689 			/* used the write-handler to finish the request on demand */
690 
691 		}
692 
693 # if 0
694 		log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents);
695 # endif
696 
697 		/* rtsigs didn't liked the close */
698 		cgi_connection_close(srv, hctx);
699 	} else if (revents & FDEVENT_ERR) {
700 		con->file_finished = 1;
701 
702 		/* kill all connections to the cgi process */
703 		cgi_connection_close(srv, hctx);
704 #if 1
705 		log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
706 #endif
707 		return HANDLER_ERROR;
708 	}
709 
710 	return HANDLER_FINISHED;
711 }
712 
713 
cgi_env_add(char_array * env,const char * key,size_t key_len,const char * val,size_t val_len)714 static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
715 	char *dst;
716 
717 	if (!key || !val) return -1;
718 
719 	dst = malloc(key_len + val_len + 2);
720 	memcpy(dst, key, key_len);
721 	dst[key_len] = '=';
722 	memcpy(dst + key_len + 1, val, val_len);
723 	dst[key_len + 1 + val_len] = '\0';
724 
725 	if (env->size == 0) {
726 		env->size = 16;
727 		env->ptr = malloc(env->size * sizeof(*env->ptr));
728 	} else if (env->size == env->used) {
729 		env->size += 16;
730 		env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
731 	}
732 
733 	env->ptr[env->used++] = dst;
734 
735 	return 0;
736 }
737 
cgi_create_env(server * srv,connection * con,plugin_data * p,buffer * cgi_handler)738 static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) {
739 	pid_t pid;
740 
741 #ifdef HAVE_IPV6
742 	char b2[INET6_ADDRSTRLEN + 1];
743 #endif
744 
745 	int to_cgi_fds[2];
746 	int from_cgi_fds[2];
747 	struct stat st;
748 
749 #ifndef __WIN32
750 
751 	if (cgi_handler->used > 1) {
752 		/* stat the exec file */
753 		if (-1 == (stat(cgi_handler->ptr, &st))) {
754 			log_error_write(srv, __FILE__, __LINE__, "sbss",
755 					"stat for cgi-handler", cgi_handler,
756 					"failed:", strerror(errno));
757 			return -1;
758 		}
759 	}
760 
761 	if (pipe(to_cgi_fds)) {
762 		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
763 		return -1;
764 	}
765 
766 	if (pipe(from_cgi_fds)) {
767 		close(to_cgi_fds[0]);
768 		close(to_cgi_fds[1]);
769 		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
770 		return -1;
771 	}
772 
773 	/* fork, execve */
774 	switch (pid = fork()) {
775 	case 0: {
776 		/* child */
777 		char **args;
778 		int argc;
779 		int i = 0;
780 		char buf[32];
781 		size_t n;
782 		char_array env;
783 		char *c;
784 		const char *s;
785 		server_socket *srv_sock = con->srv_socket;
786 
787 		/* move stdout to from_cgi_fd[1] */
788 		close(STDOUT_FILENO);
789 		dup2(from_cgi_fds[1], STDOUT_FILENO);
790 		close(from_cgi_fds[1]);
791 		/* not needed */
792 		close(from_cgi_fds[0]);
793 
794 		/* move the stdin to to_cgi_fd[0] */
795 		close(STDIN_FILENO);
796 		dup2(to_cgi_fds[0], STDIN_FILENO);
797 		close(to_cgi_fds[0]);
798 		/* not needed */
799 		close(to_cgi_fds[1]);
800 
801 		/* create environment */
802 		env.ptr = NULL;
803 		env.size = 0;
804 		env.used = 0;
805 
806 		if (buffer_is_empty(con->conf.server_tag)) {
807 			cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC));
808 		} else {
809 			cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag));
810 		}
811 
812 		if (!buffer_is_empty(con->server_name)) {
813 			size_t len = con->server_name->used - 1;
814 
815 			if (con->server_name->ptr[0] == '[') {
816 				const char *colon = strstr(con->server_name->ptr, "]:");
817 				if (colon) len = (colon + 1) - con->server_name->ptr;
818 			} else {
819 				const char *colon = strchr(con->server_name->ptr, ':');
820 				if (colon) len = colon - con->server_name->ptr;
821 			}
822 
823 			cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
824 		} else {
825 #ifdef HAVE_IPV6
826 			s = inet_ntop(srv_sock->addr.plain.sa_family,
827 				      srv_sock->addr.plain.sa_family == AF_INET6 ?
828 				      (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
829 				      (const void *) &(srv_sock->addr.ipv4.sin_addr),
830 				      b2, sizeof(b2)-1);
831 #else
832 			s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
833 #endif
834 			cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
835 		}
836 		cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
837 
838 		s = get_http_version_name(con->request.http_version);
839 
840 		cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
841 
842 		LI_ltostr(buf,
843 #ifdef HAVE_IPV6
844 			ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
845 #else
846 			ntohs(srv_sock->addr.ipv4.sin_port)
847 #endif
848 			);
849 		cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
850 
851 		switch (srv_sock->addr.plain.sa_family) {
852 #ifdef HAVE_IPV6
853 		case AF_INET6:
854 			s = inet_ntop(srv_sock->addr.plain.sa_family,
855 			              (const void *) &(srv_sock->addr.ipv6.sin6_addr),
856 			              b2, sizeof(b2)-1);
857 			break;
858 		case AF_INET:
859 			s = inet_ntop(srv_sock->addr.plain.sa_family,
860 			              (const void *) &(srv_sock->addr.ipv4.sin_addr),
861 			              b2, sizeof(b2)-1);
862 			break;
863 #else
864 		case AF_INET:
865 			s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
866 			break;
867 #endif
868 		default:
869 			s = "";
870 			break;
871 		}
872 		cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
873 
874 		s = get_http_method_name(con->request.http_method);
875 		cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
876 
877 		if (!buffer_is_empty(con->request.pathinfo)) {
878 			cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
879 		}
880 		cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
881 		if (!buffer_is_empty(con->uri.query)) {
882 			cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
883 		}
884 		if (!buffer_is_empty(con->request.orig_uri)) {
885 			cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
886 		}
887 
888 
889 		switch (con->dst_addr.plain.sa_family) {
890 #ifdef HAVE_IPV6
891 		case AF_INET6:
892 			s = inet_ntop(con->dst_addr.plain.sa_family,
893 			              (const void *) &(con->dst_addr.ipv6.sin6_addr),
894 			              b2, sizeof(b2)-1);
895 			break;
896 		case AF_INET:
897 			s = inet_ntop(con->dst_addr.plain.sa_family,
898 			              (const void *) &(con->dst_addr.ipv4.sin_addr),
899 			              b2, sizeof(b2)-1);
900 			break;
901 #else
902 		case AF_INET:
903 			s = inet_ntoa(con->dst_addr.ipv4.sin_addr);
904 			break;
905 #endif
906 		default:
907 			s = "";
908 			break;
909 		}
910 		cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
911 
912 		LI_ltostr(buf,
913 #ifdef HAVE_IPV6
914 			ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
915 #else
916 			ntohs(con->dst_addr.ipv4.sin_port)
917 #endif
918 			);
919 		cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
920 
921 		if (!buffer_is_empty(con->authed_user)) {
922 			cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"),
923 				    CONST_BUF_LEN(con->authed_user));
924 		}
925 
926 #ifdef USE_OPENSSL
927 	if (srv_sock->is_ssl) {
928 		cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
929 	}
930 #endif
931 
932 		/* request.content_length < SSIZE_MAX, see request.c */
933 		LI_ltostr(buf, con->request.content_length);
934 		cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
935 		cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
936 		cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
937 		cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
938 
939 		/* for valgrind */
940 		if (NULL != (s = getenv("LD_PRELOAD"))) {
941 			cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
942 		}
943 
944 		if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
945 			cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
946 		}
947 #ifdef __CYGWIN__
948 		/* CYGWIN needs SYSTEMROOT */
949 		if (NULL != (s = getenv("SYSTEMROOT"))) {
950 			cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
951 		}
952 #endif
953 
954 		for (n = 0; n < con->request.headers->used; n++) {
955 			data_string *ds;
956 
957 			ds = (data_string *)con->request.headers->data[n];
958 
959 			if (ds->value->used && ds->key->used) {
960 				size_t j;
961 
962 				buffer_reset(p->tmp_buf);
963 
964 				if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) {
965 					buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_"));
966 					p->tmp_buf->used--; /* strip \0 after HTTP_ */
967 				}
968 
969 				buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
970 
971 				for (j = 0; j < ds->key->used - 1; j++) {
972 					char cr = '_';
973 					if (light_isalpha(ds->key->ptr[j])) {
974 						/* upper-case */
975 						cr = ds->key->ptr[j] & ~32;
976 					} else if (light_isdigit(ds->key->ptr[j])) {
977 						/* copy */
978 						cr = ds->key->ptr[j];
979 					}
980 					p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
981 				}
982 				p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
983 
984 				cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
985 			}
986 		}
987 
988 		for (n = 0; n < con->environment->used; n++) {
989 			data_string *ds;
990 
991 			ds = (data_string *)con->environment->data[n];
992 
993 			if (ds->value->used && ds->key->used) {
994 				size_t j;
995 
996 				buffer_reset(p->tmp_buf);
997 
998 				buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
999 
1000 				for (j = 0; j < ds->key->used - 1; j++) {
1001 					char cr = '_';
1002 					if (light_isalpha(ds->key->ptr[j])) {
1003 						/* upper-case */
1004 						cr = ds->key->ptr[j] & ~32;
1005 					} else if (light_isdigit(ds->key->ptr[j])) {
1006 						/* copy */
1007 						cr = ds->key->ptr[j];
1008 					}
1009 					p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
1010 				}
1011 				p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
1012 
1013 				cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1014 			}
1015 		}
1016 
1017 		if (env.size == env.used) {
1018 			env.size += 16;
1019 			env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
1020 		}
1021 
1022 		env.ptr[env.used] = NULL;
1023 
1024 		/* set up args */
1025 		argc = 3;
1026 		args = malloc(sizeof(*args) * argc);
1027 		i = 0;
1028 
1029 		if (cgi_handler->used > 1) {
1030 			args[i++] = cgi_handler->ptr;
1031 		}
1032 		args[i++] = con->physical.path->ptr;
1033 		args[i  ] = NULL;
1034 
1035 		/* search for the last / */
1036 		if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
1037 			*c = '\0';
1038 
1039 			/* change to the physical directory */
1040 			if (-1 == chdir(con->physical.path->ptr)) {
1041 				log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
1042 			}
1043 			*c = '/';
1044 		}
1045 
1046 		/* we don't need the client socket */
1047 		for (i = 3; i < 256; i++) {
1048 			if (i != srv->errorlog_fd) close(i);
1049 		}
1050 
1051 		/* exec the cgi */
1052 		execve(args[0], args, env.ptr);
1053 
1054 		/* log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); */
1055 
1056 		/* */
1057 		SEGFAULT();
1058 		break;
1059 	}
1060 	case -1:
1061 		/* error */
1062 		log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
1063 		close(from_cgi_fds[0]);
1064 		close(from_cgi_fds[1]);
1065 		close(to_cgi_fds[0]);
1066 		close(to_cgi_fds[1]);
1067 		return -1;
1068 		break;
1069 	default: {
1070 		handler_ctx *hctx;
1071 		/* father */
1072 
1073 		close(from_cgi_fds[1]);
1074 		close(to_cgi_fds[0]);
1075 
1076 		if (con->request.content_length) {
1077 			chunkqueue *cq = con->request_content_queue;
1078 			chunk *c;
1079 
1080 			assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
1081 
1082 			/* there is content to send */
1083 			for (c = cq->first; c; c = cq->first) {
1084 				int r = 0;
1085 
1086 				/* copy all chunks */
1087 				switch(c->type) {
1088 				case FILE_CHUNK:
1089 
1090 					if (c->file.mmap.start == MAP_FAILED) {
1091 						if (-1 == c->file.fd &&  /* open the file if not already open */
1092 						    -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
1093 							log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
1094 
1095 							close(from_cgi_fds[0]);
1096 							close(to_cgi_fds[1]);
1097 							return -1;
1098 						}
1099 
1100 						c->file.mmap.length = c->file.length;
1101 
1102 						if (MAP_FAILED == (c->file.mmap.start = mmap(NULL,  c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
1103 							log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
1104 									strerror(errno), c->file.name,  c->file.fd);
1105 
1106 							close(from_cgi_fds[0]);
1107 							close(to_cgi_fds[1]);
1108 							return -1;
1109 						}
1110 
1111 						close(c->file.fd);
1112 						c->file.fd = -1;
1113 
1114 						/* chunk_reset() or chunk_free() will cleanup for us */
1115 					}
1116 
1117 					if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
1118 						switch(errno) {
1119 						case ENOSPC:
1120 							con->http_status = 507;
1121 							break;
1122 						case EINTR:
1123 							continue;
1124 						default:
1125 							con->http_status = 403;
1126 							break;
1127 						}
1128 					}
1129 					break;
1130 				case MEM_CHUNK:
1131 					if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
1132 						switch(errno) {
1133 						case ENOSPC:
1134 							con->http_status = 507;
1135 							break;
1136 						case EINTR:
1137 							continue;
1138 						default:
1139 							con->http_status = 403;
1140 							break;
1141 						}
1142 					}
1143 					break;
1144 				case UNUSED_CHUNK:
1145 					break;
1146 				}
1147 
1148 				if (r > 0) {
1149 					c->offset += r;
1150 					cq->bytes_out += r;
1151 				} else {
1152 					log_error_write(srv, __FILE__, __LINE__, "ss", "write() failed due to: ", strerror(errno));
1153 					con->http_status = 500;
1154 					break;
1155 				}
1156 				chunkqueue_remove_finished_chunks(cq);
1157 			}
1158 		}
1159 
1160 		close(to_cgi_fds[1]);
1161 
1162 		/* register PID and wait for them asyncronously */
1163 		con->mode = p->id;
1164 		buffer_reset(con->physical.path);
1165 
1166 		hctx = cgi_handler_ctx_init();
1167 
1168 		hctx->remote_conn = con;
1169 		hctx->plugin_data = p;
1170 		hctx->pid = pid;
1171 		hctx->fd = from_cgi_fds[0];
1172 		hctx->fde_ndx = -1;
1173 
1174 		con->plugin_ctx[p->id] = hctx;
1175 
1176 		fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
1177 		fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1178 
1179 		if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
1180 			log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
1181 
1182 			fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1183 			fdevent_unregister(srv->ev, hctx->fd);
1184 
1185 			log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd);
1186 
1187 			close(hctx->fd);
1188 
1189 			cgi_handler_ctx_free(hctx);
1190 
1191 			con->plugin_ctx[p->id] = NULL;
1192 
1193 			return -1;
1194 		}
1195 
1196 		break;
1197 	}
1198 	}
1199 
1200 	return 0;
1201 #else
1202 	return -1;
1203 #endif
1204 }
1205 
1206 #define PATCH(x) \
1207 	p->conf.x = s->x;
mod_cgi_patch_connection(server * srv,connection * con,plugin_data * p)1208 static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
1209 	size_t i, j;
1210 	plugin_config *s = p->config_storage[0];
1211 
1212 	PATCH(cgi);
1213 	PATCH(execute_x_only);
1214 
1215 	/* skip the first, the global context */
1216 	for (i = 1; i < srv->config_context->used; i++) {
1217 		data_config *dc = (data_config *)srv->config_context->data[i];
1218 		s = p->config_storage[i];
1219 
1220 		/* condition didn't match */
1221 		if (!config_check_cond(srv, con, dc)) continue;
1222 
1223 		/* merge config */
1224 		for (j = 0; j < dc->value->used; j++) {
1225 			data_unset *du = dc->value->data[j];
1226 
1227 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
1228 				PATCH(cgi);
1229 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) {
1230 				PATCH(execute_x_only);
1231 			}
1232 		}
1233 	}
1234 
1235 	return 0;
1236 }
1237 #undef PATCH
1238 
URIHANDLER_FUNC(cgi_is_handled)1239 URIHANDLER_FUNC(cgi_is_handled) {
1240 	size_t k, s_len;
1241 	plugin_data *p = p_d;
1242 	buffer *fn = con->physical.path;
1243 	stat_cache_entry *sce = NULL;
1244 
1245 	if (con->mode != DIRECT) return HANDLER_GO_ON;
1246 
1247 	if (fn->used == 0) return HANDLER_GO_ON;
1248 
1249 	mod_cgi_patch_connection(srv, con, p);
1250 
1251 	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON;
1252 	if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
1253 	if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1254 
1255 	s_len = fn->used - 1;
1256 
1257 	for (k = 0; k < p->conf.cgi->used; k++) {
1258 		data_string *ds = (data_string *)p->conf.cgi->data[k];
1259 		size_t ct_len = ds->key->used - 1;
1260 
1261 		if (ds->key->used == 0) continue;
1262 		if (s_len < ct_len) continue;
1263 
1264 		if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
1265 			if (cgi_create_env(srv, con, p, ds->value)) {
1266 				con->mode = DIRECT;
1267 				con->http_status = 500;
1268 
1269 				buffer_reset(con->physical.path);
1270 				return HANDLER_FINISHED;
1271 			}
1272 			/* one handler is enough for the request */
1273 			break;
1274 		}
1275 	}
1276 
1277 	return HANDLER_GO_ON;
1278 }
1279 
TRIGGER_FUNC(cgi_trigger)1280 TRIGGER_FUNC(cgi_trigger) {
1281 	plugin_data *p = p_d;
1282 	size_t ndx;
1283 	/* the trigger handle only cares about lonely PID which we have to wait for */
1284 #ifndef __WIN32
1285 
1286 	for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
1287 		int status;
1288 
1289 		switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
1290 		case 0:
1291 			/* not finished yet */
1292 #if 0
1293 			log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
1294 #endif
1295 			break;
1296 		case -1:
1297 			if (errno == ECHILD) {
1298 				/* someone else called waitpid... remove the pid to stop looping the error each time */
1299 				log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid");
1300 
1301 				cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1302 				ndx--;
1303 				continue;
1304 			}
1305 
1306 			log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1307 
1308 			return HANDLER_ERROR;
1309 		default:
1310 
1311 			if (WIFEXITED(status)) {
1312 #if 0
1313 				log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
1314 #endif
1315 			} else if (WIFSIGNALED(status)) {
1316 				/* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
1317 				 */
1318 				if (WTERMSIG(status) != SIGTERM) {
1319 					log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
1320 				}
1321 			} else {
1322 				log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
1323 			}
1324 
1325 			cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1326 			/* del modified the buffer structure
1327 			 * and copies the last entry to the current one
1328 			 * -> recheck the current index
1329 			 */
1330 			ndx--;
1331 		}
1332 	}
1333 #endif
1334 	return HANDLER_GO_ON;
1335 }
1336 
1337 /*
1338  * - HANDLER_GO_ON : not our job
1339  * - HANDLER_FINISHED: got response header
1340  * - HANDLER_WAIT_FOR_EVENT: waiting for response header
1341  */
SUBREQUEST_FUNC(mod_cgi_handle_subrequest)1342 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
1343 	int status;
1344 	plugin_data *p = p_d;
1345 	handler_ctx *hctx = con->plugin_ctx[p->id];
1346 
1347 	if (con->mode != p->id) return HANDLER_GO_ON;
1348 	if (NULL == hctx) return HANDLER_GO_ON;
1349 
1350 #if 0
1351 	log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
1352 #endif
1353 
1354 	if (hctx->pid == 0) {
1355 		/* cgi already dead */
1356 		if (!con->file_started) return HANDLER_WAIT_FOR_EVENT;
1357 		return HANDLER_FINISHED;
1358 	}
1359 
1360 #ifndef __WIN32
1361 	switch(waitpid(hctx->pid, &status, WNOHANG)) {
1362 	case 0:
1363 		/* we only have for events here if we don't have the header yet,
1364 		 * otherwise the event-handler will send us the incoming data */
1365 		if (con->file_started) return HANDLER_FINISHED;
1366 
1367 		return HANDLER_WAIT_FOR_EVENT;
1368 	case -1:
1369 		if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT;
1370 
1371 		if (errno == ECHILD && con->file_started == 0) {
1372 			/*
1373 			 * second round but still not response
1374 			 */
1375 			return HANDLER_WAIT_FOR_EVENT;
1376 		}
1377 
1378 		log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1379 		con->mode = DIRECT;
1380 		con->http_status = 500;
1381 
1382 		hctx->pid = 0;
1383 
1384 		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1385 		fdevent_unregister(srv->ev, hctx->fd);
1386 
1387 		if (close(hctx->fd)) {
1388 			log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
1389 		}
1390 
1391 		cgi_handler_ctx_free(hctx);
1392 
1393 		con->plugin_ctx[p->id] = NULL;
1394 
1395 		return HANDLER_FINISHED;
1396 	default:
1397 		/* cgi process exited
1398 		 */
1399 
1400 		hctx->pid = 0;
1401 
1402 		/* we already have response headers? just continue */
1403 		if (con->file_started) return HANDLER_FINISHED;
1404 
1405 		if (WIFEXITED(status)) {
1406 			/* clean exit - just continue */
1407 			return HANDLER_WAIT_FOR_EVENT;
1408 		}
1409 
1410 		/* cgi proc died, and we didn't get any data yet - send error message and close cgi con */
1411 		log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?");
1412 
1413 		con->http_status = 500;
1414 		con->mode = DIRECT;
1415 
1416 		fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1417 		fdevent_unregister(srv->ev, hctx->fd);
1418 
1419 		if (close(hctx->fd)) {
1420 			log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
1421 		}
1422 
1423 		cgi_handler_ctx_free(hctx);
1424 
1425 		con->plugin_ctx[p->id] = NULL;
1426 		return HANDLER_FINISHED;
1427 	}
1428 #else
1429 	return HANDLER_ERROR;
1430 #endif
1431 }
1432 
1433 
1434 int mod_cgi_plugin_init(plugin *p);
mod_cgi_plugin_init(plugin * p)1435 int mod_cgi_plugin_init(plugin *p) {
1436 	p->version     = LIGHTTPD_VERSION_ID;
1437 	p->name        = buffer_init_string("cgi");
1438 
1439 	p->connection_reset = cgi_connection_close_callback;
1440 	p->handle_subrequest_start = cgi_is_handled;
1441 	p->handle_subrequest = mod_cgi_handle_subrequest;
1442 #if 0
1443 	p->handle_fdevent = cgi_handle_fdevent;
1444 #endif
1445 	p->handle_trigger = cgi_trigger;
1446 	p->init           = mod_cgi_init;
1447 	p->cleanup        = mod_cgi_free;
1448 	p->set_defaults   = mod_fastcgi_set_defaults;
1449 
1450 	p->data        = NULL;
1451 
1452 	return 0;
1453 }
1454