ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/thttpd/thttpd.c
Revision: 1.2
Committed: Mon Jun 25 02:35:16 2001 UTC (25 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +170 -18 lines
Log Message:
*** empty log message ***

File Contents

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