1 /* thttpd.c - tiny/turbo/throttling HTTP server
2 **
3 ** Copyright � 1995,1998,1999,2000,2001,2015 by
4 ** Jef Poskanzer <jef@mail.acme.com>. All rights reserved.
5 **
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
8 ** are met:
9 ** 1. Redistributions of source code must retain the above copyright
10 ** notice, this list of conditions and the following disclaimer.
11 ** 2. Redistributions in binary form must reproduce the above copyright
12 ** notice, this list of conditions and the following disclaimer in the
13 ** documentation and/or other materials provided with the distribution.
14 **
15 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 */
27
28
29 #include "config.h"
30 #include "version.h"
31
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/time.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
37 #include <sys/uio.h>
38
39 #include <errno.h>
40 #ifdef HAVE_FCNTL_H
41 #include <fcntl.h>
42 #endif
43 #include <pwd.h>
44 #ifdef HAVE_GRP_H
45 #include <grp.h>
46 #endif
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <syslog.h>
52 #ifdef TIME_WITH_SYS_TIME
53 #include <time.h>
54 #endif
55 #include <unistd.h>
56
57 #include "fdwatch.h"
58 #include "libhttpd.h"
59 #include "mmc.h"
60 #include "timers.h"
61 #include "match.h"
62
63 #ifndef SHUT_WR
64 #define SHUT_WR 1
65 #endif
66
67 #ifndef HAVE_INT64T
68 typedef long long int64_t;
69 #endif
70
71
72 static char* argv0;
73 static int debug;
74 static unsigned short port;
75 static char* dir;
76 static char* data_dir;
77 static int do_chroot, no_log, no_symlink_check, do_vhost, do_global_passwd;
78 static char* cgi_pattern;
79 static int cgi_limit;
80 static char* url_pattern;
81 static int no_empty_referrers;
82 static char* local_pattern;
83 static char* logfile;
84 static char* throttlefile;
85 static char* hostname;
86 static char* pidfile;
87 static char* user;
88 static char* charset;
89 static char* p3p;
90 static int max_age;
91
92
93 typedef struct {
94 char* pattern;
95 long max_limit, min_limit;
96 long rate;
97 off_t bytes_since_avg;
98 int num_sending;
99 } throttletab;
100 static throttletab* throttles;
101 static int numthrottles, maxthrottles;
102
103 #define THROTTLE_NOLIMIT -1
104
105
106 typedef struct {
107 int conn_state;
108 int next_free_connect;
109 httpd_conn* hc;
110 int tnums[MAXTHROTTLENUMS]; /* throttle indexes */
111 int numtnums;
112 long max_limit, min_limit;
113 time_t started_at, active_at;
114 Timer* wakeup_timer;
115 Timer* linger_timer;
116 long wouldblock_delay;
117 off_t bytes;
118 off_t end_byte_index;
119 off_t next_byte_index;
120 } connecttab;
121 static connecttab* connects;
122 static int num_connects, max_connects, first_free_connect;
123 static int httpd_conn_count;
124
125 /* The connection states. */
126 #define CNST_FREE 0
127 #define CNST_READING 1
128 #define CNST_SENDING 2
129 #define CNST_PAUSING 3
130 #define CNST_LINGERING 4
131
132
133 static httpd_server* hs = (httpd_server*) 0;
134 int terminate = 0;
135 time_t start_time, stats_time;
136 long stats_connections;
137 off_t stats_bytes;
138 int stats_simultaneous;
139
140 static volatile int got_hup, got_usr1, watchdog_flag;
141
142
143 /* Forwards. */
144 static void parse_args( int argc, char** argv );
145 static void usage( void );
146 static void read_config( char* filename );
147 static void value_required( char* name, char* value );
148 static void no_value_required( char* name, char* value );
149 static char* e_strdup( char* oldstr );
150 static void lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P );
151 static void read_throttlefile( char* tf );
152 static void shut_down( void );
153 static int handle_newconnect( struct timeval* tvP, int listen_fd );
154 static void handle_read( connecttab* c, struct timeval* tvP );
155 static void handle_send( connecttab* c, struct timeval* tvP );
156 static void handle_linger( connecttab* c, struct timeval* tvP );
157 static int check_throttles( connecttab* c );
158 static void clear_throttles( connecttab* c, struct timeval* tvP );
159 static void update_throttles( ClientData client_data, struct timeval* nowP );
160 static void finish_connection( connecttab* c, struct timeval* tvP );
161 static void clear_connection( connecttab* c, struct timeval* tvP );
162 static void really_clear_connection( connecttab* c, struct timeval* tvP );
163 static void idle( ClientData client_data, struct timeval* nowP );
164 static void wakeup_connection( ClientData client_data, struct timeval* nowP );
165 static void linger_clear_connection( ClientData client_data, struct timeval* nowP );
166 static void occasional( ClientData client_data, struct timeval* nowP );
167 #ifdef STATS_TIME
168 static void show_stats( ClientData client_data, struct timeval* nowP );
169 #endif /* STATS_TIME */
170 static void logstats( struct timeval* nowP );
171 static void thttpd_logstats( long secs );
172
173
174 /* SIGTERM and SIGINT say to exit immediately. */
175 static void
handle_term(int sig)176 handle_term( int sig )
177 {
178 /* Don't need to set up the handler again, since it's a one-shot. */
179
180 shut_down();
181 syslog( LOG_NOTICE, "exiting due to signal %d", sig );
182 closelog();
183 exit( 1 );
184 }
185
186
187 /* SIGCHLD - a chile process exitted, so we need to reap the zombie */
188 static void
handle_chld(int sig)189 handle_chld( int sig )
190 {
191 const int oerrno = errno;
192 pid_t pid;
193 int status;
194
195 #ifndef HAVE_SIGSET
196 /* Set up handler again. */
197 (void) signal( SIGCHLD, handle_chld );
198 #endif /* ! HAVE_SIGSET */
199
200 /* Reap defunct children until there aren't any more. */
201 for (;;)
202 {
203 #ifdef HAVE_WAITPID
204 pid = waitpid( (pid_t) -1, &status, WNOHANG );
205 #else /* HAVE_WAITPID */
206 pid = wait3( &status, WNOHANG, (struct rusage*) 0 );
207 #endif /* HAVE_WAITPID */
208 if ( (int) pid == 0 ) /* none left */
209 break;
210 if ( (int) pid < 0 )
211 {
212 if ( errno == EINTR || errno == EAGAIN )
213 continue;
214 /* ECHILD shouldn't happen with the WNOHANG option,
215 ** but with some kernels it does anyway. Ignore it.
216 */
217 if ( errno != ECHILD )
218 syslog( LOG_ERR, "child wait - %m" );
219 break;
220 }
221 /* Decrement the CGI count. Note that this is not accurate, since
222 ** each CGI can involve two or even three child processes.
223 ** Decrementing for each child means that when there is heavy CGI
224 ** activity, the count will be lower than it should be, and therefore
225 ** more CGIs will be allowed than should be.
226 */
227 if ( hs != (httpd_server*) 0 )
228 {
229 --hs->cgi_count;
230 if ( hs->cgi_count < 0 )
231 hs->cgi_count = 0;
232 }
233 }
234
235 /* Restore previous errno. */
236 errno = oerrno;
237 }
238
239
240 /* SIGHUP says to re-open the log file. */
241 static void
handle_hup(int sig)242 handle_hup( int sig )
243 {
244 const int oerrno = errno;
245
246 #ifndef HAVE_SIGSET
247 /* Set up handler again. */
248 (void) signal( SIGHUP, handle_hup );
249 #endif /* ! HAVE_SIGSET */
250
251 /* Just set a flag that we got the signal. */
252 got_hup = 1;
253
254 /* Restore previous errno. */
255 errno = oerrno;
256 }
257
258
259 /* SIGUSR1 says to exit as soon as all current connections are done. */
260 static void
handle_usr1(int sig)261 handle_usr1( int sig )
262 {
263 /* Don't need to set up the handler again, since it's a one-shot. */
264
265 if ( num_connects == 0 )
266 {
267 /* If there are no active connections we want to exit immediately
268 ** here. Not only is it faster, but without any connections the
269 ** main loop won't wake up until the next new connection.
270 */
271 shut_down();
272 syslog( LOG_NOTICE, "exiting" );
273 closelog();
274 exit( 0 );
275 }
276
277 /* Otherwise, just set a flag that we got the signal. */
278 got_usr1 = 1;
279
280 /* Don't need to restore old errno, since we didn't do any syscalls. */
281 }
282
283
284 /* SIGUSR2 says to generate the stats syslogs immediately. */
285 static void
handle_usr2(int sig)286 handle_usr2( int sig )
287 {
288 const int oerrno = errno;
289
290 #ifndef HAVE_SIGSET
291 /* Set up handler again. */
292 (void) signal( SIGUSR2, handle_usr2 );
293 #endif /* ! HAVE_SIGSET */
294
295 logstats( (struct timeval*) 0 );
296
297 /* Restore previous errno. */
298 errno = oerrno;
299 }
300
301
302 /* SIGALRM is used as a watchdog. */
303 static void
handle_alrm(int sig)304 handle_alrm( int sig )
305 {
306 const int oerrno = errno;
307
308 /* If nothing has been happening */
309 if ( ! watchdog_flag )
310 {
311 /* Try changing dirs to someplace we can write. */
312 (void) chdir( "/tmp" );
313 /* Dump core. */
314 abort();
315 }
316 watchdog_flag = 0;
317
318 #ifndef HAVE_SIGSET
319 /* Set up handler again. */
320 (void) signal( SIGALRM, handle_alrm );
321 #endif /* ! HAVE_SIGSET */
322 /* Set up alarm again. */
323 (void) alarm( OCCASIONAL_TIME * 3 );
324
325 /* Restore previous errno. */
326 errno = oerrno;
327 }
328
329
330 static void
re_open_logfile(void)331 re_open_logfile( void )
332 {
333 FILE* logfp;
334 int retchmod;
335
336 if ( no_log || hs == (httpd_server*) 0 )
337 return;
338
339 /* Re-open the log file. */
340 if ( logfile != (char*) 0 && strcmp( logfile, "-" ) != 0 )
341 {
342 syslog( LOG_NOTICE, "re-opening logfile" );
343 logfp = fopen( logfile, "a" );
344 retchmod = chmod( logfile, S_IRUSR|S_IWUSR|S_IRGRP );
345 if ( logfp == (FILE*) 0 || retchmod != 0 )
346 {
347 syslog( LOG_CRIT, "re-opening %.80s - %m", logfile );
348 return;
349 }
350 (void) fcntl( fileno( logfp ), F_SETFD, 1 );
351 httpd_set_logfp( hs, logfp );
352 }
353 }
354
355
356 int
main(int argc,char ** argv)357 main( int argc, char** argv )
358 {
359 char* cp;
360 struct passwd* pwd;
361 uid_t uid = 32767;
362 gid_t gid = 32767;
363 char cwd[MAXPATHLEN+1];
364 FILE* logfp;
365 int retchmod;
366 int num_ready;
367 int cnum;
368 connecttab* c;
369 httpd_conn* hc;
370 httpd_sockaddr sa4;
371 httpd_sockaddr sa6;
372 int gotv4, gotv6;
373 struct timeval tv;
374
375 argv0 = argv[0];
376
377 cp = strrchr( argv0, '/' );
378 if ( cp != (char*) 0 )
379 ++cp;
380 else
381 cp = argv0;
382 openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY );
383
384 /* Handle command-line arguments. */
385 parse_args( argc, argv );
386
387 /* Read zone info now, in case we chroot(). */
388 tzset();
389
390 /* Look up hostname now, in case we chroot(). */
391 lookup_hostname( &sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6 );
392 if ( ! ( gotv4 || gotv6 ) )
393 {
394 syslog( LOG_ERR, "can't find any valid address" );
395 (void) fprintf( stderr, "%s: can't find any valid address\n", argv0 );
396 exit( 1 );
397 }
398
399 /* Throttle file. */
400 numthrottles = 0;
401 maxthrottles = 0;
402 throttles = (throttletab*) 0;
403 if ( throttlefile != (char*) 0 )
404 read_throttlefile( throttlefile );
405
406 /* If we're root and we're going to become another user, get the uid/gid
407 ** now.
408 */
409 if ( getuid() == 0 )
410 {
411 pwd = getpwnam( user );
412 if ( pwd == (struct passwd*) 0 )
413 {
414 syslog( LOG_CRIT, "unknown user - '%.80s'", user );
415 (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user );
416 exit( 1 );
417 }
418 uid = pwd->pw_uid;
419 gid = pwd->pw_gid;
420 }
421
422 /* Log file. */
423 if ( logfile != (char*) 0 )
424 {
425 if ( strcmp( logfile, "/dev/null" ) == 0 )
426 {
427 no_log = 1;
428 logfp = (FILE*) 0;
429 }
430 else if ( strcmp( logfile, "-" ) == 0 )
431 logfp = stdout;
432 else
433 {
434 logfp = fopen( logfile, "a" );
435 retchmod = chmod( logfile, S_IRUSR|S_IWUSR|S_IRGRP );
436 if ( logfp == (FILE*) 0 || retchmod != 0 )
437 {
438 syslog( LOG_CRIT, "%.80s - %m", logfile );
439 perror( logfile );
440 exit( 1 );
441 }
442 if ( logfile[0] != '/' )
443 {
444 syslog( LOG_WARNING, "logfile is not an absolute path, you may not be able to re-open it" );
445 (void) fprintf( stderr, "%s: logfile is not an absolute path, you may not be able to re-open it\n", argv0 );
446 }
447 (void) fcntl( fileno( logfp ), F_SETFD, 1 );
448 if ( getuid() == 0 )
449 {
450 /* If we are root then we chown the log file to the user we'll
451 ** be switching to.
452 */
453 if ( fchown( fileno( logfp ), uid, gid ) < 0 )
454 {
455 syslog( LOG_WARNING, "fchown logfile - %m" );
456 perror( "fchown logfile" );
457 }
458 }
459 }
460 }
461 else
462 logfp = (FILE*) 0;
463
464 /* Switch directories if requested. */
465 if ( dir != (char*) 0 )
466 {
467 if ( chdir( dir ) < 0 )
468 {
469 syslog( LOG_CRIT, "chdir - %m" );
470 perror( "chdir" );
471 exit( 1 );
472 }
473 }
474 #ifdef USE_USER_DIR
475 else if ( getuid() == 0 )
476 {
477 /* No explicit directory was specified, we're root, and the
478 ** USE_USER_DIR option is set - switch to the specified user's
479 ** home dir.
480 */
481 if ( chdir( pwd->pw_dir ) < 0 )
482 {
483 syslog( LOG_CRIT, "chdir - %m" );
484 perror( "chdir" );
485 exit( 1 );
486 }
487 }
488 #endif /* USE_USER_DIR */
489
490 /* Get current directory. */
491 (void) getcwd( cwd, sizeof(cwd) - 1 );
492 if ( cwd[strlen( cwd ) - 1] != '/' )
493 (void) strcat( cwd, "/" );
494
495 if ( ! debug )
496 {
497 /* We're not going to use stdin stdout or stderr from here on, so close
498 ** them to save file descriptors.
499 */
500 (void) fclose( stdin );
501 if ( logfp != stdout )
502 (void) fclose( stdout );
503 (void) fclose( stderr );
504
505 /* Daemonize - make ourselves a subprocess. */
506 #ifdef HAVE_DAEMON
507 if ( daemon( 1, 1 ) < 0 )
508 {
509 syslog( LOG_CRIT, "daemon - %m" );
510 exit( 1 );
511 }
512 #else /* HAVE_DAEMON */
513 switch ( fork() )
514 {
515 case 0:
516 break;
517 case -1:
518 syslog( LOG_CRIT, "fork - %m" );
519 exit( 1 );
520 default:
521 exit( 0 );
522 }
523 #ifdef HAVE_SETSID
524 (void) setsid();
525 #endif /* HAVE_SETSID */
526 #endif /* HAVE_DAEMON */
527 }
528 else
529 {
530 /* Even if we don't daemonize, we still want to disown our parent
531 ** process.
532 */
533 #ifdef HAVE_SETSID
534 (void) setsid();
535 #endif /* HAVE_SETSID */
536 }
537
538 if ( pidfile != (char*) 0 )
539 {
540 /* Write the PID file. */
541 FILE* pidfp = fopen( pidfile, "w" );
542 if ( pidfp == (FILE*) 0 )
543 {
544 syslog( LOG_CRIT, "%.80s - %m", pidfile );
545 exit( 1 );
546 }
547 (void) fprintf( pidfp, "%d\n", (int) getpid() );
548 (void) fclose( pidfp );
549 }
550
551 /* Initialize the fdwatch package. Have to do this before chroot,
552 ** if /dev/poll is used.
553 */
554 max_connects = fdwatch_get_nfiles();
555 if ( max_connects < 0 )
556 {
557 syslog( LOG_CRIT, "fdwatch initialization failure" );
558 exit( 1 );
559 }
560 max_connects -= SPARE_FDS;
561
562 /* Chroot if requested. */
563 if ( do_chroot )
564 {
565 if ( chroot( cwd ) < 0 )
566 {
567 syslog( LOG_CRIT, "chroot - %m" );
568 perror( "chroot" );
569 exit( 1 );
570 }
571 /* If we're logging and the logfile's pathname begins with the
572 ** chroot tree's pathname, then elide the chroot pathname so
573 ** that the logfile pathname still works from inside the chroot
574 ** tree.
575 */
576 if ( logfile != (char*) 0 && strcmp( logfile, "-" ) != 0 )
577 {
578 if ( strncmp( logfile, cwd, strlen( cwd ) ) == 0 )
579 {
580 (void) ol_strcpy( logfile, &logfile[strlen( cwd ) - 1] );
581 /* (We already guaranteed that cwd ends with a slash, so leaving
582 ** that slash in logfile makes it an absolute pathname within
583 ** the chroot tree.)
584 */
585 }
586 else
587 {
588 syslog( LOG_WARNING, "logfile is not within the chroot tree, you will not be able to re-open it" );
589 (void) fprintf( stderr, "%s: logfile is not within the chroot tree, you will not be able to re-open it\n", argv0 );
590 }
591 }
592 (void) strcpy( cwd, "/" );
593 /* Always chdir to / after a chroot. */
594 if ( chdir( cwd ) < 0 )
595 {
596 syslog( LOG_CRIT, "chroot chdir - %m" );
597 perror( "chroot chdir" );
598 exit( 1 );
599 }
600 }
601
602 /* Switch directories again if requested. */
603 if ( data_dir != (char*) 0 )
604 {
605 if ( chdir( data_dir ) < 0 )
606 {
607 syslog( LOG_CRIT, "data_dir chdir - %m" );
608 perror( "data_dir chdir" );
609 exit( 1 );
610 }
611 }
612
613 /* Set up to catch signals. */
614 #ifdef HAVE_SIGSET
615 (void) sigset( SIGTERM, handle_term );
616 (void) sigset( SIGINT, handle_term );
617 (void) sigset( SIGCHLD, handle_chld );
618 (void) sigset( SIGPIPE, SIG_IGN ); /* get EPIPE instead */
619 (void) sigset( SIGHUP, handle_hup );
620 (void) sigset( SIGUSR1, handle_usr1 );
621 (void) sigset( SIGUSR2, handle_usr2 );
622 (void) sigset( SIGALRM, handle_alrm );
623 #else /* HAVE_SIGSET */
624 (void) signal( SIGTERM, handle_term );
625 (void) signal( SIGINT, handle_term );
626 (void) signal( SIGCHLD, handle_chld );
627 (void) signal( SIGPIPE, SIG_IGN ); /* get EPIPE instead */
628 (void) signal( SIGHUP, handle_hup );
629 (void) signal( SIGUSR1, handle_usr1 );
630 (void) signal( SIGUSR2, handle_usr2 );
631 (void) signal( SIGALRM, handle_alrm );
632 #endif /* HAVE_SIGSET */
633 got_hup = 0;
634 got_usr1 = 0;
635 watchdog_flag = 0;
636 (void) alarm( OCCASIONAL_TIME * 3 );
637
638 /* Initialize the timer package. */
639 tmr_init();
640
641 /* Initialize the HTTP layer. Got to do this before giving up root,
642 ** so that we can bind to a privileged port.
643 */
644 hs = httpd_initialize(
645 hostname,
646 gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0,
647 port, cgi_pattern, cgi_limit, charset, p3p, max_age, cwd, no_log, logfp,
648 no_symlink_check, do_vhost, do_global_passwd, url_pattern,
649 local_pattern, no_empty_referrers );
650 if ( hs == (httpd_server*) 0 )
651 exit( 1 );
652
653 /* Set up the occasional timer. */
654 if ( tmr_create( (struct timeval*) 0, occasional, JunkClientData, OCCASIONAL_TIME * 1000L, 1 ) == (Timer*) 0 )
655 {
656 syslog( LOG_CRIT, "tmr_create(occasional) failed" );
657 exit( 1 );
658 }
659 /* Set up the idle timer. */
660 if ( tmr_create( (struct timeval*) 0, idle, JunkClientData, 5 * 1000L, 1 ) == (Timer*) 0 )
661 {
662 syslog( LOG_CRIT, "tmr_create(idle) failed" );
663 exit( 1 );
664 }
665 if ( numthrottles > 0 )
666 {
667 /* Set up the throttles timer. */
668 if ( tmr_create( (struct timeval*) 0, update_throttles, JunkClientData, THROTTLE_TIME * 1000L, 1 ) == (Timer*) 0 )
669 {
670 syslog( LOG_CRIT, "tmr_create(update_throttles) failed" );
671 exit( 1 );
672 }
673 }
674 #ifdef STATS_TIME
675 /* Set up the stats timer. */
676 if ( tmr_create( (struct timeval*) 0, show_stats, JunkClientData, STATS_TIME * 1000L, 1 ) == (Timer*) 0 )
677 {
678 syslog( LOG_CRIT, "tmr_create(show_stats) failed" );
679 exit( 1 );
680 }
681 #endif /* STATS_TIME */
682 start_time = stats_time = time( (time_t*) 0 );
683 stats_connections = 0;
684 stats_bytes = 0;
685 stats_simultaneous = 0;
686
687 /* If we're root, try to become someone else. */
688 if ( getuid() == 0 )
689 {
690 /* Set aux groups to null. */
691 if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
692 {
693 syslog( LOG_CRIT, "setgroups - %m" );
694 exit( 1 );
695 }
696 /* Set primary group. */
697 if ( setgid( gid ) < 0 )
698 {
699 syslog( LOG_CRIT, "setgid - %m" );
700 exit( 1 );
701 }
702 /* Try setting aux groups correctly - not critical if this fails. */
703 if ( initgroups( user, gid ) < 0 )
704 syslog( LOG_WARNING, "initgroups - %m" );
705 #ifdef HAVE_SETLOGIN
706 /* Set login name. */
707 (void) setlogin( user );
708 #endif /* HAVE_SETLOGIN */
709 /* Set uid. */
710 if ( setuid( uid ) < 0 )
711 {
712 syslog( LOG_CRIT, "setuid - %m" );
713 exit( 1 );
714 }
715 /* Check for unnecessary security exposure. */
716 if ( ! do_chroot )
717 syslog(
718 LOG_WARNING,
719 "started as root without requesting chroot(), warning only" );
720 }
721
722 /* Initialize our connections table. */
723 connects = NEW( connecttab, max_connects );
724 if ( connects == (connecttab*) 0 )
725 {
726 syslog( LOG_CRIT, "out of memory allocating a connecttab" );
727 exit( 1 );
728 }
729 for ( cnum = 0; cnum < max_connects; ++cnum )
730 {
731 connects[cnum].conn_state = CNST_FREE;
732 connects[cnum].next_free_connect = cnum + 1;
733 connects[cnum].hc = (httpd_conn*) 0;
734 }
735 connects[max_connects - 1].next_free_connect = -1; /* end of link list */
736 first_free_connect = 0;
737 num_connects = 0;
738 httpd_conn_count = 0;
739
740 if ( hs != (httpd_server*) 0 )
741 {
742 if ( hs->listen4_fd != -1 )
743 fdwatch_add_fd( hs->listen4_fd, (void*) 0, FDW_READ );
744 if ( hs->listen6_fd != -1 )
745 fdwatch_add_fd( hs->listen6_fd, (void*) 0, FDW_READ );
746 }
747
748 /* Main loop. */
749 (void) gettimeofday( &tv, (struct timezone*) 0 );
750 while ( ( ! terminate ) || num_connects > 0 )
751 {
752 /* Do we need to re-open the log file? */
753 if ( got_hup )
754 {
755 re_open_logfile();
756 got_hup = 0;
757 }
758
759 /* Do the fd watch. */
760 num_ready = fdwatch( tmr_mstimeout( &tv ) );
761 if ( num_ready < 0 )
762 {
763 if ( errno == EINTR || errno == EAGAIN )
764 continue; /* try again */
765 syslog( LOG_ERR, "fdwatch - %m" );
766 exit( 1 );
767 }
768 (void) gettimeofday( &tv, (struct timezone*) 0 );
769
770 if ( num_ready == 0 )
771 {
772 /* No fd's are ready - run the timers. */
773 tmr_run( &tv );
774 continue;
775 }
776
777 /* Is it a new connection? */
778 if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 &&
779 fdwatch_check_fd( hs->listen6_fd ) )
780 {
781 if ( handle_newconnect( &tv, hs->listen6_fd ) )
782 /* Go around the loop and do another fdwatch, rather than
783 ** dropping through and processing existing connections.
784 ** New connections always get priority.
785 */
786 continue;
787 }
788 if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 &&
789 fdwatch_check_fd( hs->listen4_fd ) )
790 {
791 if ( handle_newconnect( &tv, hs->listen4_fd ) )
792 /* Go around the loop and do another fdwatch, rather than
793 ** dropping through and processing existing connections.
794 ** New connections always get priority.
795 */
796 continue;
797 }
798
799 /* Find the connections that need servicing. */
800 while ( ( c = (connecttab*) fdwatch_get_next_client_data() ) != (connecttab*) -1 )
801 {
802 if ( c == (connecttab*) 0 )
803 continue;
804 hc = c->hc;
805 if ( ! fdwatch_check_fd( hc->conn_fd ) )
806 /* Something went wrong. */
807 clear_connection( c, &tv );
808 else
809 switch ( c->conn_state )
810 {
811 case CNST_READING: handle_read( c, &tv ); break;
812 case CNST_SENDING: handle_send( c, &tv ); break;
813 case CNST_LINGERING: handle_linger( c, &tv ); break;
814 }
815 }
816 tmr_run( &tv );
817
818 if ( got_usr1 && ! terminate )
819 {
820 terminate = 1;
821 if ( hs != (httpd_server*) 0 )
822 {
823 if ( hs->listen4_fd != -1 )
824 fdwatch_del_fd( hs->listen4_fd );
825 if ( hs->listen6_fd != -1 )
826 fdwatch_del_fd( hs->listen6_fd );
827 httpd_unlisten( hs );
828 }
829 }
830 }
831
832 /* The main loop terminated. */
833 shut_down();
834 syslog( LOG_NOTICE, "exiting" );
835 closelog();
836 exit( 0 );
837 }
838
839
840 static void
parse_args(int argc,char ** argv)841 parse_args( int argc, char** argv )
842 {
843 int argn;
844
845 debug = 0;
846 port = DEFAULT_PORT;
847 dir = (char*) 0;
848 data_dir = (char*) 0;
849 #ifdef ALWAYS_CHROOT
850 do_chroot = 1;
851 #else /* ALWAYS_CHROOT */
852 do_chroot = 0;
853 #endif /* ALWAYS_CHROOT */
854 no_log = 0;
855 no_symlink_check = do_chroot;
856 #ifdef ALWAYS_VHOST
857 do_vhost = 1;
858 #else /* ALWAYS_VHOST */
859 do_vhost = 0;
860 #endif /* ALWAYS_VHOST */
861 #ifdef ALWAYS_GLOBAL_PASSWD
862 do_global_passwd = 1;
863 #else /* ALWAYS_GLOBAL_PASSWD */
864 do_global_passwd = 0;
865 #endif /* ALWAYS_GLOBAL_PASSWD */
866 #ifdef CGI_PATTERN
867 cgi_pattern = CGI_PATTERN;
868 #else /* CGI_PATTERN */
869 cgi_pattern = (char*) 0;
870 #endif /* CGI_PATTERN */
871 #ifdef CGI_LIMIT
872 cgi_limit = CGI_LIMIT;
873 #else /* CGI_LIMIT */
874 cgi_limit = 0;
875 #endif /* CGI_LIMIT */
876 url_pattern = (char*) 0;
877 no_empty_referrers = 0;
878 local_pattern = (char*) 0;
879 throttlefile = (char*) 0;
880 hostname = (char*) 0;
881 logfile = (char*) 0;
882 pidfile = (char*) 0;
883 user = DEFAULT_USER;
884 charset = DEFAULT_CHARSET;
885 p3p = "";
886 max_age = -1;
887 argn = 1;
888 while ( argn < argc && argv[argn][0] == '-' )
889 {
890 if ( strcmp( argv[argn], "-V" ) == 0 )
891 {
892 (void) printf( "%s\n", SERVER_SOFTWARE );
893 exit( 0 );
894 }
895 else if ( strcmp( argv[argn], "-C" ) == 0 && argn + 1 < argc )
896 {
897 ++argn;
898 read_config( argv[argn] );
899 }
900 else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc )
901 {
902 ++argn;
903 port = (unsigned short) atoi( argv[argn] );
904 }
905 else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc )
906 {
907 ++argn;
908 dir = argv[argn];
909 }
910 else if ( strcmp( argv[argn], "-r" ) == 0 )
911 {
912 do_chroot = 1;
913 no_symlink_check = 1;
914 }
915 else if ( strcmp( argv[argn], "-nor" ) == 0 )
916 {
917 do_chroot = 0;
918 no_symlink_check = 0;
919 }
920 else if ( strcmp( argv[argn], "-dd" ) == 0 && argn + 1 < argc )
921 {
922 ++argn;
923 data_dir = argv[argn];
924 }
925 else if ( strcmp( argv[argn], "-s" ) == 0 )
926 no_symlink_check = 0;
927 else if ( strcmp( argv[argn], "-nos" ) == 0 )
928 no_symlink_check = 1;
929 else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
930 {
931 ++argn;
932 user = argv[argn];
933 }
934 else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc )
935 {
936 ++argn;
937 cgi_pattern = argv[argn];
938 }
939 else if ( strcmp( argv[argn], "-t" ) == 0 && argn + 1 < argc )
940 {
941 ++argn;
942 throttlefile = argv[argn];
943 }
944 else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc )
945 {
946 ++argn;
947 hostname = argv[argn];
948 }
949 else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc )
950 {
951 ++argn;
952 logfile = argv[argn];
953 }
954 else if ( strcmp( argv[argn], "-v" ) == 0 )
955 do_vhost = 1;
956 else if ( strcmp( argv[argn], "-nov" ) == 0 )
957 do_vhost = 0;
958 else if ( strcmp( argv[argn], "-g" ) == 0 )
959 do_global_passwd = 1;
960 else if ( strcmp( argv[argn], "-nog" ) == 0 )
961 do_global_passwd = 0;
962 else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc )
963 {
964 ++argn;
965 pidfile = argv[argn];
966 }
967 else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc )
968 {
969 ++argn;
970 charset = argv[argn];
971 }
972 else if ( strcmp( argv[argn], "-P" ) == 0 && argn + 1 < argc )
973 {
974 ++argn;
975 p3p = argv[argn];
976 }
977 else if ( strcmp( argv[argn], "-M" ) == 0 && argn + 1 < argc )
978 {
979 ++argn;
980 max_age = atoi( argv[argn] );
981 }
982 else if ( strcmp( argv[argn], "-D" ) == 0 )
983 debug = 1;
984 else
985 usage();
986 ++argn;
987 }
988 if ( argn != argc )
989 usage();
990 }
991
992
993 static void
usage(void)994 usage( void )
995 {
996 (void) fprintf( stderr,
997 "usage: %s [-C configfile] [-p port] [-d dir] [-r|-nor] [-dd data_dir] [-s|-nos] [-v|-nov] [-g|-nog] [-u user] [-c cgipat] [-t throttles] [-h host] [-l logfile] [-i pidfile] [-T charset] [-P P3P] [-M maxage] [-V] [-D]\n",
998 argv0 );
999 exit( 1 );
1000 }
1001
1002
1003 static void
read_config(char * filename)1004 read_config( char* filename )
1005 {
1006 FILE* fp;
1007 char line[10000];
1008 char* cp;
1009 char* cp2;
1010 char* name;
1011 char* value;
1012
1013 fp = fopen( filename, "r" );
1014 if ( fp == (FILE*) 0 )
1015 {
1016 perror( filename );
1017 exit( 1 );
1018 }
1019
1020 while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
1021 {
1022 /* Trim comments. */
1023 if ( ( cp = strchr( line, '#' ) ) != (char*) 0 )
1024 *cp = '\0';
1025
1026 /* Skip leading whitespace. */
1027 cp = line;
1028 cp += strspn( cp, " \t\n\r" );
1029
1030 /* Split line into words. */
1031 while ( *cp != '\0' )
1032 {
1033 /* Find next whitespace. */
1034 cp2 = cp + strcspn( cp, " \t\n\r" );
1035 /* Insert EOS and advance next-word pointer. */
1036 while ( *cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r' )
1037 *cp2++ = '\0';
1038 /* Split into name and value. */
1039 name = cp;
1040 value = strchr( name, '=' );
1041 if ( value != (char*) 0 )
1042 *value++ = '\0';
1043 /* Interpret. */
1044 if ( strcasecmp( name, "debug" ) == 0 )
1045 {
1046 no_value_required( name, value );
1047 debug = 1;
1048 }
1049 else if ( strcasecmp( name, "port" ) == 0 )
1050 {
1051 value_required( name, value );
1052 port = (unsigned short) atoi( value );
1053 }
1054 else if ( strcasecmp( name, "dir" ) == 0 )
1055 {
1056 value_required( name, value );
1057 dir = e_strdup( value );
1058 }
1059 else if ( strcasecmp( name, "chroot" ) == 0 )
1060 {
1061 no_value_required( name, value );
1062 do_chroot = 1;
1063 no_symlink_check = 1;
1064 }
1065 else if ( strcasecmp( name, "nochroot" ) == 0 )
1066 {
1067 no_value_required( name, value );
1068 do_chroot = 0;
1069 no_symlink_check = 0;
1070 }
1071 else if ( strcasecmp( name, "data_dir" ) == 0 )
1072 {
1073 value_required( name, value );
1074 data_dir = e_strdup( value );
1075 }
1076 else if ( strcasecmp( name, "nosymlinkcheck" ) == 0 )
1077 {
1078 no_value_required( name, value );
1079 no_symlink_check = 1;
1080 }
1081 else if ( strcasecmp( name, "symlinkcheck" ) == 0 )
1082 {
1083 no_value_required( name, value );
1084 no_symlink_check = 0;
1085 }
1086 else if ( strcasecmp( name, "user" ) == 0 )
1087 {
1088 value_required( name, value );
1089 user = e_strdup( value );
1090 }
1091 else if ( strcasecmp( name, "cgipat" ) == 0 )
1092 {
1093 value_required( name, value );
1094 cgi_pattern = e_strdup( value );
1095 }
1096 else if ( strcasecmp( name, "cgilimit" ) == 0 )
1097 {
1098 value_required( name, value );
1099 cgi_limit = atoi( value );
1100 }
1101 else if ( strcasecmp( name, "urlpat" ) == 0 )
1102 {
1103 value_required( name, value );
1104 url_pattern = e_strdup( value );
1105 }
1106 else if ( strcasecmp( name, "noemptyreferers" ) == 0 ||
1107 strcasecmp( name, "noemptyreferrers" ) == 0 )
1108 {
1109 no_value_required( name, value );
1110 no_empty_referrers = 1;
1111 }
1112 else if ( strcasecmp( name, "localpat" ) == 0 )
1113 {
1114 value_required( name, value );
1115 local_pattern = e_strdup( value );
1116 }
1117 else if ( strcasecmp( name, "throttles" ) == 0 )
1118 {
1119 value_required( name, value );
1120 throttlefile = e_strdup( value );
1121 }
1122 else if ( strcasecmp( name, "host" ) == 0 )
1123 {
1124 value_required( name, value );
1125 hostname = e_strdup( value );
1126 }
1127 else if ( strcasecmp( name, "logfile" ) == 0 )
1128 {
1129 value_required( name, value );
1130 logfile = e_strdup( value );
1131 }
1132 else if ( strcasecmp( name, "vhost" ) == 0 )
1133 {
1134 no_value_required( name, value );
1135 do_vhost = 1;
1136 }
1137 else if ( strcasecmp( name, "novhost" ) == 0 )
1138 {
1139 no_value_required( name, value );
1140 do_vhost = 0;
1141 }
1142 else if ( strcasecmp( name, "globalpasswd" ) == 0 )
1143 {
1144 no_value_required( name, value );
1145 do_global_passwd = 1;
1146 }
1147 else if ( strcasecmp( name, "noglobalpasswd" ) == 0 )
1148 {
1149 no_value_required( name, value );
1150 do_global_passwd = 0;
1151 }
1152 else if ( strcasecmp( name, "pidfile" ) == 0 )
1153 {
1154 value_required( name, value );
1155 pidfile = e_strdup( value );
1156 }
1157 else if ( strcasecmp( name, "charset" ) == 0 )
1158 {
1159 value_required( name, value );
1160 charset = e_strdup( value );
1161 }
1162 else if ( strcasecmp( name, "p3p" ) == 0 )
1163 {
1164 value_required( name, value );
1165 p3p = e_strdup( value );
1166 }
1167 else if ( strcasecmp( name, "max_age" ) == 0 )
1168 {
1169 value_required( name, value );
1170 max_age = atoi( value );
1171 }
1172 else
1173 {
1174 (void) fprintf(
1175 stderr, "%s: unknown config option '%s'\n", argv0, name );
1176 exit( 1 );
1177 }
1178
1179 /* Advance to next word. */
1180 cp = cp2;
1181 cp += strspn( cp, " \t\n\r" );
1182 }
1183 }
1184
1185 (void) fclose( fp );
1186 }
1187
1188
1189 static void
value_required(char * name,char * value)1190 value_required( char* name, char* value )
1191 {
1192 if ( value == (char*) 0 )
1193 {
1194 (void) fprintf(
1195 stderr, "%s: value required for %s option\n", argv0, name );
1196 exit( 1 );
1197 }
1198 }
1199
1200
1201 static void
no_value_required(char * name,char * value)1202 no_value_required( char* name, char* value )
1203 {
1204 if ( value != (char*) 0 )
1205 {
1206 (void) fprintf(
1207 stderr, "%s: no value required for %s option\n",
1208 argv0, name );
1209 exit( 1 );
1210 }
1211 }
1212
1213
1214 static char*
e_strdup(char * oldstr)1215 e_strdup( char* oldstr )
1216 {
1217 char* newstr;
1218
1219 newstr = strdup( oldstr );
1220 if ( newstr == (char*) 0 )
1221 {
1222 syslog( LOG_CRIT, "out of memory copying a string" );
1223 (void) fprintf( stderr, "%s: out of memory copying a string\n", argv0 );
1224 exit( 1 );
1225 }
1226 return newstr;
1227 }
1228
1229
1230 static void
lookup_hostname(httpd_sockaddr * sa4P,size_t sa4_len,int * gotv4P,httpd_sockaddr * sa6P,size_t sa6_len,int * gotv6P)1231 lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P )
1232 {
1233 #ifdef USE_IPV6
1234
1235 struct addrinfo hints;
1236 char portstr[10];
1237 int gaierr;
1238 struct addrinfo* ai;
1239 struct addrinfo* ai2;
1240 struct addrinfo* aiv6;
1241 struct addrinfo* aiv4;
1242
1243 (void) memset( &hints, 0, sizeof(hints) );
1244 hints.ai_family = PF_UNSPEC;
1245 hints.ai_flags = AI_PASSIVE;
1246 hints.ai_socktype = SOCK_STREAM;
1247 (void) snprintf( portstr, sizeof(portstr), "%d", (int) port );
1248 if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 )
1249 {
1250 syslog(
1251 LOG_CRIT, "getaddrinfo %.80s - %.80s",
1252 hostname, gai_strerror( gaierr ) );
1253 (void) fprintf(
1254 stderr, "%s: getaddrinfo %s - %s\n",
1255 argv0, hostname, gai_strerror( gaierr ) );
1256 exit( 1 );
1257 }
1258
1259 /* Find the first IPv6 and IPv4 entries. */
1260 aiv6 = (struct addrinfo*) 0;
1261 aiv4 = (struct addrinfo*) 0;
1262 for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
1263 {
1264 switch ( ai2->ai_family )
1265 {
1266 case AF_INET6:
1267 if ( aiv6 == (struct addrinfo*) 0 )
1268 aiv6 = ai2;
1269 break;
1270 case AF_INET:
1271 if ( aiv4 == (struct addrinfo*) 0 )
1272 aiv4 = ai2;
1273 break;
1274 }
1275 }
1276
1277 if ( aiv6 == (struct addrinfo*) 0 )
1278 *gotv6P = 0;
1279 else
1280 {
1281 if ( sa6_len < aiv6->ai_addrlen )
1282 {
1283 syslog(
1284 LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)",
1285 hostname, (unsigned long) sa6_len,
1286 (unsigned long) aiv6->ai_addrlen );
1287 exit( 1 );
1288 }
1289 (void) memset( sa6P, 0, sa6_len );
1290 (void) memmove( sa6P, aiv6->ai_addr, aiv6->ai_addrlen );
1291 *gotv6P = 1;
1292 }
1293
1294 if ( aiv4 == (struct addrinfo*) 0 )
1295 *gotv4P = 0;
1296 else
1297 {
1298 if ( sa4_len < aiv4->ai_addrlen )
1299 {
1300 syslog(
1301 LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)",
1302 hostname, (unsigned long) sa4_len,
1303 (unsigned long) aiv4->ai_addrlen );
1304 exit( 1 );
1305 }
1306 (void) memset( sa4P, 0, sa4_len );
1307 (void) memmove( sa4P, aiv4->ai_addr, aiv4->ai_addrlen );
1308 *gotv4P = 1;
1309 }
1310
1311 freeaddrinfo( ai );
1312
1313 #else /* USE_IPV6 */
1314
1315 struct hostent* he;
1316
1317 *gotv6P = 0;
1318
1319 (void) memset( sa4P, 0, sa4_len );
1320 sa4P->sa.sa_family = AF_INET;
1321 if ( hostname == (char*) 0 )
1322 sa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY );
1323 else
1324 {
1325 sa4P->sa_in.sin_addr.s_addr = inet_addr( hostname );
1326 if ( (int) sa4P->sa_in.sin_addr.s_addr == -1 )
1327 {
1328 he = gethostbyname( hostname );
1329 if ( he == (struct hostent*) 0 )
1330 {
1331 #ifdef HAVE_HSTRERROR
1332 syslog(
1333 LOG_CRIT, "gethostbyname %.80s - %.80s",
1334 hostname, hstrerror( h_errno ) );
1335 (void) fprintf(
1336 stderr, "%s: gethostbyname %s - %s\n",
1337 argv0, hostname, hstrerror( h_errno ) );
1338 #else /* HAVE_HSTRERROR */
1339 syslog( LOG_CRIT, "gethostbyname %.80s failed", hostname );
1340 (void) fprintf(
1341 stderr, "%s: gethostbyname %s failed\n", argv0, hostname );
1342 #endif /* HAVE_HSTRERROR */
1343 exit( 1 );
1344 }
1345 if ( he->h_addrtype != AF_INET )
1346 {
1347 syslog( LOG_CRIT, "%.80s - non-IP network address", hostname );
1348 (void) fprintf(
1349 stderr, "%s: %s - non-IP network address\n",
1350 argv0, hostname );
1351 exit( 1 );
1352 }
1353 (void) memmove(
1354 &sa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length );
1355 }
1356 }
1357 sa4P->sa_in.sin_port = htons( port );
1358 *gotv4P = 1;
1359
1360 #endif /* USE_IPV6 */
1361 }
1362
1363
1364 static void
read_throttlefile(char * tf)1365 read_throttlefile( char* tf )
1366 {
1367 FILE* fp;
1368 char buf[5000];
1369 char* cp;
1370 int len;
1371 char pattern[5000];
1372 long max_limit, min_limit;
1373 struct timeval tv;
1374
1375 fp = fopen( tf, "r" );
1376 if ( fp == (FILE*) 0 )
1377 {
1378 syslog( LOG_CRIT, "%.80s - %m", tf );
1379 perror( tf );
1380 exit( 1 );
1381 }
1382
1383 (void) gettimeofday( &tv, (struct timezone*) 0 );
1384
1385 while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 )
1386 {
1387 /* Nuke comments. */
1388 cp = strchr( buf, '#' );
1389 if ( cp != (char*) 0 )
1390 *cp = '\0';
1391
1392 /* Nuke trailing whitespace. */
1393 len = strlen( buf );
1394 while ( len > 0 &&
1395 ( buf[len-1] == ' ' || buf[len-1] == '\t' ||
1396 buf[len-1] == '\n' || buf[len-1] == '\r' ) )
1397 buf[--len] = '\0';
1398
1399 /* Ignore empty lines. */
1400 if ( len == 0 )
1401 continue;
1402
1403 /* Parse line. */
1404 if ( sscanf( buf, " %4900[^ \t] %ld-%ld", pattern, &min_limit, &max_limit ) == 3 )
1405 {}
1406 else if ( sscanf( buf, " %4900[^ \t] %ld", pattern, &max_limit ) == 2 )
1407 min_limit = 0;
1408 else
1409 {
1410 syslog( LOG_CRIT,
1411 "unparsable line in %.80s - %.80s", tf, buf );
1412 (void) fprintf( stderr,
1413 "%s: unparsable line in %.80s - %.80s\n",
1414 argv0, tf, buf );
1415 continue;
1416 }
1417
1418 /* Nuke any leading slashes in pattern. */
1419 if ( pattern[0] == '/' )
1420 (void) ol_strcpy( pattern, &pattern[1] );
1421 while ( ( cp = strstr( pattern, "|/" ) ) != (char*) 0 )
1422 (void) ol_strcpy( cp + 1, cp + 2 );
1423
1424 /* Check for room in throttles. */
1425 if ( numthrottles >= maxthrottles )
1426 {
1427 if ( maxthrottles == 0 )
1428 {
1429 maxthrottles = 100; /* arbitrary */
1430 throttles = NEW( throttletab, maxthrottles );
1431 }
1432 else
1433 {
1434 maxthrottles *= 2;
1435 throttles = RENEW( throttles, throttletab, maxthrottles );
1436 }
1437 if ( throttles == (throttletab*) 0 )
1438 {
1439 syslog( LOG_CRIT, "out of memory allocating a throttletab" );
1440 (void) fprintf(
1441 stderr, "%s: out of memory allocating a throttletab\n",
1442 argv0 );
1443 exit( 1 );
1444 }
1445 }
1446
1447 /* Add to table. */
1448 throttles[numthrottles].pattern = e_strdup( pattern );
1449 throttles[numthrottles].max_limit = max_limit;
1450 throttles[numthrottles].min_limit = min_limit;
1451 throttles[numthrottles].rate = 0;
1452 throttles[numthrottles].bytes_since_avg = 0;
1453 throttles[numthrottles].num_sending = 0;
1454
1455 ++numthrottles;
1456 }
1457 (void) fclose( fp );
1458 }
1459
1460
1461 static void
shut_down(void)1462 shut_down( void )
1463 {
1464 int cnum;
1465 struct timeval tv;
1466
1467 (void) gettimeofday( &tv, (struct timezone*) 0 );
1468 logstats( &tv );
1469 for ( cnum = 0; cnum < max_connects; ++cnum )
1470 {
1471 if ( connects[cnum].conn_state != CNST_FREE )
1472 httpd_close_conn( connects[cnum].hc, &tv );
1473 if ( connects[cnum].hc != (httpd_conn*) 0 )
1474 {
1475 httpd_destroy_conn( connects[cnum].hc );
1476 free( (void*) connects[cnum].hc );
1477 --httpd_conn_count;
1478 connects[cnum].hc = (httpd_conn*) 0;
1479 }
1480 }
1481 if ( hs != (httpd_server*) 0 )
1482 {
1483 httpd_server* ths = hs;
1484 hs = (httpd_server*) 0;
1485 if ( ths->listen4_fd != -1 )
1486 fdwatch_del_fd( ths->listen4_fd );
1487 if ( ths->listen6_fd != -1 )
1488 fdwatch_del_fd( ths->listen6_fd );
1489 httpd_terminate( ths );
1490 }
1491 mmc_term();
1492 tmr_term();
1493 free( (void*) connects );
1494 if ( throttles != (throttletab*) 0 )
1495 free( (void*) throttles );
1496 }
1497
1498
1499 static int
handle_newconnect(struct timeval * tvP,int listen_fd)1500 handle_newconnect( struct timeval* tvP, int listen_fd )
1501 {
1502 connecttab* c;
1503 ClientData client_data;
1504
1505 /* This loops until the accept() fails, trying to start new
1506 ** connections as fast as possible so we don't overrun the
1507 ** listen queue.
1508 */
1509 for (;;)
1510 {
1511 /* Is there room in the connection table? */
1512 if ( num_connects >= max_connects )
1513 {
1514 /* Out of connection slots. Run the timers, then the
1515 ** existing connections, and maybe we'll free up a slot
1516 ** by the time we get back here.
1517 */
1518 syslog( LOG_WARNING, "too many connections!" );
1519 tmr_run( tvP );
1520 return 0;
1521 }
1522 /* Get the first free connection entry off the free list. */
1523 if ( first_free_connect == -1 || connects[first_free_connect].conn_state != CNST_FREE )
1524 {
1525 syslog( LOG_CRIT, "the connects free list is messed up" );
1526 exit( 1 );
1527 }
1528 c = &connects[first_free_connect];
1529 /* Make the httpd_conn if necessary. */
1530 if ( c->hc == (httpd_conn*) 0 )
1531 {
1532 c->hc = NEW( httpd_conn, 1 );
1533 if ( c->hc == (httpd_conn*) 0 )
1534 {
1535 syslog( LOG_CRIT, "out of memory allocating an httpd_conn" );
1536 exit( 1 );
1537 }
1538 c->hc->initialized = 0;
1539 ++httpd_conn_count;
1540 }
1541
1542 /* Get the connection. */
1543 switch ( httpd_get_conn( hs, listen_fd, c->hc ) )
1544 {
1545 /* Some error happened. Run the timers, then the
1546 ** existing connections. Maybe the error will clear.
1547 */
1548 case GC_FAIL:
1549 tmr_run( tvP );
1550 return 0;
1551
1552 /* No more connections to accept for now. */
1553 case GC_NO_MORE:
1554 return 1;
1555 }
1556 c->conn_state = CNST_READING;
1557 /* Pop it off the free list. */
1558 first_free_connect = c->next_free_connect;
1559 c->next_free_connect = -1;
1560 ++num_connects;
1561 client_data.p = c;
1562 c->active_at = tvP->tv_sec;
1563 c->wakeup_timer = (Timer*) 0;
1564 c->linger_timer = (Timer*) 0;
1565 c->next_byte_index = 0;
1566 c->numtnums = 0;
1567
1568 /* Set the connection file descriptor to no-delay mode. */
1569 httpd_set_ndelay( c->hc->conn_fd );
1570
1571 fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
1572
1573 ++stats_connections;
1574 if ( num_connects > stats_simultaneous )
1575 stats_simultaneous = num_connects;
1576 }
1577 }
1578
1579
1580 static void
handle_read(connecttab * c,struct timeval * tvP)1581 handle_read( connecttab* c, struct timeval* tvP )
1582 {
1583 int sz;
1584 ClientData client_data;
1585 httpd_conn* hc = c->hc;
1586
1587 /* Is there room in our buffer to read more bytes? */
1588 if ( hc->read_idx >= hc->read_size )
1589 {
1590 if ( hc->read_size > 5000 )
1591 {
1592 httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1593 finish_connection( c, tvP );
1594 return;
1595 }
1596 httpd_realloc_str(
1597 &hc->read_buf, &hc->read_size, hc->read_size + 1000 );
1598 }
1599
1600 /* Read some more bytes. */
1601 sz = read(
1602 hc->conn_fd, &(hc->read_buf[hc->read_idx]),
1603 hc->read_size - hc->read_idx );
1604 if ( sz == 0 )
1605 {
1606 httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1607 finish_connection( c, tvP );
1608 return;
1609 }
1610 if ( sz < 0 )
1611 {
1612 /* Ignore EINTR and EAGAIN. Also ignore EWOULDBLOCK. At first glance
1613 ** you would think that connections returned by fdwatch as readable
1614 ** should never give an EWOULDBLOCK; however, this apparently can
1615 ** happen if a packet gets garbled.
1616 */
1617 if ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
1618 return;
1619 httpd_send_err(
1620 hc, 400, httpd_err400title, "", httpd_err400form, "" );
1621 finish_connection( c, tvP );
1622 return;
1623 }
1624 hc->read_idx += sz;
1625 c->active_at = tvP->tv_sec;
1626
1627 /* Do we have a complete request yet? */
1628 switch ( httpd_got_request( hc ) )
1629 {
1630 case GR_NO_REQUEST:
1631 return;
1632 case GR_BAD_REQUEST:
1633 httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1634 finish_connection( c, tvP );
1635 return;
1636 }
1637
1638 /* Yes. Try parsing and resolving it. */
1639 if ( httpd_parse_request( hc ) < 0 )
1640 {
1641 finish_connection( c, tvP );
1642 return;
1643 }
1644
1645 /* Check the throttle table */
1646 if ( ! check_throttles( c ) )
1647 {
1648 httpd_send_err(
1649 hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
1650 finish_connection( c, tvP );
1651 return;
1652 }
1653
1654 /* Start the connection going. */
1655 if ( httpd_start_request( hc, tvP ) < 0 )
1656 {
1657 /* Something went wrong. Close down the connection. */
1658 finish_connection( c, tvP );
1659 return;
1660 }
1661
1662 /* Fill in end_byte_index. */
1663 if ( hc->got_range )
1664 {
1665 c->next_byte_index = hc->first_byte_index;
1666 c->end_byte_index = hc->last_byte_index + 1;
1667 }
1668 else if ( hc->bytes_to_send < 0 )
1669 c->end_byte_index = 0;
1670 else
1671 c->end_byte_index = hc->bytes_to_send;
1672
1673 /* Check if it's already handled. */
1674 if ( hc->file_address == (char*) 0 )
1675 {
1676 /* No file address means someone else is handling it. */
1677 int tind;
1678 for ( tind = 0; tind < c->numtnums; ++tind )
1679 throttles[c->tnums[tind]].bytes_since_avg += hc->bytes_sent;
1680 c->next_byte_index = hc->bytes_sent;
1681 finish_connection( c, tvP );
1682 return;
1683 }
1684 if ( c->next_byte_index >= c->end_byte_index )
1685 {
1686 /* There's nothing to send. */
1687 finish_connection( c, tvP );
1688 return;
1689 }
1690
1691 /* Cool, we have a valid connection and a file to send to it. */
1692 c->conn_state = CNST_SENDING;
1693 c->started_at = tvP->tv_sec;
1694 c->wouldblock_delay = 0;
1695 client_data.p = c;
1696
1697 fdwatch_del_fd( hc->conn_fd );
1698 fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
1699 }
1700
1701
1702 static void
handle_send(connecttab * c,struct timeval * tvP)1703 handle_send( connecttab* c, struct timeval* tvP )
1704 {
1705 size_t max_bytes;
1706 int sz, coast;
1707 ClientData client_data;
1708 time_t elapsed;
1709 httpd_conn* hc = c->hc;
1710 int tind;
1711
1712 if ( c->max_limit == THROTTLE_NOLIMIT )
1713 max_bytes = 1000000000L;
1714 else
1715 max_bytes = c->max_limit / 4; /* send at most 1/4 seconds worth */
1716
1717 /* Do we need to write the headers first? */
1718 if ( hc->responselen == 0 )
1719 {
1720 /* No, just write the file. */
1721 #ifdef USE_SENDFILE
1722 off_t sbytes;
1723
1724 sz = sendfile(
1725 hc->file_fd, hc->conn_fd, c->next_byte_index,
1726 MIN( c->end_byte_index - c->next_byte_index, max_bytes ),
1727 NULL, &sbytes, 0 );
1728 if (sz == -1 && errno == EAGAIN)
1729 sz = sbytes > 0 ? sbytes : -1;
1730 else if (sz == 0)
1731 sz = sbytes;
1732 #else
1733 sz = write(
1734 hc->conn_fd, &(hc->file_address[c->next_byte_index]),
1735 MIN( c->end_byte_index - c->next_byte_index, max_bytes ) );
1736 #endif
1737 }
1738 else
1739 {
1740 #ifdef USE_SENDFILE
1741 struct sf_hdtr sf;
1742 struct iovec iv;
1743 off_t sbytes;
1744
1745 iv.iov_base = hc->response;
1746 iv.iov_len = hc->responselen;
1747 sf.headers = &iv;
1748 sf.hdr_cnt = 1;
1749 sf.trailers = NULL;
1750 sf.trl_cnt = 0;
1751 sz = sendfile(
1752 hc->file_fd, hc->conn_fd, c->next_byte_index,
1753 MIN( c->end_byte_index - c->next_byte_index, max_bytes ),
1754 &sf, &sbytes, 0 );
1755 if (sz == -1 && errno == EAGAIN)
1756 sz = sbytes > 0 ? sbytes : -1;
1757 else if (sz == 0)
1758 sz = sbytes;
1759 #else
1760 /* Yes. We'll combine headers and file into a single writev(),
1761 ** hoping that this generates a single packet.
1762 */
1763 struct iovec iv[2];
1764
1765 iv[0].iov_base = hc->response;
1766 iv[0].iov_len = hc->responselen;
1767 iv[1].iov_base = &(hc->file_address[c->next_byte_index]);
1768 iv[1].iov_len = MIN( c->end_byte_index - c->next_byte_index, max_bytes );
1769 sz = writev( hc->conn_fd, iv, 2 );
1770 #endif
1771 }
1772
1773 if ( sz < 0 && errno == EINTR )
1774 return;
1775
1776 if ( sz == 0 ||
1777 ( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) )
1778 {
1779 /* This shouldn't happen, but some kernels, e.g.
1780 ** SunOS 4.1.x, are broken and select() says that
1781 ** O_NDELAY sockets are always writable even when
1782 ** they're actually not.
1783 **
1784 ** Current workaround is to block sending on this
1785 ** socket for a brief adaptively-tuned period.
1786 ** Fortunately we already have all the necessary
1787 ** blocking code, for use with throttling.
1788 */
1789 c->wouldblock_delay += MIN_WOULDBLOCK_DELAY;
1790 c->conn_state = CNST_PAUSING;
1791 fdwatch_del_fd( hc->conn_fd );
1792 client_data.p = c;
1793 if ( c->wakeup_timer != (Timer*) 0 )
1794 syslog( LOG_ERR, "replacing non-null wakeup_timer!" );
1795 c->wakeup_timer = tmr_create(
1796 tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 );
1797 if ( c->wakeup_timer == (Timer*) 0 )
1798 {
1799 syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1800 exit( 1 );
1801 }
1802 return;
1803 }
1804
1805 if ( sz < 0 )
1806 {
1807 /* Something went wrong, close this connection.
1808 **
1809 ** If it's just an EPIPE, don't bother logging, that
1810 ** just means the client hung up on us.
1811 **
1812 ** On some systems, write() occasionally gives an EINVAL.
1813 ** Dunno why, something to do with the socket going
1814 ** bad. Anyway, we don't log those either.
1815 **
1816 ** And ECONNRESET isn't interesting either.
1817 */
1818 if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET
1819 #ifdef USE_SENDFILE
1820 && errno != ENOTCONN
1821 #endif
1822 )
1823 syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
1824 clear_connection( c, tvP );
1825 return;
1826 }
1827
1828 /* Ok, we wrote something. */
1829 c->active_at = tvP->tv_sec;
1830 /* Was this a headers + file writev()? */
1831 if ( hc->responselen > 0 )
1832 {
1833 /* Yes; did we write only part of the headers? */
1834 if ( sz < hc->responselen )
1835 {
1836 /* Yes; move the unwritten part to the front of the buffer. */
1837 int newlen = hc->responselen - sz;
1838 (void) memmove( hc->response, &(hc->response[sz]), newlen );
1839 hc->responselen = newlen;
1840 sz = 0;
1841 }
1842 else
1843 {
1844 /* Nope, we wrote the full headers, so adjust accordingly. */
1845 sz -= hc->responselen;
1846 hc->responselen = 0;
1847 }
1848 }
1849 /* And update how much of the file we wrote. */
1850 c->next_byte_index += sz;
1851 c->hc->bytes_sent += sz;
1852 for ( tind = 0; tind < c->numtnums; ++tind )
1853 throttles[c->tnums[tind]].bytes_since_avg += sz;
1854
1855 /* Are we done? */
1856 if ( c->next_byte_index >= c->end_byte_index )
1857 {
1858 /* This connection is finished! */
1859 finish_connection( c, tvP );
1860 return;
1861 }
1862
1863 /* Tune the (blockheaded) wouldblock delay. */
1864 if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY )
1865 c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY;
1866
1867 /* If we're throttling, check if we're sending too fast. */
1868 if ( c->max_limit != THROTTLE_NOLIMIT )
1869 {
1870 elapsed = tvP->tv_sec - c->started_at;
1871 if ( elapsed == 0 )
1872 elapsed = 1; /* count at least one second */
1873 if ( c->hc->bytes_sent / elapsed > c->max_limit )
1874 {
1875 c->conn_state = CNST_PAUSING;
1876 fdwatch_del_fd( hc->conn_fd );
1877 /* How long should we wait to get back on schedule? If less
1878 ** than a second (integer math rounding), use 1/2 second.
1879 */
1880 coast = c->hc->bytes_sent / c->max_limit - elapsed;
1881 client_data.p = c;
1882 if ( c->wakeup_timer != (Timer*) 0 )
1883 syslog( LOG_ERR, "replacing non-null wakeup_timer!" );
1884 c->wakeup_timer = tmr_create(
1885 tvP, wakeup_connection, client_data,
1886 coast > 0 ? ( coast * 1000L ) : 500L, 0 );
1887 if ( c->wakeup_timer == (Timer*) 0 )
1888 {
1889 syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1890 exit( 1 );
1891 }
1892 }
1893 }
1894 /* (No check on min_limit here, that only controls connection startups.) */
1895 }
1896
1897
1898 static void
handle_linger(connecttab * c,struct timeval * tvP)1899 handle_linger( connecttab* c, struct timeval* tvP )
1900 {
1901 char buf[4096];
1902 int r;
1903
1904 /* In lingering-close mode we just read and ignore bytes. An error
1905 ** or EOF ends things, otherwise we go until a timeout.
1906 */
1907 r = read( c->hc->conn_fd, buf, sizeof(buf) );
1908 if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
1909 return;
1910 if ( r <= 0 )
1911 really_clear_connection( c, tvP );
1912 }
1913
1914
1915 static int
check_throttles(connecttab * c)1916 check_throttles( connecttab* c )
1917 {
1918 int tnum;
1919 long l;
1920
1921 c->numtnums = 0;
1922 c->max_limit = c->min_limit = THROTTLE_NOLIMIT;
1923 for ( tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS;
1924 ++tnum )
1925 if ( match( throttles[tnum].pattern, c->hc->expnfilename ) )
1926 {
1927 /* If we're way over the limit, don't even start. */
1928 if ( throttles[tnum].rate > throttles[tnum].max_limit * 2 )
1929 return 0;
1930 /* Also don't start if we're under the minimum. */
1931 if ( throttles[tnum].rate < throttles[tnum].min_limit )
1932 return 0;
1933 if ( throttles[tnum].num_sending < 0 )
1934 {
1935 syslog( LOG_ERR, "throttle sending count was negative - shouldn't happen!" );
1936 throttles[tnum].num_sending = 0;
1937 }
1938 c->tnums[c->numtnums++] = tnum;
1939 ++throttles[tnum].num_sending;
1940 l = throttles[tnum].max_limit / throttles[tnum].num_sending;
1941 if ( c->max_limit == THROTTLE_NOLIMIT )
1942 c->max_limit = l;
1943 else
1944 c->max_limit = MIN( c->max_limit, l );
1945 l = throttles[tnum].min_limit;
1946 if ( c->min_limit == THROTTLE_NOLIMIT )
1947 c->min_limit = l;
1948 else
1949 c->min_limit = MAX( c->min_limit, l );
1950 }
1951 return 1;
1952 }
1953
1954
1955 static void
clear_throttles(connecttab * c,struct timeval * tvP)1956 clear_throttles( connecttab* c, struct timeval* tvP )
1957 {
1958 int tind;
1959
1960 for ( tind = 0; tind < c->numtnums; ++tind )
1961 --throttles[c->tnums[tind]].num_sending;
1962 }
1963
1964
1965 static void
update_throttles(ClientData client_data,struct timeval * nowP)1966 update_throttles( ClientData client_data, struct timeval* nowP )
1967 {
1968 int tnum, tind;
1969 int cnum;
1970 connecttab* c;
1971 long l;
1972
1973 /* Update the average sending rate for each throttle. This is only used
1974 ** when new connections start up.
1975 */
1976 for ( tnum = 0; tnum < numthrottles; ++tnum )
1977 {
1978 throttles[tnum].rate = ( 2 * throttles[tnum].rate + throttles[tnum].bytes_since_avg / THROTTLE_TIME ) / 3;
1979 throttles[tnum].bytes_since_avg = 0;
1980 /* Log a warning message if necessary. */
1981 if ( throttles[tnum].rate > throttles[tnum].max_limit && throttles[tnum].num_sending != 0 )
1982 {
1983 if ( throttles[tnum].rate > throttles[tnum].max_limit * 2 )
1984 syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld greatly exceeding limit %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].max_limit, throttles[tnum].num_sending );
1985 else
1986 syslog( LOG_INFO, "throttle #%d '%.80s' rate %ld exceeding limit %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].max_limit, throttles[tnum].num_sending );
1987 }
1988 if ( throttles[tnum].rate < throttles[tnum].min_limit && throttles[tnum].num_sending != 0 )
1989 {
1990 syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld lower than minimum %ld; %d sending", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].min_limit, throttles[tnum].num_sending );
1991 }
1992 }
1993
1994 /* Now update the sending rate on all the currently-sending connections,
1995 ** redistributing it evenly.
1996 */
1997 for ( cnum = 0; cnum < max_connects; ++cnum )
1998 {
1999 c = &connects[cnum];
2000 if ( c->conn_state == CNST_SENDING || c->conn_state == CNST_PAUSING )
2001 {
2002 c->max_limit = THROTTLE_NOLIMIT;
2003 for ( tind = 0; tind < c->numtnums; ++tind )
2004 {
2005 tnum = c->tnums[tind];
2006 l = throttles[tnum].max_limit / throttles[tnum].num_sending;
2007 if ( c->max_limit == THROTTLE_NOLIMIT )
2008 c->max_limit = l;
2009 else
2010 c->max_limit = MIN( c->max_limit, l );
2011 }
2012 }
2013 }
2014 }
2015
2016
2017 static void
finish_connection(connecttab * c,struct timeval * tvP)2018 finish_connection( connecttab* c, struct timeval* tvP )
2019 {
2020 /* If we haven't actually sent the buffered response yet, do so now. */
2021 httpd_write_response( c->hc );
2022
2023 /* And clear. */
2024 clear_connection( c, tvP );
2025 }
2026
2027
2028 static void
clear_connection(connecttab * c,struct timeval * tvP)2029 clear_connection( connecttab* c, struct timeval* tvP )
2030 {
2031 ClientData client_data;
2032
2033 if ( c->wakeup_timer != (Timer*) 0 )
2034 {
2035 tmr_cancel( c->wakeup_timer );
2036 c->wakeup_timer = 0;
2037 }
2038
2039 /* This is our version of Apache's lingering_close() routine, which is
2040 ** their version of the often-broken SO_LINGER socket option. For why
2041 ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html
2042 ** What we do is delay the actual closing for a few seconds, while reading
2043 ** any bytes that come over the connection. However, we don't want to do
2044 ** this unless it's necessary, because it ties up a connection slot and
2045 ** file descriptor which means our maximum connection-handling rate
2046 ** is lower. So, elsewhere we set a flag when we detect the few
2047 ** circumstances that make a lingering close necessary. If the flag
2048 ** isn't set we do the real close now.
2049 */
2050 if ( c->conn_state == CNST_LINGERING )
2051 {
2052 /* If we were already lingering, shut down for real. */
2053 tmr_cancel( c->linger_timer );
2054 c->linger_timer = (Timer*) 0;
2055 c->hc->should_linger = 0;
2056 }
2057 if ( c->hc->should_linger )
2058 {
2059 if ( c->conn_state != CNST_PAUSING )
2060 fdwatch_del_fd( c->hc->conn_fd );
2061 c->conn_state = CNST_LINGERING;
2062 shutdown( c->hc->conn_fd, SHUT_WR );
2063 fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
2064 client_data.p = c;
2065 if ( c->linger_timer != (Timer*) 0 )
2066 syslog( LOG_ERR, "replacing non-null linger_timer!" );
2067 c->linger_timer = tmr_create(
2068 tvP, linger_clear_connection, client_data, LINGER_TIME, 0 );
2069 if ( c->linger_timer == (Timer*) 0 )
2070 {
2071 syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" );
2072 exit( 1 );
2073 }
2074 }
2075 else
2076 really_clear_connection( c, tvP );
2077 }
2078
2079
2080 static void
really_clear_connection(connecttab * c,struct timeval * tvP)2081 really_clear_connection( connecttab* c, struct timeval* tvP )
2082 {
2083 stats_bytes += c->hc->bytes_sent;
2084 if ( c->conn_state != CNST_PAUSING )
2085 fdwatch_del_fd( c->hc->conn_fd );
2086 httpd_close_conn( c->hc, tvP );
2087 clear_throttles( c, tvP );
2088 if ( c->linger_timer != (Timer*) 0 )
2089 {
2090 tmr_cancel( c->linger_timer );
2091 c->linger_timer = 0;
2092 }
2093 c->conn_state = CNST_FREE;
2094 c->next_free_connect = first_free_connect;
2095 first_free_connect = c - connects; /* division by sizeof is implied */
2096 --num_connects;
2097 }
2098
2099
2100 static void
idle(ClientData client_data,struct timeval * nowP)2101 idle( ClientData client_data, struct timeval* nowP )
2102 {
2103 int cnum;
2104 connecttab* c;
2105
2106 for ( cnum = 0; cnum < max_connects; ++cnum )
2107 {
2108 c = &connects[cnum];
2109 switch ( c->conn_state )
2110 {
2111 case CNST_READING:
2112 if ( nowP->tv_sec - c->active_at >= IDLE_READ_TIMELIMIT )
2113 {
2114 syslog( LOG_INFO,
2115 "%.80s connection timed out reading",
2116 httpd_ntoa( &c->hc->client_addr ) );
2117 httpd_send_err(
2118 c->hc, 408, httpd_err408title, "", httpd_err408form, "" );
2119 finish_connection( c, nowP );
2120 }
2121 break;
2122 case CNST_SENDING:
2123 case CNST_PAUSING:
2124 if ( nowP->tv_sec - c->active_at >= IDLE_SEND_TIMELIMIT )
2125 {
2126 syslog( LOG_INFO,
2127 "%.80s connection timed out sending",
2128 httpd_ntoa( &c->hc->client_addr ) );
2129 clear_connection( c, nowP );
2130 }
2131 break;
2132 }
2133 }
2134 }
2135
2136
2137 static void
wakeup_connection(ClientData client_data,struct timeval * nowP)2138 wakeup_connection( ClientData client_data, struct timeval* nowP )
2139 {
2140 connecttab* c;
2141
2142 c = (connecttab*) client_data.p;
2143 c->wakeup_timer = (Timer*) 0;
2144 if ( c->conn_state == CNST_PAUSING )
2145 {
2146 c->conn_state = CNST_SENDING;
2147 fdwatch_add_fd( c->hc->conn_fd, c, FDW_WRITE );
2148 }
2149 }
2150
2151 static void
linger_clear_connection(ClientData client_data,struct timeval * nowP)2152 linger_clear_connection( ClientData client_data, struct timeval* nowP )
2153 {
2154 connecttab* c;
2155
2156 c = (connecttab*) client_data.p;
2157 c->linger_timer = (Timer*) 0;
2158 really_clear_connection( c, nowP );
2159 }
2160
2161
2162 static void
occasional(ClientData client_data,struct timeval * nowP)2163 occasional( ClientData client_data, struct timeval* nowP )
2164 {
2165 mmc_cleanup( nowP );
2166 tmr_cleanup();
2167 watchdog_flag = 1; /* let the watchdog know that we are alive */
2168 }
2169
2170
2171 #ifdef STATS_TIME
2172 static void
show_stats(ClientData client_data,struct timeval * nowP)2173 show_stats( ClientData client_data, struct timeval* nowP )
2174 {
2175 logstats( nowP );
2176 }
2177 #endif /* STATS_TIME */
2178
2179
2180 /* Generate debugging statistics syslog messages for all packages. */
2181 static void
logstats(struct timeval * nowP)2182 logstats( struct timeval* nowP )
2183 {
2184 struct timeval tv;
2185 time_t now;
2186 long up_secs, stats_secs;
2187
2188 if ( nowP == (struct timeval*) 0 )
2189 {
2190 (void) gettimeofday( &tv, (struct timezone*) 0 );
2191 nowP = &tv;
2192 }
2193 now = nowP->tv_sec;
2194 up_secs = now - start_time;
2195 stats_secs = now - stats_time;
2196 if ( stats_secs == 0 )
2197 stats_secs = 1; /* fudge */
2198 stats_time = now;
2199 syslog( LOG_NOTICE,
2200 "up %ld seconds, stats for %ld seconds:", up_secs, stats_secs );
2201
2202 thttpd_logstats( stats_secs );
2203 httpd_logstats( stats_secs );
2204 mmc_logstats( stats_secs );
2205 fdwatch_logstats( stats_secs );
2206 tmr_logstats( stats_secs );
2207 }
2208
2209
2210 /* Generate debugging statistics syslog message. */
2211 static void
thttpd_logstats(long secs)2212 thttpd_logstats( long secs )
2213 {
2214 if ( secs > 0 )
2215 syslog( LOG_NOTICE,
2216 " thttpd - %ld connections (%g/sec), %d max simultaneous, %lld bytes (%g/sec), %d httpd_conns allocated",
2217 stats_connections, (float) stats_connections / secs,
2218 stats_simultaneous, (long long) stats_bytes,
2219 (float) stats_bytes / secs, httpd_conn_count );
2220 stats_connections = 0;
2221 stats_bytes = 0;
2222 stats_simultaneous = 0;
2223 }
2224