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