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