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