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

File Contents

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