"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "mod_fastcgi.c":
2.4.6_vs_2.4.7-0910052141.
1 /*
2 * mod_fastcgi.c --
3 *
4 * Apache server module for FastCGI.
5 *
6 * $Id: mod_fastcgi.c,v 1.169 2008/11/09 14:31:03 robs Exp $
7 *
8 * Copyright (c) 1995-1996 Open Market, Inc.
9 *
10 * See the file "LICENSE.TERMS" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 *
14 * Patches for Apache-1.1 provided by
15 * Ralf S. Engelschall
16 * <rse@en.muc.de>
17 *
18 * Patches for Linux provided by
19 * Scott Langley
20 * <langles@vote-smart.org>
21 *
22 * Patches for suexec handling by
23 * Brian Grossman <brian@SoftHome.net> and
24 * Rob Saccoccio <robs@ipass.net>
25 */
26
27 /*
28 * Module design notes.
29 *
30 * 1. Restart cleanup.
31 *
32 * mod_fastcgi spawns several processes: one process manager process
33 * and several application processes. None of these processes
34 * handle SIGHUP, so they just go away when the Web server performs
35 * a restart (as Apache does every time it starts.)
36 *
37 * In order to allow the process manager to properly cleanup the
38 * running fastcgi processes (without being disturbed by Apache),
39 * an intermediate process was introduced. The diagram is as follows;
40 *
41 * ApacheWS --> MiddleProc --> ProcMgr --> FCGI processes
42 *
43 * On a restart, ApacheWS sends a SIGKILL to MiddleProc and then
44 * collects it via waitpid(). The ProcMgr periodically checks for
45 * its parent (via getppid()) and if it does not have one, as in
46 * case when MiddleProc has terminated, ProcMgr issues a SIGTERM
47 * to all FCGI processes, waitpid()s on them and then exits, so it
48 * can be collected by init(1). Doing it any other way (short of
49 * changing Apache API), results either in inconsistent results or
50 * in generation of zombie processes.
51 *
52 * XXX: How does Apache 1.2 implement "gentle" restart
53 * that does not disrupt current connections? How does
54 * gentle restart interact with restart cleanup?
55 *
56 * 2. Request timeouts.
57 *
58 * Earlier versions of this module used ap_soft_timeout() rather than
59 * ap_hard_timeout() and ate FastCGI server output until it completed.
60 * This precluded the FastCGI server from having to implement a
61 * SIGPIPE handler, but meant hanging the application longer than
62 * necessary. SIGPIPE handler now must be installed in ALL FastCGI
63 * applications. The handler should abort further processing and go
64 * back into the accept() loop.
65 *
66 * Although using ap_soft_timeout() is better than ap_hard_timeout()
67 * we have to be more careful about SIGINT handling and subsequent
68 * processing, so, for now, make it hard.
69 */
70
71
72 #include "fcgi.h"
73
74 #ifdef APACHE2
75 #ifndef WIN32
76
77 #include <unistd.h>
78
79 #if APR_HAVE_CTYPE_H
80 #include <ctype.h>
81 #endif
82
83 #include "unixd.h"
84
85 #endif
86 #endif
87
88 #ifndef timersub
89 #define timersub(a, b, result) \
90 do { \
91 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
92 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
93 if ((result)->tv_usec < 0) { \
94 --(result)->tv_sec; \
95 (result)->tv_usec += 1000000; \
96 } \
97 } while (0)
98 #endif
99
100 /*
101 * Global variables
102 */
103
104 pool *fcgi_config_pool; /* the config pool */
105 server_rec *fcgi_apache_main_server;
106
107 const char *fcgi_wrapper = NULL; /* wrapper path */
108 uid_t fcgi_user_id; /* the run uid of Apache & PM */
109 gid_t fcgi_group_id; /* the run gid of Apache & PM */
110
111 fcgi_server *fcgi_servers = NULL; /* AppClasses */
112
113 char *fcgi_socket_dir = NULL; /* default FastCgiIpcDir */
114
115 char *fcgi_dynamic_dir = NULL; /* directory for the dynamic
116 * fastcgi apps' sockets */
117
118 #ifdef WIN32
119
120 #pragma warning( disable : 4706 4100 4127)
121 fcgi_pm_job *fcgi_dynamic_mbox = NULL;
122 HANDLE *fcgi_dynamic_mbox_mutex = NULL;
123 HANDLE fcgi_pm_thread = INVALID_HANDLE_VALUE;
124
125 #else
126
127 int fcgi_pm_pipe[2] = { -1, -1 };
128 pid_t fcgi_pm_pid = -1;
129
130 #endif
131
132 char *fcgi_empty_env = NULL;
133
134 u_int dynamicMaxProcs = FCGI_DEFAULT_MAX_PROCS;
135 int dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS;
136 int dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS;
137 u_int dynamicKillInterval = FCGI_DEFAULT_KILL_INTERVAL;
138 u_int dynamicUpdateInterval = FCGI_DEFAULT_UPDATE_INTERVAL;
139 float dynamicGain = FCGI_DEFAULT_GAIN;
140 int dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1;
141 int dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N;
142 u_int dynamicPleaseStartDelay = FCGI_DEFAULT_START_PROCESS_DELAY;
143 u_int dynamicAppConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
144 char **dynamicEnvp = &fcgi_empty_env;
145 u_int dynamicProcessSlack = FCGI_DEFAULT_PROCESS_SLACK;
146 int dynamicAutoRestart = FCGI_DEFAULT_RESTART_DYNAMIC;
147 int dynamicAutoUpdate = FCGI_DEFAULT_AUTOUPDATE;
148 int dynamicFlush = FCGI_FLUSH;
149 u_int dynamicListenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
150 u_int dynamicInitStartDelay = DEFAULT_INIT_START_DELAY;
151 u_int dynamicRestartDelay = FCGI_DEFAULT_RESTART_DELAY;
152 array_header *dynamic_pass_headers = NULL;
153 u_int dynamic_idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
154 int dynamicMinServerLife = FCGI_DEFAULT_MIN_SERVER_LIFE;
155
156 /*******************************************************************************
157 * Construct a message and write it to the pm_pipe.
158 */
159 static void send_to_pm(const char id, const char * const fs_path,
160 const char *user, const char * const group, const unsigned long q_usec,
161 const unsigned long req_usec)
162 {
163 #ifdef WIN32
164 fcgi_pm_job *job = NULL;
165
166 if (!(job = (fcgi_pm_job *) malloc(sizeof(fcgi_pm_job))))
167 return;
168 #else
169 static int failed_count = 0;
170 int buflen = 0;
171 char buf[FCGI_MAX_MSG_LEN];
172 #endif
173
174 if (strlen(fs_path) > FCGI_MAXPATH) {
175 ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server,
176 "FastCGI: the path \"%s\" is too long (>%d) for a dynamic server", fs_path, FCGI_MAXPATH);
177 return;
178 }
179
180 switch(id) {
181
182 case FCGI_SERVER_START_JOB:
183 case FCGI_SERVER_RESTART_JOB:
184 #ifdef WIN32
185 job->id = id;
186 job->fs_path = strdup(fs_path);
187 job->user = strdup(user);
188 job->group = strdup(group);
189 job->qsec = 0L;
190 job->start_time = 0L;
191 #else
192 buflen = sprintf(buf, "%c %s %s %s*", id, fs_path, user, group);
193 #endif
194 break;
195
196 case FCGI_REQUEST_TIMEOUT_JOB:
197 #ifdef WIN32
198 job->id = id;
199 job->fs_path = strdup(fs_path);
200 job->user = strdup(user);
201 job->group = strdup(group);
202 job->qsec = 0L;
203 job->start_time = 0L;
204 #else
205 buflen = sprintf(buf, "%c %s %s %s*", id, fs_path, user, group);
206 #endif
207 break;
208
209 case FCGI_REQUEST_COMPLETE_JOB:
210 #ifdef WIN32
211 job->id = id;
212 job->fs_path = strdup(fs_path);
213 job->qsec = q_usec;
214 job->start_time = req_usec;
215 job->user = strdup(user);
216 job->group = strdup(group);
217 #else
218 buflen = sprintf(buf, "%c %s %s %s %lu %lu*", id, fs_path, user, group, q_usec, req_usec);
219 #endif
220 break;
221 }
222
223 #ifdef WIN32
224 if (fcgi_pm_add_job(job)) return;
225
226 SetEvent(fcgi_event_handles[MBOX_EVENT]);
227 #else
228 ASSERT(buflen <= FCGI_MAX_MSG_LEN);
229
230 /* There is no apache flag or function that can be used to id
231 * restart/shutdown pending so ignore the first few failures as
232 * once it breaks it will stay broke */
233 if (write(fcgi_pm_pipe[1], (const void *)buf, buflen) != buflen
234 && failed_count++ > 10)
235 {
236 ap_log_error(FCGI_LOG_WARN, fcgi_apache_main_server,
237 "FastCGI: write() to PM failed (ignore if a restart or shutdown is pending)");
238 }
239 #endif
240 }
241
242 /*
243 *----------------------------------------------------------------------
244 *
245 * init_module
246 *
247 * An Apache module initializer, called by the Apache core
248 * after reading the server config.
249 *
250 * Start the process manager no matter what, since there may be a
251 * request for dynamic FastCGI applications without any being
252 * configured as static applications. Also, check for the existence
253 * and create if necessary a subdirectory into which all dynamic
254 * sockets will go.
255 *
256 *----------------------------------------------------------------------
257 */
258 #ifdef APACHE2
259 static apcb_t init_module(apr_pool_t * p, apr_pool_t * plog,
260 apr_pool_t * tp, server_rec * s)
261 #else
262 static apcb_t init_module(server_rec *s, pool *p)
263 #endif
264 {
265 #ifndef WIN32
266 const char *err;
267 #endif
268
269 /* Register to reset to default values when the config pool is cleaned */
270 ap_block_alarms();
271 ap_register_cleanup(p, NULL, fcgi_config_reset_globals, ap_null_cleanup);
272 ap_unblock_alarms();
273
274 #ifdef APACHE2
275 ap_add_version_component(p, "mod_fastcgi/" MOD_FASTCGI_VERSION);
276 #else
277 ap_add_version_component("mod_fastcgi/" MOD_FASTCGI_VERSION);
278 #endif
279
280 fcgi_config_set_fcgi_uid_n_gid(1);
281
282 /* keep these handy */
283 fcgi_config_pool = p;
284 fcgi_apache_main_server = s;
285
286 #ifdef WIN32
287 if (fcgi_socket_dir == NULL)
288 fcgi_socket_dir = DEFAULT_SOCK_DIR;
289 fcgi_dynamic_dir = ap_pstrcat(p, fcgi_socket_dir, "dynamic", NULL);
290 #else
291
292 if (fcgi_socket_dir == NULL)
293 fcgi_socket_dir = ap_server_root_relative(p, DEFAULT_SOCK_DIR);
294
295 /* Create Unix/Domain socket directory */
296 if ((err = fcgi_config_make_dir(p, fcgi_socket_dir)))
297 ap_log_error(FCGI_LOG_ERR, s, "FastCGI: %s", err);
298
299 /* Create Dynamic directory */
300 if ((err = fcgi_config_make_dynamic_dir(p, 1)))
301 ap_log_error(FCGI_LOG_ERR, s, "FastCGI: %s", err);
302
303 /* Spawn the PM only once. Under Unix, Apache calls init() routines
304 * twice, once before detach() and once after. Win32 doesn't detach.
305 * Under DSO, DSO modules are unloaded between the two init() calls.
306 * Under Unix, the -X switch causes two calls to init() but no detach
307 * (but all subprocesses are wacked so the PM is toasted anyway)! */
308
309 #ifdef APACHE2
310 {
311 void * first_pass;
312 apr_pool_userdata_get(&first_pass, "mod_fastcgi", s->process->pool);
313 if (first_pass == NULL)
314 {
315 apr_pool_userdata_set((const void *)1, "mod_fastcgi",
316 apr_pool_cleanup_null, s->process->pool);
317 return APCB_OK;
318 }
319 }
320 #else /* !APACHE2 */
321
322 if (ap_standalone && ap_restart_time == 0)
323 return;
324
325 #endif
326
327 /* Create the pipe for comm with the PM */
328 if (pipe(fcgi_pm_pipe) < 0) {
329 ap_log_error(FCGI_LOG_ERR, s, "FastCGI: pipe() failed");
330 }
331
332 /* Start the Process Manager */
333
334 #ifdef APACHE2
335 {
336 apr_proc_t * proc = apr_palloc(p, sizeof(*proc));
337 apr_status_t rv;
338
339 rv = apr_proc_fork(proc, tp);
340
341 if (rv == APR_INCHILD)
342 {
343 /* child */
344 fcgi_pm_main(NULL);
345 exit(1);
346 }
347 else if (rv != APR_INPARENT)
348 {
349 return rv;
350 }
351
352 /* parent */
353
354 apr_pool_note_subprocess(p, proc, APR_KILL_ONLY_ONCE);
355 }
356 #else /* !APACHE2 */
357
358 fcgi_pm_pid = ap_spawn_child(p, fcgi_pm_main, NULL, kill_only_once, NULL, NULL, NULL);
359 if (fcgi_pm_pid <= 0) {
360 ap_log_error(FCGI_LOG_ALERT, s,
361 "FastCGI: can't start the process manager, spawn_child() failed");
362 }
363
364 #endif /* !APACHE2 */
365
366 close(fcgi_pm_pipe[0]);
367
368 #endif /* !WIN32 */
369
370 return APCB_OK;
371 }
372
373 #ifdef WIN32
374 #ifdef APACHE2
375 static apcb_t fcgi_child_exit(void * dc)
376 #else
377 static apcb_t fcgi_child_exit(server_rec *dc0, pool *dc1)
378 #endif
379 {
380 /* Signal the PM thread to exit*/
381 SetEvent(fcgi_event_handles[TERM_EVENT]);
382
383 /* Waiting on pm thread to exit */
384 WaitForSingleObject(fcgi_pm_thread, INFINITE);
385
386 return APCB_OK;
387 }
388 #endif /* WIN32 */
389
390 #ifdef APACHE2
391 static void fcgi_child_init(apr_pool_t * p, server_rec * dc)
392 #else
393 static void fcgi_child_init(server_rec *dc, pool *p)
394 #endif
395 {
396 #ifdef WIN32
397 /* Create the MBOX, TERM, and WAKE event handlers */
398 fcgi_event_handles[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
399 if (fcgi_event_handles[0] == NULL) {
400 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
401 "FastCGI: CreateEvent() failed");
402 }
403 fcgi_event_handles[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
404 if (fcgi_event_handles[1] == NULL) {
405 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
406 "FastCGI: CreateEvent() failed");
407 }
408 fcgi_event_handles[2] = CreateEvent(NULL, FALSE, FALSE, NULL);
409 if (fcgi_event_handles[2] == NULL) {
410 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
411 "FastCGI: CreateEvent() failed");
412 }
413
414 /* Create the mbox mutex (PM - request threads) */
415 fcgi_dynamic_mbox_mutex = CreateMutex(NULL, FALSE, NULL);
416 if (fcgi_dynamic_mbox_mutex == NULL) {
417 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
418 "FastCGI: CreateMutex() failed");
419 }
420
421 /* Spawn of the process manager thread */
422 fcgi_pm_thread = (HANDLE) _beginthread(fcgi_pm_main, 0, NULL);
423 if (fcgi_pm_thread == (HANDLE) -1) {
424 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
425 "_beginthread() failed to spawn the process manager");
426 }
427
428 #ifdef APACHE2
429 apr_pool_cleanup_register(p, NULL, fcgi_child_exit, fcgi_child_exit);
430 #endif
431 #endif
432 }
433
434 /*
435 *----------------------------------------------------------------------
436 *
437 * get_header_line --
438 *
439 * Terminate a line: scan to the next newline, scan back to the
440 * first non-space character and store a terminating zero. Return
441 * the next character past the end of the newline.
442 *
443 * If the end of the string is reached, ASSERT!
444 *
445 * If the FIRST character(s) in the line are '\n' or "\r\n", the
446 * first character is replaced with a NULL and next character
447 * past the newline is returned. NOTE: this condition supercedes
448 * the processing of RFC-822 continuation lines.
449 *
450 * If continuation is set to 'TRUE', then it parses a (possible)
451 * sequence of RFC-822 continuation lines.
452 *
453 * Results:
454 * As above.
455 *
456 * Side effects:
457 * Termination byte stored in string.
458 *
459 *----------------------------------------------------------------------
460 */
461 static char *get_header_line(char *start, int continuation)
462 {
463 char *p = start;
464 char *end = start;
465
466 if(p[0] == '\r' && p[1] == '\n') { /* If EOL in 1st 2 chars */
467 p++; /* point to \n and stop */
468 } else if(*p != '\n') {
469 if(continuation) {
470 while(*p != '\0') {
471 if(*p == '\n' && p[1] != ' ' && p[1] != '\t')
472 break;
473 p++;
474 }
475 } else {
476 while(*p != '\0' && *p != '\n') {
477 p++;
478 }
479 }
480 }
481
482 ASSERT(*p != '\0');
483 end = p;
484 end++;
485
486 /*
487 * Trim any trailing whitespace.
488 */
489 while(isspace((unsigned char)p[-1]) && p > start) {
490 p--;
491 }
492
493 *p = '\0';
494 return end;
495 }
496
497 #ifdef WIN32
498
499 static int set_nonblocking(const fcgi_request * fr, int nonblocking)
500 {
501 if (fr->using_npipe_io)
502 {
503 if (nonblocking)
504 {
505 DWORD mode = PIPE_NOWAIT | PIPE_READMODE_BYTE;
506 if (SetNamedPipeHandleState((HANDLE) fr->fd, &mode, NULL, NULL) == 0)
507 {
508 ap_log_rerror(FCGI_LOG_ERR, fr->r,
509 "FastCGI: SetNamedPipeHandleState() failed");
510 return -1;
511 }
512 }
513 }
514 else
515 {
516 unsigned long ioctl_arg = (nonblocking) ? 1 : 0;
517 if (ioctlsocket(fr->fd, FIONBIO, &ioctl_arg) != 0)
518 {
519 errno = WSAGetLastError();
520 ap_log_rerror(FCGI_LOG_ERR_ERRNO, fr->r,
521 "FastCGI: ioctlsocket() failed");
522 return -1;
523 }
524 }
525
526 return 0;
527 }
528
529 #else
530
531 static int set_nonblocking(const fcgi_request * fr, int nonblocking)
532 {
533 int nb_flag = 0;
534 int fd_flags = fcntl(fr->fd, F_GETFL, 0);
535
536 if (fd_flags < 0) return -1;
537
538 #if defined(O_NONBLOCK)
539 nb_flag = O_NONBLOCK;
540 #elif defined(O_NDELAY)
541 nb_flag = O_NDELAY;
542 #elif defined(FNDELAY)
543 nb_flag = FNDELAY;
544 #else
545 #error "TODO - don't read from app until all data from client is posted."
546 #endif
547
548 fd_flags = (nonblocking) ? (fd_flags | nb_flag) : (fd_flags & ~nb_flag);
549
550 return fcntl(fr->fd, F_SETFL, fd_flags);
551 }
552
553 #endif
554
555 /*******************************************************************************
556 * Close the connection to the FastCGI server. This is normally called by
557 * do_work(), but may also be called as in request pool cleanup.
558 */
559 static void close_connection_to_fs(fcgi_request *fr)
560 {
561 #ifdef WIN32
562
563 if (fr->fd != INVALID_SOCKET)
564 {
565 set_nonblocking(fr, FALSE);
566
567 if (fr->using_npipe_io)
568 {
569 CloseHandle((HANDLE) fr->fd);
570 }
571 else
572 {
573 /* abort the connection entirely */
574 struct linger linger = {0, 0};
575 setsockopt(fr->fd, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
576 closesocket(fr->fd);
577 }
578
579 fr->fd = INVALID_SOCKET;
580
581 #else /* ! WIN32 */
582
583 if (fr->fd >= 0)
584 {
585 struct linger linger = {0, 0};
586 set_nonblocking(fr, FALSE);
587 /* abort the connection entirely */
588 setsockopt(fr->fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
589 close(fr->fd);
590 fr->fd = -1;
591
592 #endif /* ! WIN32 */
593
594 if (fr->dynamic && fr->keepReadingFromFcgiApp == FALSE)
595 {
596 /* XXX FCGI_REQUEST_COMPLETE_JOB is only sent for requests which complete
597 * normally WRT the fcgi app. There is no data sent for
598 * connect() timeouts or requests which complete abnormally.
599 * KillDynamicProcs() and RemoveRecords() need to be looked at
600 * to be sure they can reasonably handle these cases before
601 * sending these sort of stats - theres some funk in there.
602 */
603 if (fcgi_util_ticks(&fr->completeTime) < 0)
604 {
605 /* there's no point to aborting the request, just log it */
606 ap_log_error(FCGI_LOG_ERR, fr->r->server, "FastCGI: can't get time of day");
607 }
608 }
609 }
610 }
611
612
613 /*
614 *----------------------------------------------------------------------
615 *
616 * process_headers --
617 *
618 * Call with r->parseHeader == SCAN_CGI_READING_HEADERS
619 * and initial script output in fr->header.
620 *
621 * If the initial script output does not include the header
622 * terminator ("\r\n\r\n") process_headers returns with no side
623 * effects, to be called again when more script output
624 * has been appended to fr->header.
625 *
626 * If the initial script output includes the header terminator,
627 * process_headers parses the headers and determines whether or
628 * not the remaining script output will be sent to the client.
629 * If so, process_headers sends the HTTP response headers to the
630 * client and copies any non-header script output to the output
631 * buffer reqOutbuf.
632 *
633 * Results:
634 * none.
635 *
636 * Side effects:
637 * May set r->parseHeader to:
638 * SCAN_CGI_FINISHED -- headers parsed, returning script response
639 * SCAN_CGI_BAD_HEADER -- malformed header from script
640 * SCAN_CGI_INT_REDIRECT -- handler should perform internal redirect
641 * SCAN_CGI_SRV_REDIRECT -- handler should return REDIRECT
642 *
643 *----------------------------------------------------------------------
644 */
645
646 static const char *process_headers(request_rec *r, fcgi_request *fr)
647 {
648 char *p, *next, *name, *value;
649 int len, flag;
650 int hasLocation = FALSE;
651
652 ASSERT(fr->parseHeader == SCAN_CGI_READING_HEADERS);
653
654 if (fr->header == NULL)
655 return NULL;
656
657 /*
658 * Do we have the entire header? Scan for the blank line that
659 * terminates the header.
660 */
661 p = (char *)fr->header->elts;
662 len = fr->header->nelts;
663 flag = 0;
664 while(len-- && flag < 2) {
665 switch(*p) {
666 case '\r':
667 break;
668 case '\n':
669 flag++;
670 break;
671 case '\0':
672 case '\v':
673 case '\f':
674 name = "Invalid Character";
675 goto BadHeader;
676 default:
677 flag = 0;
678 break;
679 }
680 p++;
681 }
682
683 /* Return (to be called later when we have more data)
684 * if we don't have an entire header. */
685 if (flag < 2)
686 return NULL;
687
688 /*
689 * Parse all the headers.
690 */
691 fr->parseHeader = SCAN_CGI_FINISHED;
692 next = (char *)fr->header->elts;
693 for(;;) {
694 next = get_header_line(name = next, TRUE);
695 if (*name == '\0') {
696 break;
697 }
698 if ((p = strchr(name, ':')) == NULL) {
699 goto BadHeader;
700 }
701 value = p + 1;
702 while (p != name && isspace((unsigned char)*(p - 1))) {
703 p--;
704 }
705 if (p == name) {
706 goto BadHeader;
707 }
708 *p = '\0';
709 if (strpbrk(name, " \t") != NULL) {
710 *p = ' ';
711 goto BadHeader;
712 }
713 while (isspace((unsigned char)*value)) {
714 value++;
715 }
716
717 if (strcasecmp(name, "Status") == 0) {
718 int statusValue = strtol(value, NULL, 10);
719
720 if (statusValue < 0) {
721 fr->parseHeader = SCAN_CGI_BAD_HEADER;
722 return ap_psprintf(r->pool, "invalid Status '%s'", value);
723 }
724 r->status = statusValue;
725 r->status_line = ap_pstrdup(r->pool, value);
726 continue;
727 }
728
729 if (fr->role == FCGI_RESPONDER) {
730 if (strcasecmp(name, "Content-type") == 0) {
731 #ifdef APACHE2
732 ap_set_content_type(r, value);
733 #else
734 r->content_type = ap_pstrdup(r->pool, value);
735 #endif
736 continue;
737 }
738
739 /*
740 * Special case headers that should not persist on error
741 * or across redirects, i.e. use headers_out rather than
742 * err_headers_out.
743 */
744
745 if (strcasecmp(name, "Location") == 0) {
746 hasLocation = TRUE;
747 ap_table_set(r->headers_out, name, value);
748 continue;
749 }
750
751 if (strcasecmp(name, "Content-Length") == 0) {
752 ap_table_set(r->headers_out, name, value);
753 continue;
754 }
755
756 /* If the script wants them merged, it can do it */
757 ap_table_add(r->err_headers_out, name, value);
758 continue;
759 }
760 else {
761 ap_table_add(fr->authHeaders, name, value);
762 }
763 }
764
765 if (fr->role != FCGI_RESPONDER)
766 return NULL;
767
768 /*
769 * Who responds, this handler or Apache?
770 */
771 if (hasLocation) {
772 const char *location = ap_table_get(r->headers_out, "Location");
773 /*
774 * Based on internal redirect handling in mod_cgi.c...
775 *
776 * If a script wants to produce its own Redirect
777 * body, it now has to explicitly *say* "Status: 302"
778 */
779 if (r->status == 200) {
780 if(location[0] == '/') {
781 /*
782 * Location is an relative path. This handler will
783 * consume all script output, then have Apache perform an
784 * internal redirect.
785 */
786 fr->parseHeader = SCAN_CGI_INT_REDIRECT;
787 return NULL;
788 } else {
789 /*
790 * Location is an absolute URL. If the script didn't
791 * produce a Content-type header, this handler will
792 * consume all script output and then have Apache generate
793 * its standard redirect response. Otherwise this handler
794 * will transmit the script's response.
795 */
796 fr->parseHeader = SCAN_CGI_SRV_REDIRECT;
797 return NULL;
798 }
799 }
800 }
801 /*
802 * We're responding. Send headers, buffer excess script output.
803 */
804 ap_send_http_header(r);
805
806 /* We need to reinstate our timeout, send_http_header() kill()s it */
807 ap_hard_timeout("FastCGI request processing", r);
808
809 if (r->header_only) {
810 /* we've got all we want from the server */
811 close_connection_to_fs(fr);
812 fr->exitStatusSet = 1;
813 fcgi_buf_reset(fr->clientOutputBuffer);
814 fcgi_buf_reset(fr->serverOutputBuffer);
815 return NULL;
816 }
817
818 len = fr->header->nelts - (next - fr->header->elts);
819
820 ASSERT(len >= 0);
821 ASSERT(BufferLength(fr->clientOutputBuffer) == 0);
822
823 if (BufferFree(fr->clientOutputBuffer) < len) {
824 fr->clientOutputBuffer = fcgi_buf_new(r->pool, len);
825 }
826
827 ASSERT(BufferFree(fr->clientOutputBuffer) >= len);
828
829 if (len > 0) {
830 int sent;
831 sent = fcgi_buf_add_block(fr->clientOutputBuffer, next, len);
832 ASSERT(sent == len);
833 }
834
835 return NULL;
836
837 BadHeader:
838 /* Log first line of a multi-line header */
839 if ((p = strpbrk(name, "\r\n")) != NULL)
840 *p = '\0';
841 fr->parseHeader = SCAN_CGI_BAD_HEADER;
842 return ap_psprintf(r->pool, "malformed header '%s'", name);
843 }
844
845 /*
846 * Read from the client filling both the FastCGI server buffer and the
847 * client buffer with the hopes of buffering the client data before
848 * making the connect() to the FastCGI server. This prevents slow
849 * clients from keeping the FastCGI server in processing longer than is
850 * necessary.
851 */
852 static int read_from_client_n_queue(fcgi_request *fr)
853 {
854 char *end;
855 int count;
856 long int countRead;
857
858 while (BufferFree(fr->clientInputBuffer) > 0 || BufferFree(fr->serverOutputBuffer) > 0) {
859 fcgi_protocol_queue_client_buffer(fr);
860
861 if (fr->expectingClientContent <= 0)
862 return OK;
863
864 fcgi_buf_get_free_block_info(fr->clientInputBuffer, &end, &count);
865 if (count == 0)
866 return OK;
867
868 if ((countRead = ap_get_client_block(fr->r, end, count)) < 0)
869 {
870 /* set the header scan state to done to prevent logging an error
871 * - hokey approach - probably should be using a unique value */
872 fr->parseHeader = SCAN_CGI_FINISHED;
873 return -1;
874 }
875
876 if (countRead == 0) {
877 fr->expectingClientContent = 0;
878 }
879 else {
880 fcgi_buf_add_update(fr->clientInputBuffer, countRead);
881 ap_reset_timeout(fr->r);
882 }
883 }
884 return OK;
885 }
886
887 static int write_to_client(fcgi_request *fr)
888 {
889 char *begin;
890 int count;
891 int rv;
892 #ifdef APACHE2
893 apr_bucket * bkt;
894 apr_bucket_brigade * bde;
895 apr_bucket_alloc_t * const bkt_alloc = fr->r->connection->bucket_alloc;
896 #endif
897
898 fcgi_buf_get_block_info(fr->clientOutputBuffer, &begin, &count);
899 if (count == 0)
900 return OK;
901
902 /* If fewer than count bytes are written, an error occured.
903 * ap_bwrite() typically forces a flushed write to the client, this
904 * effectively results in a block (and short packets) - it should
905 * be fixed, but I didn't win much support for the idea on new-httpd.
906 * So, without patching Apache, the best way to deal with this is
907 * to size the fcgi_bufs to hold all of the script output (within
908 * reason) so the script can be released from having to wait around
909 * for the transmission to the client to complete. */
910
911 #ifdef APACHE2
912
913 bde = apr_brigade_create(fr->r->pool, bkt_alloc);
914 bkt = apr_bucket_transient_create(begin, count, bkt_alloc);
915 APR_BRIGADE_INSERT_TAIL(bde, bkt);
916
917 if (fr->fs ? fr->fs->flush : dynamicFlush)
918 {
919 bkt = apr_bucket_flush_create(bkt_alloc);
920 APR_BRIGADE_INSERT_TAIL(bde, bkt);
921 }
922
923 rv = ap_pass_brigade(fr->r->output_filters, bde);
924
925 #elif defined(RUSSIAN_APACHE)
926
927 rv = (ap_rwrite(begin, count, fr->r) != count);
928
929 #else
930
931 rv = (ap_bwrite(fr->r->connection->client, begin, count) != count);
932
933 #endif
934
935 if (rv || fr->r->connection->aborted) {
936 ap_log_rerror(FCGI_LOG_INFO_NOERRNO, fr->r,
937 "FastCGI: client stopped connection before send body completed");
938 return -1;
939 }
940
941 #ifndef APACHE2
942
943 ap_reset_timeout(fr->r);
944
945 /* Don't bother with a wrapped buffer, limiting exposure to slow
946 * clients. The BUFF routines don't allow a writev from above,
947 * and don't always memcpy to minimize small write()s, this should
948 * be fixed, but I didn't win much support for the idea on
949 * new-httpd - I'll have to _prove_ its a problem first.. */
950
951 /* The default behaviour used to be to flush with every write, but this
952 * can tie up the FastCGI server longer than is necessary so its an option now */
953
954 if (fr->fs ? fr->fs->flush : dynamicFlush)
955 {
956 #ifdef RUSSIAN_APACHE
957 rv = ap_rflush(fr->r);
958 #else
959 rv = ap_bflush(fr->r->connection->client);
960 #endif
961
962 if (rv)
963 {
964 ap_log_rerror(FCGI_LOG_INFO_NOERRNO, fr->r,
965 "FastCGI: client stopped connection before send body completed");
966 return -1;
967 }
968
969 ap_reset_timeout(fr->r);
970 }
971
972 #endif /* !APACHE2 */
973
974 fcgi_buf_toss(fr->clientOutputBuffer, count);
975 return OK;
976 }
977
978 static void
979 get_request_identity(request_rec * const r,
980 uid_t * const uid,
981 gid_t * const gid)
982 {
983 #if defined(WIN32)
984 *uid = (uid_t) 0;
985 *gid = (gid_t) 0;
986 #elif defined(APACHE2)
987 ap_unix_identity_t * identity = ap_run_get_suexec_identity(r);
988 if (identity)
989 {
990 *uid = identity->uid;
991 *gid = identity->gid;
992 }
993 else
994 {
995 *uid = 0;
996 *gid = 0;
997 }
998 #else
999 *uid = r->server->server_uid;
1000 *gid = r->server->server_gid;
1001 #endif
1002 }
1003
1004 /*******************************************************************************
1005 * Determine the user and group the wrapper should be called with.
1006 * Based on code in Apache's create_argv_cmd() (util_script.c).
1007 */
1008 static void set_uid_n_gid(request_rec *r, const char **user, const char **group)
1009 {
1010 if (fcgi_wrapper == NULL) {
1011 *user = "-";
1012 *group = "-";
1013 return;
1014 }
1015
1016 if (strncmp("/~", r->uri, 2) == 0) {
1017 /* its a user dir uri, just send the ~user, and leave it to the PM */
1018 char *end = strchr(r->uri + 2, '/');
1019
1020 if (end)
1021 *user = memcpy(ap_pcalloc(r->pool, end - r->uri), r->uri + 1, end - r->uri - 1);
1022 else
1023 *user = ap_pstrdup(r->pool, r->uri + 1);
1024 *group = "-";
1025 }
1026 else {
1027 uid_t uid;
1028 gid_t gid;
1029
1030 get_request_identity(r, &uid, &gid);
1031
1032 *user = ap_psprintf(r->pool, "%ld", (long) uid);
1033 *group = ap_psprintf(r->pool, "%ld", (long) gid);
1034 }
1035 }
1036
1037 static void send_request_complete(fcgi_request *fr)
1038 {
1039 if (fr->completeTime.tv_sec)
1040 {
1041 struct timeval qtime, rtime;
1042
1043 timersub(&fr->queueTime, &fr->startTime, &qtime);
1044 timersub(&fr->completeTime, &fr->queueTime, &rtime);
1045
1046 send_to_pm(FCGI_REQUEST_COMPLETE_JOB, fr->fs_path,
1047 fr->user, fr->group,
1048 qtime.tv_sec * 1000000 + qtime.tv_usec,
1049 rtime.tv_sec * 1000000 + rtime.tv_usec);
1050 }
1051 }
1052
1053
1054 /*******************************************************************************
1055 * Connect to the FastCGI server.
1056 */
1057 static int open_connection_to_fs(fcgi_request *fr)
1058 {
1059 struct timeval tval;
1060 fd_set write_fds, read_fds;
1061 int status;
1062 request_rec * const r = fr->r;
1063 pool * const rp = r->pool;
1064 const char *socket_path = NULL;
1065 struct sockaddr *socket_addr = NULL;
1066 int socket_addr_len = 0;
1067 #ifndef WIN32
1068 const char *err = NULL;
1069 #endif
1070
1071 /* Create the connection point */
1072 if (fr->dynamic)
1073 {
1074 socket_path = fcgi_util_socket_hash_filename(rp, fr->fs_path, fr->user, fr->group);
1075 socket_path = fcgi_util_socket_make_path_absolute(rp, socket_path, 1);
1076
1077 #ifndef WIN32
1078 err = fcgi_util_socket_make_domain_addr(rp, (struct sockaddr_un **)&socket_addr,
1079 &socket_addr_len, socket_path);
1080 if (err) {
1081 ap_log_rerror(FCGI_LOG_ERR, r,
1082 "FastCGI: failed to connect to (dynamic) server \"%s\": "
1083 "%s", fr->fs_path, err);
1084 return FCGI_FAILED;
1085 }
1086 #endif
1087 }
1088 else
1089 {
1090 #ifdef WIN32
1091 if (fr->fs->dest_addr != NULL) {
1092 socket_addr = fr->fs->dest_addr;
1093 }
1094 else if (fr->fs->socket_addr) {
1095 socket_addr = fr->fs->socket_addr;
1096 }
1097 else {
1098 socket_path = fr->fs->socket_path;
1099 }
1100 #else
1101 socket_addr = fr->fs->socket_addr;
1102 #endif
1103 socket_addr_len = fr->fs->socket_addr_len;
1104 }
1105
1106 if (fr->dynamic)
1107 {
1108 #ifdef WIN32
1109 if (fr->fs && fr->fs->restartTime)
1110 #else
1111 struct stat sock_stat;
1112
1113 if (stat(socket_path, &sock_stat) == 0)
1114 #endif
1115 {
1116 /* It exists */
1117 if (dynamicAutoUpdate)
1118 {
1119 struct stat app_stat;
1120
1121 /* TODO: follow sym links */
1122
1123 if (stat(fr->fs_path, &app_stat) == 0)
1124 {
1125 #ifdef WIN32
1126 if (fr->fs->startTime < app_stat.st_mtime)
1127 #else
1128 if (sock_stat.st_mtime < app_stat.st_mtime)
1129 #endif
1130 {
1131 #ifndef WIN32
1132 struct timeval tv;
1133
1134 tv.tv_sec = 1;
1135 tv.tv_usec = 0;
1136 #endif
1137 /*
1138 * There's a newer one, request a restart.
1139 */
1140 send_to_pm(FCGI_SERVER_RESTART_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1141
1142 #ifdef WIN32
1143 Sleep(1000);
1144 #else
1145 /* Avoid sleep/alarm interactions */
1146 ap_select(0, NULL, NULL, NULL, &tv);
1147 #endif
1148 }
1149 }
1150 }
1151 }
1152 else
1153 {
1154 int i;
1155
1156 send_to_pm(FCGI_SERVER_START_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1157
1158 /* wait until it looks like its running - this shouldn't take
1159 * very long at all - the exception is when the sockets are
1160 * removed out from under a running application - the loop
1161 * limit addresses this (preventing spinning) */
1162
1163 for (i = 10; i > 0; i--)
1164 {
1165 #ifdef WIN32
1166 Sleep(500);
1167
1168 fr->fs = fcgi_util_fs_get_by_id(fr->fs_path, 0, 0);
1169
1170 if (fr->fs && fr->fs->restartTime)
1171 #else
1172 struct timeval tv;
1173
1174 tv.tv_sec = 0;
1175 tv.tv_usec = 500000;
1176
1177 /* Avoid sleep/alarm interactions */
1178 ap_select(0, NULL, NULL, NULL, &tv);
1179
1180 if (stat(socket_path, &sock_stat) == 0)
1181 #endif
1182 {
1183 break;
1184 }
1185 }
1186
1187 if (i <= 0)
1188 {
1189 ap_log_rerror(FCGI_LOG_ALERT, r,
1190 "FastCGI: failed to connect to (dynamic) server \"%s\": "
1191 "something is seriously wrong, any chance the "
1192 "socket/named_pipe directory was removed?, see the "
1193 "FastCgiIpcDir directive", fr->fs_path);
1194 return FCGI_FAILED;
1195 }
1196 }
1197 }
1198
1199 #ifdef WIN32
1200 if (socket_path)
1201 {
1202 BOOL ready;
1203 DWORD connect_time;
1204 int rv;
1205 HANDLE wait_npipe_mutex;
1206 DWORD interval;
1207 DWORD max_connect_time = FCGI_NAMED_PIPE_CONNECT_TIMEOUT;
1208
1209 fr->using_npipe_io = TRUE;
1210
1211 if (fr->dynamic)
1212 {
1213 interval = dynamicPleaseStartDelay * 1000;
1214
1215 if (dynamicAppConnectTimeout) {
1216 max_connect_time = dynamicAppConnectTimeout;
1217 }
1218 }
1219 else
1220 {
1221 interval = FCGI_NAMED_PIPE_CONNECT_TIMEOUT * 1000;
1222
1223 if (fr->fs->appConnectTimeout) {
1224 max_connect_time = fr->fs->appConnectTimeout;
1225 }
1226 }
1227
1228 fcgi_util_ticks(&fr->startTime);
1229
1230 {
1231 /* xxx this handle should live somewhere (see CloseHandle()s below too) */
1232 char * wait_npipe_mutex_name, * cp;
1233 wait_npipe_mutex_name = cp = ap_pstrdup(rp, socket_path);
1234 while ((cp = strchr(cp, '\\'))) *cp = '/';
1235
1236 wait_npipe_mutex = CreateMutex(NULL, FALSE, wait_npipe_mutex_name);
1237 }
1238
1239 if (wait_npipe_mutex == NULL)
1240 {
1241 ap_log_rerror(FCGI_LOG_ERR, r,
1242 "FastCGI: failed to connect to server \"%s\": "
1243 "can't create the WaitNamedPipe mutex", fr->fs_path);
1244 return FCGI_FAILED;
1245 }
1246
1247 SetLastError(ERROR_SUCCESS);
1248
1249 rv = WaitForSingleObject(wait_npipe_mutex, max_connect_time * 1000);
1250
1251 if (rv == WAIT_TIMEOUT || rv == WAIT_FAILED)
1252 {
1253 if (fr->dynamic)
1254 {
1255 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1256 }
1257 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
1258 "FastCGI: failed to connect to server \"%s\": "
1259 "wait for a npipe instance failed", fr->fs_path);
1260 FCGIDBG3("interval=%d, max_connect_time=%d", interval, max_connect_time);
1261 CloseHandle(wait_npipe_mutex);
1262 return FCGI_FAILED;
1263 }
1264
1265 fcgi_util_ticks(&fr->queueTime);
1266
1267 connect_time = fr->queueTime.tv_sec - fr->startTime.tv_sec;
1268
1269 if (fr->dynamic)
1270 {
1271 if (connect_time >= interval)
1272 {
1273 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1274 FCGIDBG4("connect_time=%d, interval=%d, max_connect_time=%d", connect_time, interval, max_connect_time);
1275 }
1276 if (max_connect_time - connect_time < interval)
1277 {
1278 interval = max_connect_time - connect_time;
1279 }
1280 }
1281 else
1282 {
1283 interval -= connect_time * 1000;
1284 }
1285
1286 for (;;)
1287 {
1288 ready = WaitNamedPipe(socket_path, interval);
1289
1290 if (ready)
1291 {
1292 fr->fd = (SOCKET) CreateFile(socket_path,
1293 GENERIC_READ | GENERIC_WRITE,
1294 FILE_SHARE_READ | FILE_SHARE_WRITE,
1295 NULL, /* no security attributes */
1296 OPEN_EXISTING, /* opens existing pipe */
1297 FILE_FLAG_OVERLAPPED,
1298 NULL); /* no template file */
1299
1300 if (fr->fd != (SOCKET) INVALID_HANDLE_VALUE)
1301 {
1302 ReleaseMutex(wait_npipe_mutex);
1303 CloseHandle(wait_npipe_mutex);
1304 fcgi_util_ticks(&fr->queueTime);
1305 FCGIDBG2("got npipe connect: %s", fr->fs_path);
1306 return FCGI_OK;
1307 }
1308
1309 if (GetLastError() != ERROR_PIPE_BUSY
1310 && GetLastError() != ERROR_FILE_NOT_FOUND)
1311 {
1312 ap_log_rerror(FCGI_LOG_ERR, r,
1313 "FastCGI: failed to connect to server \"%s\": "
1314 "CreateFile() failed", fr->fs_path);
1315 break;
1316 }
1317
1318 FCGIDBG2("missed npipe connect: %s", fr->fs_path);
1319 }
1320
1321 if (fr->dynamic)
1322 {
1323 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1324 }
1325
1326 fcgi_util_ticks(&fr->queueTime);
1327
1328 connect_time = fr->queueTime.tv_sec - fr->startTime.tv_sec;
1329
1330 FCGIDBG5("interval=%d, max_connect_time=%d, connect_time=%d, ready=%d", interval, max_connect_time, connect_time, ready);
1331
1332 if (connect_time >= max_connect_time)
1333 {
1334 ap_log_rerror(FCGI_LOG_ERR, r,
1335 "FastCGI: failed to connect to server \"%s\": "
1336 "CreateFile()/WaitNamedPipe() timed out", fr->fs_path);
1337 break;
1338 }
1339 }
1340
1341 ReleaseMutex(wait_npipe_mutex);
1342 CloseHandle(wait_npipe_mutex);
1343 fr->fd = INVALID_SOCKET;
1344 return FCGI_FAILED;
1345 }
1346
1347 #endif
1348
1349 /* Create the socket */
1350 fr->fd = socket(socket_addr->sa_family, SOCK_STREAM, 0);
1351
1352 #ifdef WIN32
1353 if (fr->fd == INVALID_SOCKET) {
1354 errno = WSAGetLastError(); /* Not sure this is going to work as expected */
1355 #else
1356 if (fr->fd < 0) {
1357 #endif
1358 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1359 "FastCGI: failed to connect to server \"%s\": "
1360 "socket() failed", fr->fs_path);
1361 return FCGI_FAILED;
1362 }
1363
1364 #ifndef WIN32
1365 if (fr->fd >= FD_SETSIZE) {
1366 ap_log_rerror(FCGI_LOG_ERR, r,
1367 "FastCGI: failed to connect to server \"%s\": "
1368 "socket file descriptor (%u) is larger than "
1369 "FD_SETSIZE (%u), you probably need to rebuild Apache with a "
1370 "larger FD_SETSIZE", fr->fs_path, fr->fd, FD_SETSIZE);
1371 return FCGI_FAILED;
1372 }
1373 #endif
1374
1375 /* If appConnectTimeout is non-zero, setup do a non-blocking connect */
1376 if ((fr->dynamic && dynamicAppConnectTimeout) || (!fr->dynamic && fr->fs->appConnectTimeout)) {
1377 set_nonblocking(fr, TRUE);
1378 }
1379
1380 if (fr->dynamic) {
1381 fcgi_util_ticks(&fr->startTime);
1382 }
1383
1384 /* Connect */
1385 do {
1386 if (connect(fr->fd, (struct sockaddr *) socket_addr, socket_addr_len) == 0) {
1387 goto ConnectionComplete;
1388 }
1389 } while (errno == EINTR);
1390
1391 #ifdef WIN32
1392
1393 errno = WSAGetLastError();
1394 if (errno != WSAEWOULDBLOCK) {
1395 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1396 "FastCGI: failed to connect to server \"%s\": "
1397 "connect() failed", fr->fs_path);
1398 return FCGI_FAILED;
1399 }
1400
1401 #else
1402
1403 /* ECONNREFUSED means the listen queue is full (or there isn't one).
1404 * With dynamic I can at least make sure the PM knows this is occuring */
1405 if (fr->dynamic && errno == ECONNREFUSED) {
1406 /* @@@ This might be better as some other "kind" of message */
1407 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1408
1409 errno = ECONNREFUSED;
1410 }
1411
1412 if (errno != EINPROGRESS) {
1413 ap_log_rerror(FCGI_LOG_ERR, r,
1414 "FastCGI: failed to connect to server \"%s\": "
1415 "connect() failed", fr->fs_path);
1416 return FCGI_FAILED;
1417 }
1418
1419 #endif
1420
1421 /* The connect() is non-blocking */
1422
1423 errno = 0;
1424
1425 if (fr->dynamic) {
1426 do {
1427 FD_ZERO(&write_fds);
1428 FD_SET(fr->fd, &write_fds);
1429 read_fds = write_fds;
1430 tval.tv_sec = dynamicPleaseStartDelay;
1431 tval.tv_usec = 0;
1432
1433 do {
1434 status = ap_select(fr->fd + 1, &read_fds, &write_fds, NULL, &tval);
1435 } while (status < 0 && errno == EINTR);
1436
1437 if (status < 0)
1438 break;
1439
1440 fcgi_util_ticks(&fr->queueTime);
1441
1442 if (status > 0)
1443 break;
1444
1445 /* select() timed out */
1446 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1447 } while ((fr->queueTime.tv_sec - fr->startTime.tv_sec) < (int)dynamicAppConnectTimeout);
1448
1449 /* XXX These can be moved down when dynamic vars live is a struct */
1450 if (status == 0) {
1451 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
1452 "FastCGI: failed to connect to server \"%s\": "
1453 "connect() timed out (appConnTimeout=%dsec)",
1454 fr->fs_path, dynamicAppConnectTimeout);
1455 return FCGI_FAILED;
1456 }
1457 } /* dynamic */
1458 else {
1459 tval.tv_sec = fr->fs->appConnectTimeout;
1460 tval.tv_usec = 0;
1461 FD_ZERO(&write_fds);
1462 FD_SET(fr->fd, &write_fds);
1463 read_fds = write_fds;
1464
1465 do {
1466 status = ap_select(fr->fd + 1, &read_fds, &write_fds, NULL, &tval);
1467 } while (status < 0 && errno == EINTR);
1468
1469 if (status == 0) {
1470 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
1471 "FastCGI: failed to connect to server \"%s\": "
1472 "connect() timed out (appConnTimeout=%dsec)",
1473 fr->fs_path, dynamicAppConnectTimeout);
1474 return FCGI_FAILED;
1475 }
1476 } /* !dynamic */
1477
1478 if (status < 0) {
1479 #ifdef WIN32
1480 errno = WSAGetLastError();
1481 #endif
1482 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1483 "FastCGI: failed to connect to server \"%s\": "
1484 "select() failed", fr->fs_path);
1485 return FCGI_FAILED;
1486 }
1487
1488 if (FD_ISSET(fr->fd, &write_fds) || FD_ISSET(fr->fd, &read_fds)) {
1489 int error = 0;
1490 NET_SIZE_T len = sizeof(error);
1491
1492 if (getsockopt(fr->fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) < 0) {
1493 /* Solaris pending error */
1494 #ifdef WIN32
1495 errno = WSAGetLastError();
1496 #endif
1497 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1498 "FastCGI: failed to connect to server \"%s\": "
1499 "select() failed (Solaris pending error)", fr->fs_path);
1500 return FCGI_FAILED;
1501 }
1502
1503 if (error != 0) {
1504 /* Berkeley-derived pending error */
1505 errno = error;
1506 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1507 "FastCGI: failed to connect to server \"%s\": "
1508 "select() failed (pending error)", fr->fs_path);
1509 return FCGI_FAILED;
1510 }
1511 }
1512 else {
1513 #ifdef WIN32
1514 errno = WSAGetLastError();
1515 #endif
1516 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
1517 "FastCGI: failed to connect to server \"%s\": "
1518 "select() error - THIS CAN'T HAPPEN!", fr->fs_path);
1519 return FCGI_FAILED;
1520 }
1521
1522 ConnectionComplete:
1523 /* Return to blocking mode if it was set up */
1524 if ((fr->dynamic && dynamicAppConnectTimeout) || (!fr->dynamic && fr->fs->appConnectTimeout)) {
1525 set_nonblocking(fr, FALSE);
1526 }
1527
1528 #ifdef TCP_NODELAY
1529 if (socket_addr->sa_family == AF_INET) {
1530 /* We shouldn't be sending small packets and there's no application
1531 * level ack of the data we send, so disable Nagle */
1532 int set = 1;
1533 setsockopt(fr->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set));
1534 }
1535 #endif
1536
1537 return FCGI_OK;
1538 }
1539
1540 static void sink_client_data(fcgi_request *fr)
1541 {
1542 char *base;
1543 int size;
1544
1545 fcgi_buf_reset(fr->clientInputBuffer);
1546 fcgi_buf_get_free_block_info(fr->clientInputBuffer, &base, &size);
1547 while (ap_get_client_block(fr->r, base, size) > 0);
1548 }
1549
1550 static apcb_t cleanup(void *data)
1551 {
1552 fcgi_request * const fr = (fcgi_request *) data;
1553
1554 if (fr == NULL) return APCB_OK;
1555
1556 /* its more than likely already run, but... */
1557 close_connection_to_fs(fr);
1558
1559 send_request_complete(fr);
1560
1561 if (fr->fs_stderr_len) {
1562 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r,
1563 "FastCGI: server \"%s\" stderr: %s", fr->fs_path, fr->fs_stderr);
1564 }
1565
1566 return APCB_OK;
1567 }
1568
1569 #ifdef WIN32
1570 static int npipe_io(fcgi_request * const fr)
1571 {
1572 request_rec * const r = fr->r;
1573 enum
1574 {
1575 STATE_ENV_SEND,
1576 STATE_CLIENT_RECV,
1577 STATE_SERVER_SEND,
1578 STATE_SERVER_RECV,
1579 STATE_CLIENT_SEND,
1580 STATE_ERROR
1581 }
1582 state = STATE_ENV_SEND;
1583 env_status env_status;
1584 int client_recv;
1585 int dynamic_first_recv = fr->dynamic;
1586 int idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout;
1587 int send_pending = 0;
1588 int recv_pending = 0;
1589 int client_send = 0;
1590 int rv;
1591 OVERLAPPED rov = { 0 };
1592 OVERLAPPED sov = { 0 };
1593 HANDLE events[2];
1594 struct timeval timeout;
1595 struct timeval dynamic_last_io_time;
1596 int did_io = 1;
1597 pool * const rp = r->pool;
1598 int is_connected = 0;
1599 DWORD recv_count = 0;
1600
1601 dynamic_last_io_time.tv_sec = 0;
1602 dynamic_last_io_time.tv_usec = 0;
1603
1604 if (fr->role == FCGI_RESPONDER)
1605 {
1606 client_recv = (fr->expectingClientContent != 0);
1607 }
1608
1609 idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout;
1610
1611 env_status.envp = NULL;
1612
1613 events[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
1614 events[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
1615 sov.hEvent = events[0];
1616 rov.hEvent = events[1];
1617
1618 if (fr->dynamic)
1619 {
1620 dynamic_last_io_time = fr->startTime;
1621
1622 if (dynamicAppConnectTimeout)
1623 {
1624 struct timeval qwait;
1625 timersub(&fr->queueTime, &fr->startTime, &qwait);
1626 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
1627 }
1628 }
1629
1630 ap_hard_timeout("FastCGI request processing", r);
1631
1632 while (state != STATE_CLIENT_SEND)
1633 {
1634 DWORD msec_timeout;
1635
1636 switch (state)
1637 {
1638 case STATE_ENV_SEND:
1639
1640 if (fcgi_protocol_queue_env(r, fr, &env_status) == 0)
1641 {
1642 goto SERVER_SEND;
1643 }
1644
1645 state = STATE_CLIENT_RECV;
1646
1647 /* fall through */
1648
1649 case STATE_CLIENT_RECV:
1650
1651 if (read_from_client_n_queue(fr) != OK)
1652 {
1653 state = STATE_ERROR;
1654 break;
1655 }
1656
1657 if (fr->eofSent)
1658 {
1659 state = STATE_SERVER_SEND;
1660 }
1661
1662 /* fall through */
1663
1664 SERVER_SEND:
1665
1666 case STATE_SERVER_SEND:
1667
1668 if (! is_connected)
1669 {
1670 if (open_connection_to_fs(fr) != FCGI_OK)
1671 {
1672 ap_kill_timeout(r);
1673 return HTTP_INTERNAL_SERVER_ERROR;
1674 }
1675
1676 is_connected = 1;
1677 }
1678
1679 if (! send_pending && BufferLength(fr->serverOutputBuffer))
1680 {
1681 Buffer * b = fr->serverOutputBuffer;
1682 DWORD sent, len;
1683
1684 len = min(b->length, b->data + b->size - b->begin);
1685
1686 if (WriteFile((HANDLE) fr->fd, b->begin, len, &sent, &sov))
1687 {
1688 /* sov.hEvent is set */
1689 fcgi_buf_removed(b, sent);
1690 }
1691 else if (GetLastError() == ERROR_IO_PENDING)
1692 {
1693 send_pending = 1;
1694 }
1695 else
1696 {
1697 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
1698 "\"%s\" aborted: WriteFile() failed", fr->fs_path);
1699 state = STATE_ERROR;
1700 break;
1701 }
1702 }
1703
1704 /* fall through */
1705
1706 case STATE_SERVER_RECV:
1707
1708 /*
1709 * Only get more data when the serverInputBuffer is empty.
1710 * Otherwise we may already have the END_REQUEST buffered
1711 * (but not processed) and a read on a closed named pipe
1712 * results in an error that is normally abnormal.
1713 */
1714 if (! recv_pending && BufferLength(fr->serverInputBuffer) == 0)
1715 {
1716 Buffer * b = fr->serverInputBuffer;
1717 DWORD rcvd, len;
1718
1719 len = min(b->size - b->length, b->data + b->size - b->end);
1720
1721 if (ReadFile((HANDLE) fr->fd, b->end, len, &rcvd, &rov))
1722 {
1723 fcgi_buf_added(b, rcvd);
1724 recv_count += rcvd;
1725 ResetEvent(rov.hEvent);
1726 if (dynamic_first_recv)
1727 {
1728 dynamic_first_recv = 0;
1729 }
1730 }
1731 else if (GetLastError() == ERROR_IO_PENDING)
1732 {
1733 recv_pending = 1;
1734 }
1735 else if (GetLastError() == ERROR_HANDLE_EOF)
1736 {
1737 fr->keepReadingFromFcgiApp = FALSE;
1738 state = STATE_CLIENT_SEND;
1739 ResetEvent(rov.hEvent);
1740 break;
1741 }
1742 else if (GetLastError() == ERROR_NO_DATA)
1743 {
1744 break;
1745 }
1746 else
1747 {
1748 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
1749 "\"%s\" aborted: ReadFile() failed", fr->fs_path);
1750 state = STATE_ERROR;
1751 break;
1752 }
1753 }
1754
1755 /* fall through */
1756
1757 case STATE_CLIENT_SEND:
1758
1759 if (client_send || ! BufferFree(fr->clientOutputBuffer))
1760 {
1761 if (write_to_client(fr))
1762 {
1763 state = STATE_ERROR;
1764 break;
1765 }
1766
1767 client_send = 0;
1768
1769 if (fcgi_protocol_dequeue(rp, fr))
1770 {
1771 state = STATE_ERROR;
1772 break;
1773 }
1774 }
1775
1776 break;
1777
1778 default:
1779
1780 ASSERT(0);
1781 }
1782
1783 if (state == STATE_ERROR)
1784 {
1785 break;
1786 }
1787
1788 /* setup the io timeout */
1789
1790 if (BufferLength(fr->clientOutputBuffer))
1791 {
1792 /* don't let client data sit too long, it might be a push */
1793 timeout.tv_sec = 0;
1794 timeout.tv_usec = 100000;
1795 }
1796 else if (dynamic_first_recv)
1797 {
1798 int delay;
1799 struct timeval qwait;
1800
1801 fcgi_util_ticks(&fr->queueTime);
1802
1803 if (did_io)
1804 {
1805 /* a send() succeeded last pass */
1806 dynamic_last_io_time = fr->queueTime;
1807 }
1808 else
1809 {
1810 /* timed out last pass */
1811 struct timeval idle_time;
1812
1813 timersub(&fr->queueTime, &dynamic_last_io_time, &idle_time);
1814
1815 if (idle_time.tv_sec > idle_timeout)
1816 {
1817 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1818 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm "
1819 "with (dynamic) server \"%s\" aborted: (first read) "
1820 "idle timeout (%d sec)", fr->fs_path, idle_timeout);
1821 state = STATE_ERROR;
1822 break;
1823 }
1824 }
1825
1826 timersub(&fr->queueTime, &fr->startTime, &qwait);
1827
1828 delay = dynamic_first_recv * dynamicPleaseStartDelay;
1829
1830 if (qwait.tv_sec < delay)
1831 {
1832 timeout.tv_sec = delay;
1833 timeout.tv_usec = 100000; /* fudge for select() slop */
1834 timersub(&timeout, &qwait, &timeout);
1835 }
1836 else
1837 {
1838 /* Killed time somewhere.. client read? */
1839 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1840 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
1841 timeout.tv_sec = dynamic_first_recv * dynamicPleaseStartDelay;
1842 timeout.tv_usec = 100000; /* fudge for select() slop */
1843 timersub(&timeout, &qwait, &timeout);
1844 }
1845 }
1846 else
1847 {
1848 timeout.tv_sec = idle_timeout;
1849 timeout.tv_usec = 0;
1850 }
1851
1852 /* require a pended recv otherwise the app can deadlock */
1853 if (recv_pending)
1854 {
1855 msec_timeout = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
1856
1857 rv = WaitForMultipleObjects(2, events, FALSE, msec_timeout);
1858
1859 if (rv == WAIT_TIMEOUT)
1860 {
1861 did_io = 0;
1862
1863 if (BufferLength(fr->clientOutputBuffer))
1864 {
1865 client_send = 1;
1866 }
1867 else if (dynamic_first_recv)
1868 {
1869 struct timeval qwait;
1870
1871 fcgi_util_ticks(&fr->queueTime);
1872 timersub(&fr->queueTime, &fr->startTime, &qwait);
1873
1874 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
1875
1876 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
1877 }
1878 else
1879 {
1880 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with "
1881 "server \"%s\" aborted: idle timeout (%d sec)",
1882 fr->fs_path, idle_timeout);
1883 state = STATE_ERROR;
1884 break;
1885 }
1886 }
1887 else
1888 {
1889 int i = rv - WAIT_OBJECT_0;
1890
1891 did_io = 1;
1892
1893 if (i == 0)
1894 {
1895 if (send_pending)
1896 {
1897 DWORD sent;
1898
1899 if (GetOverlappedResult((HANDLE) fr->fd, &sov, &sent, FALSE))
1900 {
1901 send_pending = 0;
1902 fcgi_buf_removed(fr->serverOutputBuffer, sent);
1903 }
1904 else
1905 {
1906 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
1907 "\"%s\" aborted: GetOverlappedResult() failed", fr->fs_path);
1908 state = STATE_ERROR;
1909 break;
1910 }
1911 }
1912
1913 ResetEvent(sov.hEvent);
1914 }
1915 else
1916 {
1917 DWORD rcvd;
1918
1919 ASSERT(i == 1);
1920
1921 recv_pending = 0;
1922 ResetEvent(rov.hEvent);
1923
1924 if (GetOverlappedResult((HANDLE) fr->fd, &rov, &rcvd, FALSE))
1925 {
1926 fcgi_buf_added(fr->serverInputBuffer, rcvd);
1927 if (dynamic_first_recv)
1928 {
1929 dynamic_first_recv = 0;
1930 }
1931 }
1932 else
1933 {
1934 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
1935 "\"%s\" aborted: GetOverlappedResult() failed", fr->fs_path);
1936 state = STATE_ERROR;
1937 break;
1938 }
1939 }
1940 }
1941 }
1942
1943 if (fcgi_protocol_dequeue(rp, fr))
1944 {
1945 state = STATE_ERROR;
1946 break;
1947 }
1948
1949 if (fr->parseHeader == SCAN_CGI_READING_HEADERS)
1950 {
1951 const char * err = process_headers(r, fr);
1952 if (err)
1953 {
1954 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
1955 "FastCGI: comm with server \"%s\" aborted: "
1956 "error parsing headers: %s", fr->fs_path, err);
1957 state = STATE_ERROR;
1958 break;
1959 }
1960 }
1961
1962 if (fr->exitStatusSet)
1963 {
1964 fr->keepReadingFromFcgiApp = FALSE;
1965 state = STATE_CLIENT_SEND;
1966 break;
1967 }
1968 }
1969
1970 if (! fr->exitStatusSet || ! fr->eofSent)
1971 {
1972 CancelIo((HANDLE) fr->fd);
1973 }
1974
1975 CloseHandle(rov.hEvent);
1976 CloseHandle(sov.hEvent);
1977
1978 return (state == STATE_ERROR);
1979 }
1980 #endif /* WIN32 */
1981
1982 static int socket_io(fcgi_request * const fr)
1983 {
1984 enum
1985 {
1986 STATE_SOCKET_NONE,
1987 STATE_ENV_SEND,
1988 STATE_CLIENT_RECV,
1989 STATE_SERVER_SEND,
1990 STATE_SERVER_RECV,
1991 STATE_CLIENT_SEND,
1992 STATE_ERROR,
1993 STATE_CLIENT_ERROR
1994 }
1995 state = STATE_ENV_SEND;
1996
1997 request_rec * const r = fr->r;
1998
1999 struct timeval timeout;
2000 struct timeval dynamic_last_io_time;
2001 fd_set read_set;
2002 fd_set write_set;
2003 int nfds = 0;
2004 int select_status = 1;
2005 int idle_timeout;
2006 int rv;
2007 int dynamic_first_recv = fr->dynamic ? 1 : 0;
2008 int client_send = FALSE;
2009 int client_recv = FALSE;
2010 env_status env;
2011 pool *rp = r->pool;
2012 int is_connected = 0;
2013
2014 dynamic_last_io_time.tv_sec = 0;
2015 dynamic_last_io_time.tv_usec = 0;
2016
2017 if (fr->role == FCGI_RESPONDER)
2018 {
2019 client_recv = (fr->expectingClientContent != 0);
2020 }
2021
2022 idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout;
2023
2024 env.envp = NULL;
2025
2026 if (fr->dynamic)
2027 {
2028 dynamic_last_io_time = fr->startTime;
2029
2030 if (dynamicAppConnectTimeout)
2031 {
2032 struct timeval qwait;
2033 timersub(&fr->queueTime, &fr->startTime, &qwait);
2034 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
2035 }
2036 }
2037
2038 ap_hard_timeout("FastCGI request processing", r);
2039
2040 for (;;)
2041 {
2042 FD_ZERO(&read_set);
2043 FD_ZERO(&write_set);
2044
2045 switch (state)
2046 {
2047 case STATE_ENV_SEND:
2048
2049 if (fcgi_protocol_queue_env(r, fr, &env) == 0)
2050 {
2051 goto SERVER_SEND;
2052 }
2053
2054 state = STATE_CLIENT_RECV;
2055
2056 /* fall through */
2057
2058 case STATE_CLIENT_RECV:
2059
2060 if (read_from_client_n_queue(fr))
2061 {
2062 state = STATE_CLIENT_ERROR;
2063 break;
2064 }
2065
2066 if (fr->eofSent)
2067 {
2068 state = STATE_SERVER_SEND;
2069 }
2070
2071 /* fall through */
2072
2073 SERVER_SEND:
2074
2075 case STATE_SERVER_SEND:
2076
2077 if (! is_connected)
2078 {
2079 if (open_connection_to_fs(fr) != FCGI_OK)
2080 {
2081 ap_kill_timeout(r);
2082 return HTTP_INTERNAL_SERVER_ERROR;
2083 }
2084
2085 set_nonblocking(fr, TRUE);
2086 is_connected = 1;
2087 nfds = fr->fd + 1;
2088 }
2089
2090 if (BufferLength(fr->serverOutputBuffer))
2091 {
2092 FD_SET(fr->fd, &write_set);
2093 }
2094 else
2095 {
2096 ASSERT(fr->eofSent);
2097 state = STATE_SERVER_RECV;
2098 }
2099
2100 /* fall through */
2101
2102 case STATE_SERVER_RECV:
2103
2104 FD_SET(fr->fd, &read_set);
2105
2106 /* fall through */
2107
2108 case STATE_CLIENT_SEND:
2109
2110 if (client_send || ! BufferFree(fr->clientOutputBuffer))
2111 {
2112 if (write_to_client(fr))
2113 {
2114 state = STATE_CLIENT_ERROR;
2115 break;
2116 }
2117
2118 client_send = 0;
2119
2120 if (fcgi_protocol_dequeue(rp, fr))
2121 {
2122 state = STATE_ERROR;
2123 break;
2124 }
2125 }
2126
2127 break;
2128
2129 case STATE_ERROR:
2130 case STATE_CLIENT_ERROR:
2131
2132 break;
2133
2134 default:
2135
2136 ASSERT(0);
2137 }
2138
2139 if (state == STATE_CLIENT_ERROR || state == STATE_ERROR)
2140 {
2141 break;
2142 }
2143
2144 /* setup the io timeout */
2145
2146 if (BufferLength(fr->clientOutputBuffer))
2147 {
2148 /* don't let client data sit too long, it might be a push */
2149 timeout.tv_sec = 0;
2150 timeout.tv_usec = 100000;
2151 }
2152 else if (dynamic_first_recv)
2153 {
2154 int delay;
2155 struct timeval qwait;
2156
2157 fcgi_util_ticks(&fr->queueTime);
2158
2159 if (select_status)
2160 {
2161 /* a send() succeeded last pass */
2162 dynamic_last_io_time = fr->queueTime;
2163 }
2164 else
2165 {
2166 /* timed out last pass */
2167 struct timeval idle_time;
2168
2169 timersub(&fr->queueTime, &dynamic_last_io_time, &idle_time);
2170
2171 if (idle_time.tv_sec > idle_timeout)
2172 {
2173 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
2174 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm "
2175 "with (dynamic) server \"%s\" aborted: (first read) "
2176 "idle timeout (%d sec)", fr->fs_path, idle_timeout);
2177 state = STATE_ERROR;
2178 break;
2179 }
2180 }
2181
2182 timersub(&fr->queueTime, &fr->startTime, &qwait);
2183
2184 delay = dynamic_first_recv * dynamicPleaseStartDelay;
2185
2186 FCGIDBG5("qwait=%ld.%06ld delay=%d first_recv=%d", qwait.tv_sec, qwait.tv_usec, delay, dynamic_first_recv);
2187
2188 if (qwait.tv_sec < delay)
2189 {
2190 timeout.tv_sec = delay;
2191 timeout.tv_usec = 100000; /* fudge for select() slop */
2192 timersub(&timeout, &qwait, &timeout);
2193 }
2194 else
2195 {
2196 /* Killed time somewhere.. client read? */
2197 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
2198 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
2199 timeout.tv_sec = dynamic_first_recv * dynamicPleaseStartDelay;
2200 timeout.tv_usec = 100000; /* fudge for select() slop */
2201 timersub(&timeout, &qwait, &timeout);
2202 }
2203 }
2204 else
2205 {
2206 timeout.tv_sec = idle_timeout;
2207 timeout.tv_usec = 0;
2208 }
2209
2210 /* wait on the socket */
2211 do {
2212 select_status = ap_select(nfds, &read_set, &write_set, NULL, &timeout);
2213 } while (select_status < 0 && errno == EINTR);
2214
2215 if (select_status < 0)
2216 {
2217 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: comm with server "
2218 "\"%s\" aborted: select() failed", fr->fs_path);
2219 state = STATE_ERROR;
2220 break;
2221 }
2222
2223 if (select_status == 0)
2224 {
2225 /* select() timeout */
2226
2227 if (BufferLength(fr->clientOutputBuffer))
2228 {
2229 if (fr->role == FCGI_RESPONDER)
2230 {
2231 client_send = TRUE;
2232 }
2233 }
2234 else if (dynamic_first_recv)
2235 {
2236 struct timeval qwait;
2237
2238 fcgi_util_ticks(&fr->queueTime);
2239 timersub(&fr->queueTime, &fr->startTime, &qwait);
2240
2241 send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0);
2242
2243 dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1;
2244 continue;
2245 }
2246 else
2247 {
2248 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with "
2249 "server \"%s\" aborted: idle timeout (%d sec)",
2250 fr->fs_path, idle_timeout);
2251 state = STATE_ERROR;
2252 }
2253 }
2254
2255 if (FD_ISSET(fr->fd, &write_set))
2256 {
2257 /* send to the server */
2258
2259 rv = fcgi_buf_socket_send(fr->serverOutputBuffer, fr->fd);
2260
2261 if (rv < 0)
2262 {
2263 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
2264 "\"%s\" aborted: write failed", fr->fs_path);
2265 state = STATE_ERROR;
2266 break;
2267 }
2268 }
2269
2270 if (FD_ISSET(fr->fd, &read_set))
2271 {
2272 /* recv from the server */
2273
2274 if (dynamic_first_recv)
2275 {
2276 dynamic_first_recv = 0;
2277 fcgi_util_ticks(&fr->queueTime);
2278 }
2279
2280 rv = fcgi_buf_socket_recv(fr->serverInputBuffer, fr->fd);
2281
2282 if (rv < 0)
2283 {
2284 if (errno == EAGAIN)
2285 {
2286 /* this reportedly occurs on AIX 5.2 sporadically */
2287 struct timeval tv;
2288 tv.tv_sec = 1;
2289 tv.tv_usec = 0;
2290
2291 ap_log_rerror(FCGI_LOG_INFO, r, "FastCGI: comm with server "
2292 "\"%s\" interrupted: read will be retried in 1 second",
2293 fr->fs_path);
2294
2295 /* avoid sleep/alarm interactions */
2296 ap_select(0, NULL, NULL, NULL, &tv);
2297 }
2298 else
2299 {
2300 ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server "
2301 "\"%s\" aborted: read failed", fr->fs_path);
2302 state = STATE_ERROR;
2303 break;
2304 }
2305 }
2306 else if (rv == 0)
2307 {
2308 fr->keepReadingFromFcgiApp = FALSE;
2309 state = STATE_CLIENT_SEND;
2310 break;
2311 }
2312 }
2313
2314 if (fcgi_protocol_dequeue(rp, fr))
2315 {
2316 state = STATE_ERROR;
2317 break;
2318 }
2319
2320 if (fr->parseHeader == SCAN_CGI_READING_HEADERS)
2321 {
2322 const char * err = process_headers(r, fr);
2323 if (err)
2324 {
2325 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2326 "FastCGI: comm with server \"%s\" aborted: "
2327 "error parsing headers: %s", fr->fs_path, err);
2328 state = STATE_ERROR;
2329 break;
2330 }
2331 }
2332
2333 if (fr->exitStatusSet)
2334 {
2335 fr->keepReadingFromFcgiApp = FALSE;
2336 state = STATE_CLIENT_SEND;
2337 break;
2338 }
2339 }
2340
2341 return (state == STATE_ERROR);
2342 }
2343
2344
2345 /*----------------------------------------------------------------------
2346 * This is the core routine for moving data between the FastCGI
2347 * application and the Web server's client.
2348 */
2349 static int do_work(request_rec * const r, fcgi_request * const fr)
2350 {
2351 int rv;
2352 pool *rp = r->pool;
2353
2354 fcgi_protocol_queue_begin_request(fr);
2355
2356 if (fr->role == FCGI_RESPONDER)
2357 {
2358 rv = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
2359 if (rv != OK)
2360 {
2361 ap_kill_timeout(r);
2362 return rv;
2363 }
2364
2365 fr->expectingClientContent = ap_should_client_block(r);
2366 }
2367
2368 ap_block_alarms();
2369 ap_register_cleanup(rp, (void *)fr, cleanup, ap_null_cleanup);
2370 ap_unblock_alarms();
2371
2372 #ifdef WIN32
2373 if (fr->using_npipe_io)
2374 {
2375 rv = npipe_io(fr);
2376 }
2377 else
2378 #endif
2379 {
2380 rv = socket_io(fr);
2381 }
2382
2383 /* comm with the server is done */
2384 close_connection_to_fs(fr);
2385
2386 if (fr->role == FCGI_RESPONDER)
2387 {
2388 sink_client_data(fr);
2389 }
2390
2391 while (rv == 0 && (BufferLength(fr->serverInputBuffer) || BufferLength(fr->clientOutputBuffer)))
2392 {
2393 if (fcgi_protocol_dequeue(rp, fr))
2394 {
2395 rv = HTTP_INTERNAL_SERVER_ERROR;
2396 }
2397
2398 if (fr->parseHeader == SCAN_CGI_READING_HEADERS)
2399 {
2400 const char * err = process_headers(r, fr);
2401 if (err)
2402 {
2403 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2404 "FastCGI: comm with server \"%s\" aborted: "
2405 "error parsing headers: %s", fr->fs_path, err);
2406 rv = HTTP_INTERNAL_SERVER_ERROR;
2407 }
2408 }
2409
2410 if (fr->role == FCGI_RESPONDER)
2411 {
2412 if (write_to_client(fr))
2413 {
2414 break;
2415 }
2416 }
2417 else
2418 {
2419 fcgi_buf_reset(fr->clientOutputBuffer);
2420 }
2421 }
2422
2423 switch (fr->parseHeader)
2424 {
2425 case SCAN_CGI_FINISHED:
2426
2427 if (fr->role == FCGI_RESPONDER)
2428 {
2429 /* RUSSIAN_APACHE requires rflush() over bflush() */
2430 ap_rflush(r);
2431 #ifndef APACHE2
2432 ap_bgetopt(r->connection->client, BO_BYTECT, &r->bytes_sent);
2433 #endif
2434 }
2435
2436 /* fall through */
2437
2438 case SCAN_CGI_INT_REDIRECT:
2439 case SCAN_CGI_SRV_REDIRECT:
2440
2441 break;
2442
2443 case SCAN_CGI_READING_HEADERS:
2444
2445 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: incomplete headers "
2446 "(%d bytes) received from server \"%s\"", fr->header->nelts, fr->fs_path);
2447
2448 /* fall through */
2449
2450 case SCAN_CGI_BAD_HEADER:
2451
2452 rv = HTTP_INTERNAL_SERVER_ERROR;
2453 break;
2454
2455 default:
2456
2457 ASSERT(0);
2458 rv = HTTP_INTERNAL_SERVER_ERROR;
2459 }
2460
2461 ap_kill_timeout(r);
2462 return rv;
2463 }
2464
2465 static int
2466 create_fcgi_request(request_rec * const r,
2467 const char * const path,
2468 fcgi_request ** const frP)
2469 {
2470 const char *fs_path;
2471 pool * const p = r->pool;
2472 fcgi_server *fs;
2473 fcgi_request * const fr = (fcgi_request *)ap_pcalloc(p, sizeof(fcgi_request));
2474 uid_t uid;
2475 gid_t gid;
2476
2477 fs_path = path ? path : r->filename;
2478
2479 get_request_identity(r, &uid, &gid);
2480
2481 fs = fcgi_util_fs_get_by_id(fs_path, uid, gid);
2482
2483 if (fs == NULL)
2484 {
2485 const char * err;
2486 struct stat *my_finfo;
2487
2488 /* dynamic? */
2489
2490 #ifndef APACHE2
2491 if (path == NULL)
2492 {
2493 /* AP2: its bogus that we don't make use of r->finfo, but
2494 * its an apr_finfo_t and there is no apr_os_finfo_get() */
2495
2496 my_finfo = &r->finfo;
2497 }
2498 else
2499 #endif
2500 {
2501 my_finfo = (struct stat *) ap_palloc(p, sizeof(struct stat));
2502
2503 if (stat(fs_path, my_finfo) < 0)
2504 {
2505 ap_log_rerror(FCGI_LOG_ERR_ERRNO, r,
2506 "FastCGI: stat() of \"%s\" failed", fs_path);
2507 return HTTP_NOT_FOUND;
2508 }
2509 }
2510
2511 err = fcgi_util_fs_is_path_ok(p, fs_path, my_finfo);
2512 if (err)
2513 {
2514 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2515 "FastCGI: invalid (dynamic) server \"%s\": %s", fs_path, err);
2516 return HTTP_FORBIDDEN;
2517 }
2518 }
2519
2520 fr->nph = (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
2521 || (fs && fs->nph);
2522
2523 fr->serverInputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE);
2524 fr->serverOutputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE);
2525 fr->clientInputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE);
2526 fr->clientOutputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE);
2527 fr->erBufPtr = fcgi_buf_new(p, sizeof(FCGI_EndRequestBody) + 1);
2528 fr->gotHeader = FALSE;
2529 fr->parseHeader = SCAN_CGI_READING_HEADERS;
2530 fr->header = ap_make_array(p, 1, 1);
2531 fr->fs_stderr = NULL;
2532 fr->r = r;
2533 fr->readingEndRequestBody = FALSE;
2534 fr->exitStatus = 0;
2535 fr->exitStatusSet = FALSE;
2536 fr->requestId = 1; /* anything but zero is OK here */
2537 fr->eofSent = FALSE;
2538 fr->role = FCGI_RESPONDER;
2539 fr->expectingClientContent = FALSE;
2540 fr->keepReadingFromFcgiApp = TRUE;
2541 fr->fs = fs;
2542 fr->fs_path = fs_path;
2543 fr->authHeaders = ap_make_table(p, 10);
2544 #ifdef WIN32
2545 fr->fd = INVALID_SOCKET;
2546 fr->dynamic = ((fs == NULL) || (fs->directive == APP_CLASS_DYNAMIC)) ? TRUE : FALSE;
2547 fr->using_npipe_io = (! fr->dynamic && (fs->dest_addr || fs->socket_addr)) ? 0 : 1;
2548 #else
2549 fr->dynamic = (fs == NULL) ? TRUE : FALSE;
2550 fr->fd = -1;
2551 #endif
2552
2553 if (fr->nph) {
2554 #ifdef APACHE2
2555 struct ap_filter_t *cur;
2556
2557 fr->parseHeader = SCAN_CGI_FINISHED;
2558
2559 /* remove the filters up through protocol - since the headers
2560 * haven't been parsed, there is no way they can work */
2561
2562 cur = r->proto_output_filters;
2563 while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) {
2564 cur = cur->next;
2565 }
2566 r->output_filters = r->proto_output_filters = cur;
2567 #else
2568 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2569 "FastCGI: invalid request \"%s\": non parsed header support is "
2570 "not available in Apache13 (patch welcome)", fs_path);
2571 return HTTP_FORBIDDEN;
2572 #endif
2573 }
2574
2575 set_uid_n_gid(r, &fr->user, &fr->group);
2576
2577 *frP = fr;
2578
2579 return OK;
2580 }
2581
2582 /*
2583 *----------------------------------------------------------------------
2584 *
2585 * handler --
2586 *
2587 * This routine gets called for a request that corresponds to
2588 * a FastCGI connection. It performs the request synchronously.
2589 *
2590 * Results:
2591 * Final status of request: OK or NOT_FOUND or HTTP_INTERNAL_SERVER_ERROR.
2592 *
2593 * Side effects:
2594 * Request performed.
2595 *
2596 *----------------------------------------------------------------------
2597 */
2598
2599 /* Stolen from mod_cgi.c..
2600 * KLUDGE --- for back-combatibility, we don't have to check ExecCGI
2601 * in ScriptAliased directories, which means we need to know if this
2602 * request came through ScriptAlias or not... so the Alias module
2603 * leaves a note for us.
2604 */
2605 static int apache_is_scriptaliased(request_rec *r)
2606 {
2607 const char *t = ap_table_get(r->notes, "alias-forced-type");
2608 return t && (!strcasecmp(t, "cgi-script"));
2609 }
2610
2611 /* If a script wants to produce its own Redirect body, it now
2612 * has to explicitly *say* "Status: 302". If it wants to use
2613 * Apache redirects say "Status: 200". See process_headers().
2614 */
2615 static int post_process_for_redirects(request_rec * const r,
2616 const fcgi_request * const fr)
2617 {
2618 switch(fr->parseHeader) {
2619 case SCAN_CGI_INT_REDIRECT:
2620
2621 /* @@@ There are still differences between the handling in
2622 * mod_cgi and mod_fastcgi. This needs to be revisited.
2623 */
2624 /* We already read the message body (if any), so don't allow
2625 * the redirected request to think it has one. We can ignore
2626 * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
2627 */
2628 r->method = "GET";
2629 r->method_number = M_GET;
2630 ap_table_unset(r->headers_in, "Content-length");
2631
2632 ap_internal_redirect_handler(ap_table_get(r->headers_out, "Location"), r);
2633 return OK;
2634
2635 case SCAN_CGI_SRV_REDIRECT:
2636 return HTTP_MOVED_TEMPORARILY;
2637
2638 default:
2639 #ifdef APACHE2
2640 {
2641 apr_bucket_brigade *brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
2642 apr_bucket* bucket = apr_bucket_eos_create(r->connection->bucket_alloc);
2643 APR_BRIGADE_INSERT_HEAD(brigade, bucket);
2644 return ap_pass_brigade(r->output_filters, brigade);
2645 }
2646 #else
2647 return OK;
2648 #endif
2649 }
2650 }
2651
2652 /******************************************************************************
2653 * Process fastcgi-script requests. Based on mod_cgi::cgi_handler().
2654 */
2655 static int content_handler(request_rec *r)
2656 {
2657 fcgi_request *fr = NULL;
2658 int ret;
2659
2660 #ifdef APACHE2
2661 if (strcmp(r->handler, FASTCGI_HANDLER_NAME))
2662 return DECLINED;
2663 #endif
2664
2665 /* Setup a new FastCGI request */
2666 ret = create_fcgi_request(r, NULL, &fr);
2667 if (ret)
2668 {
2669 return ret;
2670 }
2671
2672 /* If its a dynamic invocation, make sure scripts are OK here */
2673 if (fr->dynamic && ! (ap_allow_options(r) & OPT_EXECCGI)
2674 && ! apache_is_scriptaliased(r))
2675 {
2676 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2677 "FastCGI: \"ExecCGI Option\" is off in this directory: %s", r->uri);
2678 return HTTP_FORBIDDEN;
2679 }
2680
2681 /* Process the fastcgi-script request */
2682 if ((ret = do_work(r, fr)) != OK)
2683 return ret;
2684
2685 /* Special case redirects */
2686 ret = post_process_for_redirects(r, fr);
2687
2688 return ret;
2689 }
2690
2691
2692 static int post_process_auth_passed_header(table *t, const char *key, const char * const val)
2693 {
2694 if (strncasecmp(key, "Variable-", 9) == 0)
2695 key += 9;
2696
2697 ap_table_setn(t, key, val);
2698 return 1;
2699 }
2700
2701 static int post_process_auth_passed_compat_header(table *t, const char *key, const char * const val)
2702 {
2703 if (strncasecmp(key, "Variable-", 9) == 0)
2704 ap_table_setn(t, key + 9, val);
2705
2706 return 1;
2707 }
2708
2709 static int post_process_auth_failed_header(table * const t, const char * const key, const char * const val)
2710 {
2711 ap_table_setn(t, key, val);
2712 return 1;
2713 }
2714
2715 static void post_process_auth(fcgi_request * const fr, const int passed)
2716 {
2717 request_rec * const r = fr->r;
2718
2719 /* Restore the saved subprocess_env because we muddied ours up */
2720 r->subprocess_env = fr->saved_subprocess_env;
2721
2722 if (passed) {
2723 if (fr->auth_compat) {
2724 ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_passed_compat_header,
2725 (void *)r->subprocess_env, fr->authHeaders, NULL);
2726 }
2727 else {
2728 ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_passed_header,
2729 (void *)r->subprocess_env, fr->authHeaders, NULL);
2730 }
2731 }
2732 else {
2733 ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_failed_header,
2734 (void *)r->err_headers_out, fr->authHeaders, NULL);
2735 }
2736
2737 /* @@@ Restore these.. its a hack until I rewrite the header handling */
2738 r->status = HTTP_OK;
2739 r->status_line = NULL;
2740 }
2741
2742 static int check_user_authentication(request_rec *r)
2743 {
2744 int res, authenticated = 0;
2745 const char *password;
2746 fcgi_request *fr;
2747 const fcgi_dir_config * const dir_config =
2748 (const fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module);
2749
2750 if (dir_config->authenticator == NULL)
2751 return DECLINED;
2752
2753 /* Get the user password */
2754 if ((res = ap_get_basic_auth_pw(r, &password)) != OK)
2755 return res;
2756
2757 res = create_fcgi_request(r, dir_config->authenticator, &fr);
2758 if (res)
2759 {
2760 return res;
2761 }
2762
2763 /* Save the existing subprocess_env, because we're gonna muddy it up */
2764 fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env);
2765
2766 ap_table_setn(r->subprocess_env, "REMOTE_PASSWD", password);
2767 ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "AUTHENTICATOR");
2768
2769 /* The FastCGI Protocol doesn't differentiate authentication */
2770 fr->role = FCGI_AUTHORIZER;
2771
2772 /* Do we need compatibility mode? */
2773 fr->auth_compat = (dir_config->authenticator_options & FCGI_COMPAT);
2774
2775 if ((res = do_work(r, fr)) != OK)
2776 goto AuthenticationFailed;
2777
2778 authenticated = (r->status == 200);
2779 post_process_auth(fr, authenticated);
2780
2781 /* A redirect shouldn't be allowed during the authentication phase */
2782 if (ap_table_get(r->headers_out, "Location") != NULL) {
2783 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2784 "FastCGI: FastCgiAuthenticator \"%s\" redirected (not allowed)",
2785 dir_config->authenticator);
2786 goto AuthenticationFailed;
2787 }
2788
2789 if (authenticated)
2790 return OK;
2791
2792 AuthenticationFailed:
2793 if (!(dir_config->authenticator_options & FCGI_AUTHORITATIVE))
2794 return DECLINED;
2795
2796 /* @@@ Probably should support custom_responses */
2797 ap_note_basic_auth_failure(r);
2798 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2799 "FastCGI: authentication failed for user \"%s\": %s",
2800 #ifdef APACHE2
2801 r->user, r->uri);
2802 #else
2803 r->connection->user, r->uri);
2804 #endif
2805
2806 return (res == OK) ? HTTP_UNAUTHORIZED : res;
2807 }
2808
2809 static int check_user_authorization(request_rec *r)
2810 {
2811 int res, authorized = 0;
2812 fcgi_request *fr;
2813 const fcgi_dir_config * const dir_config =
2814 (const fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module);
2815
2816 if (dir_config->authorizer == NULL)
2817 return DECLINED;
2818
2819 /* @@@ We should probably honor the existing parameters to the require directive
2820 * as well as allow the definition of new ones (or use the basename of the
2821 * FastCGI server and pass the rest of the directive line), but for now keep
2822 * it simple. */
2823
2824 res = create_fcgi_request(r, dir_config->authorizer, &fr);
2825 if (res)
2826 {
2827 return res;
2828 }
2829
2830 /* Save the existing subprocess_env, because we're gonna muddy it up */
2831 fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env);
2832
2833 ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "AUTHORIZER");
2834
2835 fr->role = FCGI_AUTHORIZER;
2836
2837 /* Do we need compatibility mode? */
2838 fr->auth_compat = (dir_config->authorizer_options & FCGI_COMPAT);
2839
2840 if ((res = do_work(r, fr)) != OK)
2841 goto AuthorizationFailed;
2842
2843 authorized = (r->status == 200);
2844 post_process_auth(fr, authorized);
2845
2846 /* A redirect shouldn't be allowed during the authorization phase */
2847 if (ap_table_get(r->headers_out, "Location") != NULL) {
2848 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2849 "FastCGI: FastCgiAuthorizer \"%s\" redirected (not allowed)",
2850 dir_config->authorizer);
2851 goto AuthorizationFailed;
2852 }
2853
2854 if (authorized)
2855 return OK;
2856
2857 AuthorizationFailed:
2858 if (!(dir_config->authorizer_options & FCGI_AUTHORITATIVE))
2859 return DECLINED;
2860
2861 /* @@@ Probably should support custom_responses */
2862 ap_note_basic_auth_failure(r);
2863 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2864 "FastCGI: authorization failed for user \"%s\": %s",
2865 #ifdef APACHE2
2866 r->user, r->uri);
2867 #else
2868 r->connection->user, r->uri);
2869 #endif
2870
2871 return (res == OK) ? HTTP_UNAUTHORIZED : res;
2872 }
2873
2874 static int check_access(request_rec *r)
2875 {
2876 int res, access_allowed = 0;
2877 fcgi_request *fr;
2878 const fcgi_dir_config * const dir_config =
2879 (fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module);
2880
2881 if (dir_config == NULL || dir_config->access_checker == NULL)
2882 return DECLINED;
2883
2884 res = create_fcgi_request(r, dir_config->access_checker, &fr);
2885 if (res)
2886 {
2887 return res;
2888 }
2889
2890 /* Save the existing subprocess_env, because we're gonna muddy it up */
2891 fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env);
2892
2893 ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "ACCESS_CHECKER");
2894
2895 /* The FastCGI Protocol doesn't differentiate access control */
2896 fr->role = FCGI_AUTHORIZER;
2897
2898 /* Do we need compatibility mode? */
2899 fr->auth_compat = (dir_config->access_checker_options & FCGI_COMPAT);
2900
2901 if ((res = do_work(r, fr)) != OK)
2902 goto AccessFailed;
2903
2904 access_allowed = (r->status == 200);
2905 post_process_auth(fr, access_allowed);
2906
2907 /* A redirect shouldn't be allowed during the access check phase */
2908 if (ap_table_get(r->headers_out, "Location") != NULL) {
2909 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r,
2910 "FastCGI: FastCgiAccessChecker \"%s\" redirected (not allowed)",
2911 dir_config->access_checker);
2912 goto AccessFailed;
2913 }
2914
2915 if (access_allowed)
2916 return OK;
2917
2918 AccessFailed:
2919 if (!(dir_config->access_checker_options & FCGI_AUTHORITATIVE))
2920 return DECLINED;
2921
2922 /* @@@ Probably should support custom_responses */
2923 ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: access denied: %s", r->uri);
2924 return (res == OK) ? HTTP_FORBIDDEN : res;
2925 }
2926
2927 static int
2928 fixups(request_rec * r)
2929 {
2930 if (r->filename) {
2931 uid_t uid;
2932 gid_t gid;
2933
2934 get_request_identity(r, &uid, &gid);
2935
2936 if (fcgi_util_fs_get_by_id(r->filename, uid, gid))
2937 {
2938 r->handler = FASTCGI_HANDLER_NAME;
2939 return OK;
2940 }
2941 }
2942
2943 return DECLINED;
2944 }
2945
2946 #ifndef APACHE2
2947
2948 # define AP_INIT_RAW_ARGS(directive, func, mconfig, where, help) \
2949 { directive, func, mconfig, where, RAW_ARGS, help }
2950 # define AP_INIT_TAKE1(directive, func, mconfig, where, help) \
2951 { directive, func, mconfig, where, TAKE1, help }
2952 # define AP_INIT_TAKE12(directive, func, mconfig, where, help) \
2953 { directive, func, mconfig, where, TAKE12, help }
2954 # define AP_INIT_FLAG(directive, func, mconfig, where, help) \
2955 { directive, func, mconfig, where, FLAG, help }
2956
2957 #endif
2958
2959 static const command_rec fastcgi_cmds[] =
2960 {
2961 AP_INIT_RAW_ARGS("AppClass", fcgi_config_new_static_server, NULL, RSRC_CONF, NULL),
2962 AP_INIT_RAW_ARGS("FastCgiServer", fcgi_config_new_static_server, NULL, RSRC_CONF, NULL),
2963
2964 AP_INIT_RAW_ARGS("ExternalAppClass", fcgi_config_new_external_server, NULL, RSRC_CONF, NULL),
2965 AP_INIT_RAW_ARGS("FastCgiExternalServer", fcgi_config_new_external_server, NULL, RSRC_CONF, NULL),
2966
2967 AP_INIT_TAKE1("FastCgiIpcDir", fcgi_config_set_socket_dir, NULL, RSRC_CONF, NULL),
2968
2969 AP_INIT_TAKE1("FastCgiSuexec", fcgi_config_set_wrapper, NULL, RSRC_CONF, NULL),
2970 AP_INIT_TAKE1("FastCgiWrapper", fcgi_config_set_wrapper, NULL, RSRC_CONF, NULL),
2971
2972 AP_INIT_RAW_ARGS("FCGIConfig", fcgi_config_set_config, NULL, RSRC_CONF, NULL),
2973 AP_INIT_RAW_ARGS("FastCgiConfig", fcgi_config_set_config, NULL, RSRC_CONF, NULL),
2974
2975 AP_INIT_TAKE12("FastCgiAuthenticator", fcgi_config_new_auth_server,
2976 (void *)FCGI_AUTH_TYPE_AUTHENTICATOR, ACCESS_CONF,
2977 "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat"),
2978 AP_INIT_FLAG("FastCgiAuthenticatorAuthoritative", fcgi_config_set_authoritative_slot,
2979 (void *)XtOffsetOf(fcgi_dir_config, authenticator_options), ACCESS_CONF,
2980 "Set to 'off' to allow authentication to be passed along to lower modules upon failure"),
2981
2982 AP_INIT_TAKE12("FastCgiAuthorizer", fcgi_config_new_auth_server,
2983 (void *)FCGI_AUTH_TYPE_AUTHORIZER, ACCESS_CONF,
2984 "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat"),
2985 AP_INIT_FLAG("FastCgiAuthorizerAuthoritative", fcgi_config_set_authoritative_slot,
2986 (void *)XtOffsetOf(fcgi_dir_config, authorizer_options), ACCESS_CONF,
2987 "Set to 'off' to allow authorization to be passed along to lower modules upon failure"),
2988
2989 AP_INIT_TAKE12("FastCgiAccessChecker", fcgi_config_new_auth_server,
2990 (void *)FCGI_AUTH_TYPE_ACCESS_CHECKER, ACCESS_CONF,
2991 "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat"),
2992 AP_INIT_FLAG("FastCgiAccessCheckerAuthoritative", fcgi_config_set_authoritative_slot,
2993 (void *)XtOffsetOf(fcgi_dir_config, access_checker_options), ACCESS_CONF,
2994 "Set to 'off' to allow access control to be passed along to lower modules upon failure"),
2995 { NULL }
2996 };
2997
2998 #ifdef APACHE2
2999
3000 static void register_hooks(apr_pool_t * p)
3001 {
3002 /* ap_hook_pre_config(x_pre_config, NULL, NULL, APR_HOOK_MIDDLE); */
3003 ap_hook_post_config(init_module, NULL, NULL, APR_HOOK_MIDDLE);
3004 ap_hook_child_init(fcgi_child_init, NULL, NULL, APR_HOOK_MIDDLE);
3005 ap_hook_handler(content_handler, NULL, NULL, APR_HOOK_MIDDLE);
3006 ap_hook_check_user_id(check_user_authentication, NULL, NULL, APR_HOOK_MIDDLE);
3007 ap_hook_access_checker(check_access, NULL, NULL, APR_HOOK_MIDDLE);
3008 ap_hook_auth_checker(check_user_authorization, NULL, NULL, APR_HOOK_MIDDLE);
3009 ap_hook_fixups(fixups, NULL, NULL, APR_HOOK_MIDDLE);
3010 }
3011
3012 module AP_MODULE_DECLARE_DATA fastcgi_module =
3013 {
3014 STANDARD20_MODULE_STUFF,
3015 fcgi_config_create_dir_config, /* per-directory config creator */
3016 NULL, /* dir config merger */
3017 NULL, /* server config creator */
3018 NULL, /* server config merger */
3019 fastcgi_cmds, /* command table */
3020 register_hooks, /* set up other request processing hooks */
3021 };
3022
3023 #else /* !APACHE2 */
3024
3025 handler_rec fastcgi_handlers[] = {
3026 { FCGI_MAGIC_TYPE, content_handler },
3027 { FASTCGI_HANDLER_NAME, content_handler },
3028 { NULL }
3029 };
3030
3031 module MODULE_VAR_EXPORT fastcgi_module = {
3032 STANDARD_MODULE_STUFF,
3033 init_module, /* initializer */
3034 fcgi_config_create_dir_config, /* per-dir config creator */
3035 NULL, /* per-dir config merger (default: override) */
3036 NULL, /* per-server config creator */
3037 NULL, /* per-server config merger (default: override) */
3038 fastcgi_cmds, /* command table */
3039 fastcgi_handlers, /* [9] content handlers */
3040 NULL, /* [2] URI-to-filename translation */
3041 check_user_authentication, /* [5] authenticate user_id */
3042 check_user_authorization, /* [6] authorize user_id */
3043 check_access, /* [4] check access (based on src & http headers) */
3044 NULL, /* [7] check/set MIME type */
3045 fixups, /* [8] fixups */
3046 NULL, /* [10] logger */
3047 NULL, /* [3] header-parser */
3048 fcgi_child_init, /* process initialization */
3049 #ifdef WIN32
3050 fcgi_child_exit, /* process exit/cleanup */
3051 #else
3052 NULL,
3053 #endif
3054 NULL /* [1] post read-request handling */
3055 };
3056
3057 #endif /* !APACHE2 */