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