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