ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/thttpd/thttpd.c
Revision: 1.4
Committed: Tue Jul 17 22:30:18 2001 UTC (22 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.3: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /* thttpd.c - tiny/turbo/throttling HTTP server
2     **
3     ** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@acme.com>.
4     ** All rights reserved.
5     **
6     ** Redistribution and use in source and binary forms, with or without
7     ** modification, are permitted provided that the following conditions
8     ** are met:
9     ** 1. Redistributions of source code must retain the above copyright
10     ** notice, this list of conditions and the following disclaimer.
11     ** 2. Redistributions in binary form must reproduce the above copyright
12     ** notice, this list of conditions and the following disclaimer in the
13     ** documentation and/or other materials provided with the distribution.
14     **
15     ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16     ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17     ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18     ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19     ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20     ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21     ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22     ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23     ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24     ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25     ** SUCH DAMAGE.
26     */
27    
28     #include "config.h"
29     #include "version.h"
30    
31     #include <sys/param.h>
32     #include <sys/types.h>
33     #include <sys/time.h>
34     #include <sys/stat.h>
35     #include <sys/uio.h>
36    
37     #include <errno.h>
38     #ifdef HAVE_FCNTL_H
39     #include <fcntl.h>
40     #endif
41     #include <pwd.h>
42     #ifdef HAVE_GRP_H
43     #include <grp.h>
44     #endif
45     #include <signal.h>
46     #include <stdio.h>
47     #include <stdlib.h>
48     #include <string.h>
49     #include <syslog.h>
50     #ifdef TIME_WITH_SYS_TIME
51     #include <time.h>
52     #endif
53     #include <unistd.h>
54    
55     #include "fdwatch.h"
56     #include "libhttpd.h"
57     #include "mmc.h"
58     #include "timers.h"
59     #include "match.h"
60    
61    
62     static char* argv0;
63     static int debug;
64     static int port;
65     static char* dir;
66     static int do_chroot, no_log, no_symlink, do_vhost, do_global_passwd;
67     static char* cgi_pattern;
68 root 1.2 static char* autoindex_prog;
69 root 1.1 static char* url_pattern;
70     static int no_empty_referers;
71     static char* local_pattern;
72     static char* logfile;
73     static char* throttlefile;
74     static char* hostname;
75     static char* pidfile;
76     static char* user;
77     static char* charset;
78 root 1.2 static int conns_per_address=0;
79 root 1.1
80     typedef struct {
81     char* pattern;
82     long limit;
83     long rate;
84     off_t bytes_since_avg;
85     int num_sending;
86     } throttletab;
87     static throttletab* throttles;
88     static int numthrottles, maxthrottles;
89    
90     #define THROTTLE_NOLIMIT 1234567890L /* default limit (if none) */
91    
92    
93     typedef struct {
94     int conn_state;
95     httpd_conn* hc;
96     int tnums[MAXTHROTTLENUMS]; /* throttle indexes */
97     int numtnums;
98     long limit;
99     time_t started_at;
100     Timer* idle_read_timer;
101     Timer* idle_send_timer;
102     Timer* wakeup_timer;
103     Timer* linger_timer;
104     long wouldblock_delay;
105     off_t bytes;
106     off_t bytes_sent;
107     off_t bytes_to_send;
108     } connecttab;
109     static connecttab* connects;
110     static int numconnects, maxconnects;
111     static int httpd_conn_count;
112    
113 root 1.2 typedef struct {
114 root 1.3 time_t next_access;
115 root 1.2 struct in_addr sin_addr;
116     #ifdef HAVE_SOCKADDR_IN6
117     struct in6_addr sin6_addr;
118     #endif /* HAVE_SOCKADDR_IN6 */
119     } blocktab;
120     static blocktab *blocks;
121    
122 root 1.3 static int blocktime = DEFAULT_BLOCKTIME;
123 root 1.2
124     static int *match_ip;
125    
126 root 1.1 /* The connection states. */
127     #define CNST_FREE 0
128     #define CNST_READING 1
129     #define CNST_SENDING 2
130     #define CNST_PAUSING 3
131     #define CNST_LINGERING 4
132    
133    
134     static httpd_server* hs = (httpd_server*) 0;
135     int terminate = 0;
136     time_t start_time, stats_time;
137     long stats_connections, stats_bytes;
138     int stats_simultaneous;
139    
140     static int got_usr1;
141    
142    
143     /* Forwards. */
144     static void parse_args( int argc, char** argv );
145     static void usage( void );
146     static void read_config( char* filename );
147     static void value_required( char* name, char* value );
148     static void no_value_required( char* name, char* value );
149     static char* e_strdup( char* oldstr );
150     static void lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P );
151     static void read_throttlefile( char* throttlefile );
152     static void shut_down( void );
153 root 1.2 static int handle_newconnect( struct timeval* tvP, int listen_fd, int family );
154 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 root 1.2 do_global_passwd, url_pattern, local_pattern, no_empty_referers,
437     autoindex_prog );
438 root 1.1 if ( hs == (httpd_server*) 0 )
439     exit( 1 );
440    
441     /* Set up the occasional timer. */
442     if ( tmr_create( (struct timeval*) 0, occasional, JunkClientData, OCCASIONAL_TIME * 1000L, 1 ) == (Timer*) 0 )
443     {
444     syslog( LOG_CRIT, "tmr_create(occasional) failed" );
445     exit( 1 );
446     }
447     if ( numthrottles > 0 )
448     {
449     /* Set up the throttles timer. */
450     if ( tmr_create( (struct timeval*) 0, update_throttles, JunkClientData, THROTTLE_TIME * 1000L, 1 ) == (Timer*) 0 )
451     {
452     syslog( LOG_CRIT, "tmr_create(update_throttles) failed" );
453     exit( 1 );
454     }
455     }
456     #ifdef STATS_TIME
457     /* Set up the stats timer. */
458     if ( tmr_create( (struct timeval*) 0, show_stats, JunkClientData, STATS_TIME * 1000L, 1 ) == (Timer*) 0 )
459     {
460     syslog( LOG_CRIT, "tmr_create(show_stats) failed" );
461     exit( 1 );
462     }
463     #endif /* STATS_TIME */
464     start_time = stats_time = time( (time_t*) 0 );
465     stats_connections = stats_bytes = 0L;
466     stats_simultaneous = 0;
467    
468     /* If we're root, try to become someone else. */
469     if ( getuid() == 0 )
470     {
471     /* Set aux groups to null. */
472     if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
473     {
474     syslog( LOG_CRIT, "setgroups - %m" );
475     exit( 1 );
476     }
477     /* Set primary group. */
478     if ( setgid( gid ) < 0 )
479     {
480     syslog( LOG_CRIT, "setgid - %m" );
481     exit( 1 );
482     }
483     /* Try setting aux groups correctly - not critical if this fails. */
484     if ( initgroups( user, gid ) < 0 )
485     syslog( LOG_WARNING, "initgroups - %m" );
486     #ifdef HAVE_SETLOGIN
487     /* Set login name. */
488     (void) setlogin( user );
489     #endif /* HAVE_SETLOGIN */
490     /* Set uid. */
491     if ( setuid( uid ) < 0 )
492     {
493     syslog( LOG_CRIT, "setuid - %m" );
494     exit( 1 );
495     }
496     /* Check for unnecessary security exposure. */
497     if ( ! do_chroot )
498     syslog(
499     LOG_CRIT,
500     "started as root without requesting chroot(), warning only" );
501     }
502    
503     /* Initialize our connections table. */
504     maxconnects = fdwatch_get_nfiles();
505     if ( maxconnects < 0 )
506     {
507     syslog( LOG_CRIT, "fdwatch initialization failure" );
508     exit( 1 );
509     }
510     maxconnects -= SPARE_FDS;
511 root 1.3 #ifdef MMAP_MAX
512     maxconnects = maxconnects / 2;
513     #endif
514 root 1.1 connects = NEW( connecttab, maxconnects );
515     if ( connects == (connecttab*) 0 )
516     {
517     syslog( LOG_CRIT, "out of memory allocating a connecttab" );
518     exit( 1 );
519     }
520     for ( cnum = 0; cnum < maxconnects; ++cnum )
521     {
522     connects[cnum].conn_state = CNST_FREE;
523     connects[cnum].hc = (httpd_conn*) 0;
524     }
525     numconnects = 0;
526     httpd_conn_count = 0;
527    
528 root 1.2 if(conns_per_address) {
529     if(NULL == (blocks=calloc(BLOCKLIST_LENGTH, sizeof(blocktab)))) {
530     syslog(LOG_CRIT, "out of memory allocating a blocktab");
531     exit(1);
532     }
533     if(NULL == (match_ip=calloc(maxconnects, sizeof(int)))) {
534     syslog(LOG_CRIT, "out of memory");
535     exit(1);
536     }
537     }
538    
539 root 1.1 if ( hs != (httpd_server*) 0 )
540     {
541     if ( hs->listen4_fd != -1 )
542     fdwatch_add_fd( hs->listen4_fd, (void*) 0, FDW_READ );
543     if ( hs->listen6_fd != -1 )
544     fdwatch_add_fd( hs->listen6_fd, (void*) 0, FDW_READ );
545     }
546    
547     /* Main loop. */
548     (void) gettimeofday( &tv, (struct timezone*) 0 );
549     while ( ( ! terminate ) || numconnects > 0 )
550     {
551     /* Do the fd watch. */
552     num_ready = fdwatch( tmr_mstimeout( &tv ) );
553     if ( num_ready < 0 )
554     {
555     if ( errno == EINTR )
556     continue; /* try again */
557     syslog( LOG_ERR, "fdwatch - %m" );
558     exit( 1 );
559     }
560     (void) gettimeofday( &tv, (struct timezone*) 0 );
561     if ( num_ready == 0 )
562     {
563     /* No fd's are ready - run the timers. */
564     tmr_run( &tv );
565     continue;
566     }
567    
568     /* Is it a new connection? */
569     if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 &&
570     fdwatch_check_fd( hs->listen6_fd ) )
571     {
572 root 1.2 if ( handle_newconnect( &tv, hs->listen6_fd, AF_INET6 ) )
573 root 1.1 /* Go around the loop and do another fdwatch, rather than
574     ** dropping through and processing existing connections.
575     ** New connections always get priority.
576     */
577     continue;
578     }
579     if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 &&
580     fdwatch_check_fd( hs->listen4_fd ) )
581     {
582 root 1.2 if ( handle_newconnect( &tv, hs->listen4_fd, AF_INET ) )
583 root 1.1 /* Go around the loop and do another fdwatch, rather than
584     ** dropping through and processing existing connections.
585     ** New connections always get priority.
586     */
587     continue;
588     }
589     /* Find the connections that need servicing. */
590     for ( ridx = 0; ridx < num_ready; ++ridx )
591     {
592     c = (connecttab*) fdwatch_get_client_data( ridx );
593     if ( c == (connecttab*) 0 )
594     continue;
595     hc = c->hc;
596     if ( c->conn_state == CNST_READING &&
597     fdwatch_check_fd( hc->conn_fd ) )
598     handle_read( c, &tv );
599     else if ( c->conn_state == CNST_SENDING &&
600     fdwatch_check_fd( hc->conn_fd ) )
601     handle_send( c, &tv );
602     else if ( c->conn_state == CNST_LINGERING &&
603     fdwatch_check_fd( hc->conn_fd ) )
604     handle_linger( c, &tv );
605     }
606     tmr_run( &tv );
607    
608     if ( got_usr1 && ! terminate )
609     {
610     terminate = 1;
611     if ( hs != (httpd_server*) 0 )
612     {
613     httpd_terminate( hs );
614     hs = (httpd_server*) 0;
615     }
616     }
617     }
618    
619     /* The main loop terminated. */
620     shut_down();
621     syslog( LOG_NOTICE, "exiting" );
622     closelog();
623     exit( 0 );
624     }
625    
626    
627     static void
628     parse_args( int argc, char** argv )
629     {
630     int argn;
631    
632     debug = 0;
633     port = DEFAULT_PORT;
634     dir = (char*) 0;
635     #ifdef ALWAYS_CHROOT
636     do_chroot = 1;
637     #else /* ALWAYS_CHROOT */
638     do_chroot = 0;
639     #endif /* ALWAYS_CHROOT */
640     no_log = 0;
641     no_symlink = do_chroot;
642     #ifdef ALWAYS_VHOST
643     do_vhost = 1;
644     #else /* ALWAYS_VHOST */
645     do_vhost = 0;
646     #endif /* ALWAYS_VHOST */
647     #ifdef ALWAYS_GLOBAL_PASSWD
648     do_global_passwd = 1;
649     #else /* ALWAYS_GLOBAL_PASSWD */
650     do_global_passwd = 0;
651     #endif /* ALWAYS_GLOBAL_PASSWD */
652     #ifdef CGI_PATTERN
653     cgi_pattern = CGI_PATTERN;
654     #else /* CGI_PATTERN */
655     cgi_pattern = (char*) 0;
656     #endif /* CGI_PATTERN */
657 root 1.2 autoindex_prog = (char*) 0;
658 root 1.1 url_pattern = (char*) 0;
659     no_empty_referers = 0;
660     local_pattern = (char*) 0;
661     throttlefile = (char*) 0;
662     hostname = (char*) 0;
663     logfile = (char*) 0;
664     pidfile = (char*) 0;
665     user = DEFAULT_USER;
666     charset = DEFAULT_CHARSET;
667     argn = 1;
668     while ( argn < argc && argv[argn][0] == '-' )
669     {
670     if ( strcmp( argv[argn], "-C" ) == 0 && argn + 1 < argc )
671     {
672     ++argn;
673     read_config( argv[argn] );
674     }
675     else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc )
676     {
677     ++argn;
678     port = atoi( argv[argn] );
679     }
680     else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc )
681     {
682     ++argn;
683     dir = argv[argn];
684     }
685     else if ( strcmp( argv[argn], "-r" ) == 0 )
686     {
687     do_chroot = 1;
688     no_symlink = 1;
689     }
690     else if ( strcmp( argv[argn], "-nor" ) == 0 )
691     {
692     do_chroot = 0;
693     no_symlink = 0;
694     }
695     else if ( strcmp( argv[argn], "-s" ) == 0 )
696     no_symlink = 0;
697     else if ( strcmp( argv[argn], "-nos" ) == 0 )
698     no_symlink = 1;
699     else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
700     {
701     ++argn;
702     user = argv[argn];
703     }
704     else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc )
705     {
706     ++argn;
707     cgi_pattern = argv[argn];
708     }
709 root 1.2 else if ( strcmp( argv[argn], "-x" ) == 0 && argn + 1 < argc )
710     {
711     ++argn;
712     autoindex_prog = argv[argn];
713     }
714 root 1.1 else if ( strcmp( argv[argn], "-t" ) == 0 && argn + 1 < argc )
715     {
716     ++argn;
717     throttlefile = argv[argn];
718     }
719     else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc )
720     {
721     ++argn;
722     hostname = argv[argn];
723     }
724     else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc )
725     {
726     ++argn;
727     logfile = argv[argn];
728     }
729     else if ( strcmp( argv[argn], "-v" ) == 0 )
730     do_vhost = 1;
731     else if ( strcmp( argv[argn], "-nov" ) == 0 )
732     do_vhost = 0;
733     else if ( strcmp( argv[argn], "-g" ) == 0 )
734     do_global_passwd = 1;
735     else if ( strcmp( argv[argn], "-nog" ) == 0 )
736     do_global_passwd = 0;
737     else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc )
738     {
739     ++argn;
740     pidfile = argv[argn];
741     }
742     else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc )
743     {
744     ++argn;
745     charset = argv[argn];
746     }
747     else if ( strcmp( argv[argn], "-V" ) == 0 )
748     {
749     (void) fprintf( stderr, "%s\n", SERVER_SOFTWARE );
750     exit( 0 );
751     }
752     else if ( strcmp( argv[argn], "-D" ) == 0 )
753     debug = 1;
754 root 1.2 else if( strcmp(argv[argn], "-n") == 0)
755     conns_per_address = atoi(argv[++argn]);
756     else if( strcmp(argv[argn], "-o") == 0)
757 root 1.3 blocktime = atoi(argv[++argn]);
758 root 1.1 else
759     usage();
760     ++argn;
761     }
762     if ( argn != argc )
763     usage();
764     }
765    
766    
767     static void
768     usage( void )
769     {
770     (void) fprintf( stderr,
771 root 1.2 "usage: %s [-C configfile] [-p port] [-d dir] [-r|-nor] [-v|-nov] [-g|-nog] [-u user] [-c cgipat] [-t throttles] [-n connections] [-o timeout] [-h host] [-l logfile] [-i pidfile] [-T charset] [-V] [-D]\n",
772 root 1.1 argv0 );
773     exit( 1 );
774     }
775    
776    
777     static void
778     read_config( char* filename )
779     {
780     FILE* fp;
781     char line[10000];
782     char* cp;
783     char* cp2;
784     char* name;
785     char* value;
786    
787     fp = fopen( filename, "r" );
788     if ( fp == (FILE*) 0 )
789     {
790     perror( filename );
791     exit( 1 );
792     }
793    
794     while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
795     {
796     /* Trim comments. */
797     if ( ( cp = strchr( line, '#' ) ) != (char*) 0 )
798     *cp = '\0';
799    
800     /* Split line into words. */
801     for ( cp = line; *cp != '\0'; cp = cp2 )
802     {
803     /* Skip leading whitespace. */
804     cp += strspn( cp, " \t\n\r" );
805     /* Find next whitespace. */
806     cp2 = cp + strcspn( cp, " \t\n\r" );
807     /* Insert EOS and advance next-word pointer. */
808     while ( *cp2 == ' ' || *cp2 == '\t' || *cp2 == '\n' || *cp2 == '\r' )
809     *cp2++ = '\0';
810     /* Split into name and value. */
811     name = cp;
812     value = strchr( name, '=' );
813     if ( value != (char*) 0 )
814     *value++ = '\0';
815     /* Interpret. */
816     if ( strcasecmp( name, "debug" ) == 0 )
817     {
818     no_value_required( name, value );
819     debug = 1;
820     }
821     else if ( strcasecmp( name, "port" ) == 0 )
822     {
823     value_required( name, value );
824     port = atoi( value );
825     }
826     else if ( strcasecmp( name, "dir" ) == 0 )
827     {
828     value_required( name, value );
829     dir = e_strdup( value );
830     }
831     else if ( strcasecmp( name, "chroot" ) == 0 )
832     {
833     no_value_required( name, value );
834     do_chroot = 1;
835     no_symlink = 1;
836     }
837     else if ( strcasecmp( name, "nochroot" ) == 0 )
838     {
839     no_value_required( name, value );
840     do_chroot = 0;
841     no_symlink = 0;
842     }
843     else if ( strcasecmp( name, "symlink" ) == 0 )
844     {
845     no_value_required( name, value );
846     no_symlink = 0;
847     }
848     else if ( strcasecmp( name, "nosymlink" ) == 0 )
849     {
850     no_value_required( name, value );
851     no_symlink = 1;
852     }
853     else if ( strcasecmp( name, "symlinks" ) == 0 )
854     {
855     no_value_required( name, value );
856     no_symlink = 0;
857     }
858     else if ( strcasecmp( name, "nosymlinks" ) == 0 )
859     {
860     no_value_required( name, value );
861     no_symlink = 1;
862     }
863     else if ( strcasecmp( name, "user" ) == 0 )
864     {
865     value_required( name, value );
866     user = e_strdup( value );
867     }
868     else if ( strcasecmp( name, "cgipat" ) == 0 )
869     {
870     value_required( name, value );
871     cgi_pattern = e_strdup( value );
872     }
873 root 1.2 else if ( strcasecmp( name, "indexprog" ) == 0 )
874     {
875     value_required( name, value );
876     autoindex_prog = e_strdup( value );
877     }
878 root 1.1 else if ( strcasecmp( name, "urlpat" ) == 0 )
879     {
880     value_required( name, value );
881     url_pattern = e_strdup( value );
882     }
883     else if ( strcasecmp( name, "noemptyreferers" ) == 0 )
884     {
885     no_value_required( name, value );
886     no_empty_referers = 1;
887     }
888     else if ( strcasecmp( name, "localpat" ) == 0 )
889     {
890     value_required( name, value );
891     local_pattern = e_strdup( value );
892     }
893     else if ( strcasecmp( name, "throttles" ) == 0 )
894     {
895     value_required( name, value );
896     throttlefile = e_strdup( value );
897     }
898     else if ( strcasecmp( name, "host" ) == 0 )
899     {
900     value_required( name, value );
901     hostname = e_strdup( value );
902     }
903     else if ( strcasecmp( name, "logfile" ) == 0 )
904     {
905     value_required( name, value );
906     logfile = e_strdup( value );
907     }
908     else if ( strcasecmp( name, "vhost" ) == 0 )
909     {
910     no_value_required( name, value );
911     do_vhost = 1;
912     }
913     else if ( strcasecmp( name, "novhost" ) == 0 )
914     {
915     no_value_required( name, value );
916     do_vhost = 0;
917     }
918     else if ( strcasecmp( name, "globalpasswd" ) == 0 )
919     {
920     no_value_required( name, value );
921     do_global_passwd = 1;
922     }
923     else if ( strcasecmp( name, "noglobalpasswd" ) == 0 )
924     {
925     no_value_required( name, value );
926     do_global_passwd = 0;
927     }
928     else if ( strcasecmp( name, "pidfile" ) == 0 )
929     {
930     value_required( name, value );
931     pidfile = e_strdup( value );
932     }
933     else if ( strcasecmp( name, "charset" ) == 0 )
934     {
935     value_required( name, value );
936     charset = e_strdup( value );
937     }
938 root 1.2 else if(strcasecmp(name, "connections") == 0) {
939     value_required(name, value);
940     conns_per_address = atoi(value);
941     }
942     else if(strcasecmp(name, "blocktime") == 0) {
943     value_required(name, value);
944 root 1.3 blocktime = atoi(value);
945 root 1.2 } else
946 root 1.1 {
947     (void) fprintf(
948     stderr, "%s: unknown config option '%s'\n", argv0, name );
949     exit( 1 );
950     }
951     }
952     }
953    
954     (void) fclose( fp );
955     }
956    
957    
958     static void
959     value_required( char* name, char* value )
960     {
961     if ( value == (char*) 0 )
962     {
963     (void) fprintf(
964     stderr, "%s: value required for %s option\n", argv0, name );
965     exit( 1 );
966     }
967     }
968    
969    
970     static void
971     no_value_required( char* name, char* value )
972     {
973     if ( value != (char*) 0 )
974     {
975     (void) fprintf(
976     stderr, "%s: no value required for %s option\n",
977     argv0, name );
978     exit( 1 );
979     }
980     }
981    
982    
983     static char*
984     e_strdup( char* oldstr )
985     {
986     char* newstr;
987    
988     newstr = strdup( oldstr );
989     if ( newstr == (char*) 0 )
990     {
991     syslog( LOG_CRIT, "out of memory copying a string" );
992     (void) fprintf( stderr, "%s: out of memory copying a string\n", argv0 );
993     exit( 1 );
994     }
995     return newstr;
996     }
997    
998    
999     static void
1000     lookup_hostname( httpd_sockaddr* sa4P, size_t sa4_len, int* gotv4P, httpd_sockaddr* sa6P, size_t sa6_len, int* gotv6P )
1001     {
1002     #if defined(HAVE_GETADDRINFO) && defined(HAVE_GAI_STRERROR)
1003    
1004     struct addrinfo hints;
1005     struct addrinfo* ai;
1006     struct addrinfo* ai2;
1007     struct addrinfo* aiv4;
1008     struct addrinfo* aiv6;
1009     int gaierr;
1010     char strport[10];
1011    
1012     memset( &hints, 0, sizeof(hints) );
1013     hints.ai_family = AF_UNSPEC;
1014     hints.ai_flags = AI_PASSIVE;
1015     hints.ai_socktype = SOCK_STREAM;
1016     (void) snprintf( strport, sizeof(strport), "%d", port );
1017     if ( (gaierr = getaddrinfo( hostname, strport, &hints, &ai )) != 0 )
1018     {
1019     syslog(
1020     LOG_CRIT, "getaddrinfo %.80s - %.80s",
1021     hostname, gai_strerror( gaierr ) );
1022     (void) fprintf(
1023     stderr, "%s: getaddrinfo %s - %s\n",
1024     argv0, hostname, gai_strerror( gaierr ) );
1025     exit( 1 );
1026     }
1027    
1028     /* Find the first IPv4 and IPv6 entries. */
1029     aiv4 = (struct addrinfo*) 0;
1030     aiv6 = (struct addrinfo*) 0;
1031     for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
1032     {
1033     switch ( ai2->ai_family )
1034     {
1035     case AF_INET:
1036     if ( aiv4 == (struct addrinfo*) 0 )
1037     aiv4 = ai2;
1038     break;
1039     #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
1040     case AF_INET6:
1041     if ( aiv6 == (struct addrinfo*) 0 )
1042     aiv6 = ai2;
1043     break;
1044     #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
1045     }
1046     }
1047    
1048     if ( aiv4 == (struct addrinfo*) 0 )
1049     *gotv4P = 0;
1050     else
1051     {
1052     if ( sa4_len < aiv4->ai_addrlen )
1053     {
1054     syslog(
1055     LOG_CRIT, "%.80s - sockaddr too small (%d < %d)",
1056     hostname, sa4_len, aiv4->ai_addrlen );
1057     exit( 1 );
1058     }
1059     memset( sa4P, 0, sa4_len );
1060     memcpy( sa4P, aiv4->ai_addr, aiv4->ai_addrlen );
1061     *gotv4P = 1;
1062     }
1063     if ( aiv6 == (struct addrinfo*) 0 )
1064     *gotv6P = 0;
1065     else
1066     {
1067     if ( sa6_len < aiv6->ai_addrlen )
1068     {
1069     syslog(
1070     LOG_CRIT, "%.80s - sockaddr too small (%d < %d)",
1071     hostname, sa6_len, aiv6->ai_addrlen );
1072     exit( 1 );
1073     }
1074     memset( sa6P, 0, sa6_len );
1075     memcpy( sa6P, aiv6->ai_addr, aiv6->ai_addrlen );
1076     *gotv6P = 1;
1077     }
1078    
1079     freeaddrinfo( ai );
1080    
1081     #else /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1082    
1083     struct hostent* he;
1084    
1085     *gotv6P = 0;
1086    
1087     memset( sa4P, 0, sa4_len );
1088     #ifdef notdef
1089     /* We don't really need to set sa_len. */
1090     #ifdef HAVE_SA_LEN
1091     sa4P->sa_len = sa4_len;
1092     #endif /* HAVE_SA_LEN */
1093     #endif
1094     sa4P->sa.sa_family = AF_INET;
1095     if ( hostname == (char*) 0 )
1096     sa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY );
1097     else
1098     {
1099     sa4P->sa_in.sin_addr.s_addr = inet_addr( hostname );
1100     if ( (int) sa4P->sa_in.sin_addr.s_addr == -1 )
1101     {
1102     he = gethostbyname( hostname );
1103     if ( he == (struct hostent*) 0 )
1104     {
1105     #ifdef HAVE_HSTRERROR
1106     syslog(
1107     LOG_CRIT, "gethostbyname %.80s - %.80s",
1108     hostname, hstrerror( h_errno ) );
1109     (void) fprintf(
1110     stderr, "%s: gethostbyname %s - %s\n",
1111     argv0, hostname, hstrerror( h_errno ) );
1112     #else /* HAVE_HSTRERROR */
1113     syslog( LOG_CRIT, "gethostbyname %.80s failed", hostname );
1114     (void) fprintf(
1115     stderr, "%s: gethostbyname %s failed\n", argv0, hostname );
1116     #endif /* HAVE_HSTRERROR */
1117     exit( 1 );
1118     }
1119     if ( he->h_addrtype != AF_INET )
1120     {
1121     syslog( LOG_CRIT, "%.80s - non-IP network address", hostname );
1122     (void) fprintf(
1123     stderr, "%s: %s - non-IP network address\n",
1124     argv0, hostname );
1125     exit( 1 );
1126     }
1127     (void) memcpy(
1128     &sa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length );
1129     }
1130     }
1131     sa4P->sa_in.sin_port = htons( port );
1132     *gotv4P = 1;
1133    
1134     #endif /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1135     }
1136    
1137    
1138     static void
1139     read_throttlefile( char* throttlefile )
1140     {
1141     FILE* fp;
1142     char buf[5000];
1143     char* cp;
1144     int len;
1145     char pattern[5000];
1146     long limit;
1147     struct timeval tv;
1148    
1149     fp = fopen( throttlefile, "r" );
1150     if ( fp == (FILE*) 0 )
1151     {
1152     syslog( LOG_CRIT, "%.80s - %m", throttlefile );
1153     perror( throttlefile );
1154     exit( 1 );
1155     }
1156    
1157     (void) gettimeofday( &tv, (struct timezone*) 0 );
1158    
1159     while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 )
1160     {
1161     /* Nuke comments. */
1162     cp = strchr( buf, '#' );
1163     if ( cp != (char*) 0 )
1164     *cp = '\0';
1165    
1166     /* Nuke trailing whitespace. */
1167     len = strlen( buf );
1168     while ( len > 0 &&
1169     ( buf[len-1] == ' ' || buf[len-1] == '\t' ||
1170     buf[len-1] == '\n' || buf[len-1] == '\r' ) )
1171     buf[--len] = '\0';
1172    
1173     /* Ignore empty lines. */
1174     if ( len == 0 )
1175     continue;
1176    
1177     /* Parse line. */
1178     if ( sscanf( buf, " %4900[^ \t] %ld", pattern, &limit ) != 2 || limit <= 0 )
1179     {
1180     syslog( LOG_CRIT,
1181     "unparsable line in %.80s - %.80s", throttlefile, buf );
1182     (void) fprintf( stderr,
1183     "%s: unparsable line in %.80s - %.80s\n",
1184     argv0, throttlefile, buf );
1185     continue;
1186     }
1187    
1188     /* Nuke any leading slashes in pattern. */
1189     if ( pattern[0] == '/' )
1190     (void) strcpy( pattern, &pattern[1] );
1191     while ( ( cp = strstr( pattern, "|/" ) ) != (char*) 0 )
1192     (void) strcpy( cp + 1, cp + 2 );
1193    
1194     /* Check for room in throttles. */
1195     if ( numthrottles >= maxthrottles )
1196     {
1197     if ( maxthrottles == 0 )
1198     {
1199     maxthrottles = 100; /* arbitrary */
1200     throttles = NEW( throttletab, maxthrottles );
1201     }
1202     else
1203     {
1204     maxthrottles *= 2;
1205     throttles = RENEW( throttles, throttletab, maxthrottles );
1206     }
1207     if ( throttles == (throttletab*) 0 )
1208     {
1209     syslog( LOG_CRIT, "out of memory allocating a throttletab" );
1210     (void) fprintf(
1211     stderr, "%s: out of memory allocating a throttletab\n",
1212     argv0 );
1213     exit( 1 );
1214     }
1215     }
1216    
1217     /* Add to table. */
1218     throttles[numthrottles].pattern = strdup( pattern );
1219     if ( throttles[numthrottles].pattern == (char*) 0 )
1220     {
1221     syslog( LOG_CRIT, "out of memory copying a throttle pattern" );
1222     (void) fprintf(
1223     stderr, "%s: out of memory copying a throttle pattern\n",
1224     argv0 );
1225     exit( 1 );
1226     }
1227     throttles[numthrottles].limit = limit;
1228     throttles[numthrottles].rate = 0;
1229     throttles[numthrottles].bytes_since_avg = 0;
1230     throttles[numthrottles].num_sending = 0;
1231    
1232     ++numthrottles;
1233     }
1234     (void) fclose( fp );
1235     }
1236    
1237    
1238     static void
1239     shut_down( void )
1240     {
1241     int cnum;
1242     struct timeval tv;
1243    
1244     (void) gettimeofday( &tv, (struct timezone*) 0 );
1245     logstats( &tv );
1246     for ( cnum = 0; cnum < maxconnects; ++cnum )
1247     {
1248     if ( connects[cnum].conn_state != CNST_FREE )
1249     httpd_close_conn( connects[cnum].hc, &tv );
1250     if ( connects[cnum].hc != (httpd_conn*) 0 )
1251     {
1252     httpd_destroy_conn( connects[cnum].hc );
1253     free( (void*) connects[cnum].hc );
1254     --httpd_conn_count;
1255     connects[cnum].hc = (httpd_conn*) 0;
1256     }
1257     }
1258     if ( hs != (httpd_server*) 0 )
1259     {
1260     httpd_server* ths = hs;
1261     hs = (httpd_server*) 0;
1262     httpd_terminate( ths );
1263     }
1264     mmc_destroy();
1265     tmr_destroy();
1266     free( (void*) connects );
1267     if ( throttles != (throttletab*) 0 )
1268     free( (void*) throttles );
1269 root 1.2 if(conns_per_address) {
1270     free(blocks);
1271     free(match_ip);
1272     }
1273 root 1.1 }
1274    
1275    
1276     static int
1277 root 1.2 handle_newconnect( struct timeval* tvP, int listen_fd, int family )
1278 root 1.1 {
1279 root 1.2 int cnum, i, j;
1280 root 1.1 connecttab* c;
1281     ClientData client_data;
1282    
1283     /* This loops until the accept() fails, trying to start new
1284     ** connections as fast as possible so we don't overrun the
1285     ** listen queue.
1286     */
1287     for (;;)
1288     {
1289     /* Is there room in the connection table? */
1290     if ( numconnects >= maxconnects )
1291     {
1292     /* Out of connection slots. Run the timers, then the
1293     ** existing connections, and maybe we'll free up a slot
1294     ** by the time we get back here.
1295     **/
1296     syslog( LOG_WARNING, "too many connections!" );
1297     tmr_run( tvP );
1298     return 0;
1299     }
1300     /* Find a free connection entry. */
1301     for ( cnum = 0; cnum < maxconnects; ++cnum )
1302     if ( connects[cnum].conn_state == CNST_FREE )
1303     break;
1304     c = &connects[cnum];
1305     /* Make the httpd_conn if necessary. */
1306     if ( c->hc == (httpd_conn*) 0 )
1307     {
1308     c->hc = NEW( httpd_conn, 1 );
1309     if ( c->hc == (httpd_conn*) 0 )
1310     {
1311     syslog( LOG_CRIT, "out of memory allocating an httpd_conn" );
1312     exit( 1 );
1313     }
1314     c->hc->initialized = 0;
1315     ++httpd_conn_count;
1316     }
1317    
1318     /* Get the connection. */
1319     switch ( httpd_get_conn( hs, listen_fd, c->hc ) )
1320     {
1321     case GC_FAIL:
1322     case GC_NO_MORE:
1323     return 1;
1324     }
1325 root 1.2
1326 root 1.1 c->conn_state = CNST_READING;
1327     ++numconnects;
1328     client_data.p = c;
1329     c->idle_read_timer = tmr_create(
1330     tvP, idle_read_connection, client_data, IDLE_READ_TIMELIMIT * 1000L,
1331     0 );
1332     if ( c->idle_read_timer == (Timer*) 0 )
1333     {
1334     syslog( LOG_CRIT, "tmr_create(idle_read_connection) failed" );
1335     exit( 1 );
1336     }
1337     c->idle_send_timer = (Timer*) 0;
1338     c->wakeup_timer = (Timer*) 0;
1339     c->linger_timer = (Timer*) 0;
1340     c->bytes_sent = 0;
1341     c->numtnums = 0;
1342    
1343     /* Set the connection file descriptor to no-delay mode. */
1344     httpd_set_ndelay( c->hc->conn_fd );
1345    
1346     fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
1347    
1348     ++stats_connections;
1349     if ( numconnects > stats_simultaneous )
1350     stats_simultaneous = numconnects;
1351 root 1.2
1352     if(conns_per_address) {
1353     for(i=0; i<BLOCKLIST_LENGTH; i++)
1354 root 1.3 if(blocks[i].next_access) {
1355     if (blocks[i].next_access > tvP->tv_sec) {
1356 root 1.2 if(memcmp(&(c->hc->client_addr.sa_in.sin_addr), &(blocks[i].sin_addr),
1357     sizeof(struct in_addr)) == 0
1358     #ifdef HAVE_SOCKADDR_IN6
1359     || memcmp(&(c->hc->client_addr.sa_in6.sin6_addr),
1360     &(blocks[i].sin6_addr), sizeof(struct in6_addr)) == 0
1361     #endif /* HAVE_SOCKADDR_IN6 */
1362     ) {
1363 root 1.3 blocks[i].next_access = tvP->tv_sec + blocktime;
1364     httpd_send_err_blocked(c->hc);
1365     clear_connection(c, tvP);
1366    
1367     return 0;
1368 root 1.2 }
1369     } else
1370 root 1.3 blocks[i].next_access = 0;
1371 root 1.2 }
1372     cnum = j = 0;
1373     #ifdef HAVE_SOCKADDR_IN6
1374     if(AF_INET == family) {
1375     #endif /* HAVE_SOCKADDR_IN6 */
1376     for(i=0; i<maxconnects; i++)
1377     if((connects[i].conn_state != CNST_FREE) &&
1378     memcmp(&(connects[i].hc->client_addr.sa_in.sin_addr),
1379     &(c->hc->client_addr.sa_in.sin_addr), sizeof(struct in_addr)) == 0)
1380     cnum++, match_ip[j++] = i;
1381     #ifdef HAVE_SOCKADDR_IN6
1382     } else {
1383     for(i=0; i<maxconnects; i++)
1384     if(connects[i].conn_state != CNST_FREE &&
1385     memcmp(&(connects[i].hc->client_addr.sa_in6.sin6_addr),
1386     &(c->hc->client_addr.sa_in6.sin6_addr), sizeof(struct in6_addr)) == 0)
1387     cnum++, match_ip[j++] = i;
1388     }
1389     #endif /* HAVE_SOCKADDR_IN6 */
1390     if(cnum > conns_per_address) {
1391 root 1.3 httpd_send_err_blocked(c->hc);
1392     clear_connection(c, tvP);
1393 root 1.4 /*for(i=0; i<j; i++)
1394     shutdown (connects[match_ip[i]].hc->conn_fd, 2);*/
1395 root 1.2 for(j=i=0; i<BLOCKLIST_LENGTH; i++)
1396 root 1.3 if(blocks[i].next_access < blocks[j].next_access) {
1397 root 1.2 j = i;
1398 root 1.3 if(blocks[i].next_access == 0) break;
1399 root 1.2 }
1400 root 1.3 memcpy(&(blocks[j].sin_addr), &(c->hc->client_addr.sa_in.sin_addr), sizeof(struct in_addr));
1401 root 1.2 #ifdef HAVE_SOCKADDR_IN6
1402 root 1.3 memcpy(&(blocks[j].sin_addr), &(c->hc->client_addr.sa_in6.sin6_addr), sizeof(struct in6_addr));
1403 root 1.2 #endif /* HAVE_SOCKADDR_IN6 */
1404 root 1.3 blocks[j].next_access = tvP->tv_sec + blocktime;
1405    
1406     return 0;
1407 root 1.2 }
1408 root 1.3 }
1409     }
1410    
1411     return 1;
1412 root 1.1 }
1413    
1414     static void
1415     handle_read( connecttab* c, struct timeval* tvP )
1416     {
1417     int sz;
1418     ClientData client_data;
1419     httpd_conn* hc = c->hc;
1420    
1421     /* Is there room in our buffer to read more bytes? */
1422     if ( hc->read_idx >= hc->read_size )
1423     {
1424     if ( hc->read_size > 5000 )
1425     {
1426     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1427     clear_connection( c, tvP );
1428     return;
1429     }
1430     httpd_realloc_str(
1431     &hc->read_buf, &hc->read_size, hc->read_size + 1000 );
1432     }
1433    
1434     /* Read some more bytes. */
1435     sz = read(
1436     hc->conn_fd, &(hc->read_buf[hc->read_idx]),
1437     hc->read_size - hc->read_idx );
1438     /* Ignore EWOULDBLOCK errors. At first glance you would think that
1439     ** connections returned by fdwatch as readable should never give an
1440     ** EWOULDBLOCK; however, this apparently can happen if a packet gets
1441     ** garbled.
1442     */
1443     if ( sz == 0 || ( sz < 0 && ( errno != EWOULDBLOCK ) ) )
1444     {
1445     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1446     clear_connection( c, tvP );
1447     return;
1448     }
1449     hc->read_idx += sz;
1450    
1451     /* Do we have a complete request yet? */
1452     switch ( httpd_got_request( hc ) )
1453     {
1454     case GR_NO_REQUEST:
1455     return;
1456     case GR_BAD_REQUEST:
1457     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1458     clear_connection( c, tvP );
1459     return;
1460     }
1461    
1462     /* Yes. Try parsing and resolving it. */
1463     if ( httpd_parse_request( hc ) < 0 )
1464     {
1465     clear_connection( c, tvP );
1466     return;
1467     }
1468    
1469     /* Check the throttle table */
1470     if ( ! check_throttles( c ) )
1471     {
1472     httpd_send_err(
1473     hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
1474     clear_connection( c, tvP );
1475     return;
1476     }
1477    
1478     /* Start the connection going. */
1479     if ( httpd_start_request( hc, tvP ) < 0 )
1480     {
1481     /* Something went wrong. Close down the connection. */
1482     clear_connection( c, tvP );
1483     return;
1484     }
1485    
1486     /* Fill in bytes_to_send. */
1487     if ( hc->got_range )
1488     {
1489     c->bytes_sent = hc->init_byte_loc;
1490     c->bytes_to_send = hc->end_byte_loc + 1;
1491     }
1492     else
1493     c->bytes_to_send = hc->bytes_to_send;
1494    
1495     /* Check if it's already handled. */
1496 root 1.2 if ( hc->file_address == (char*) 0
1497     #ifdef MMAP_MAX
1498     && hc->file_fd <= 0
1499     #endif
1500     )
1501 root 1.1 {
1502     /* No file address means someone else is handling it. */
1503     c->bytes_sent = hc->bytes_sent;
1504     clear_connection( c, tvP );
1505     return;
1506     }
1507     if ( c->bytes_sent >= c->bytes_to_send )
1508     {
1509     /* There's nothing to send. */
1510     clear_connection( c, tvP );
1511     return;
1512     }
1513    
1514     /* Cool, we have a valid connection and a file to send to it. */
1515     c->conn_state = CNST_SENDING;
1516     c->started_at = tvP->tv_sec;
1517     c->wouldblock_delay = 0;
1518     client_data.p = c;
1519     tmr_cancel( c->idle_read_timer );
1520     c->idle_read_timer = (Timer*) 0;
1521     c->idle_send_timer = tmr_create(
1522     tvP, idle_send_connection, client_data, IDLE_SEND_TIMELIMIT * 1000L,
1523     0 );
1524     if ( c->idle_send_timer == (Timer*) 0 )
1525     {
1526     syslog( LOG_CRIT, "tmr_create(idle_send_connection) failed" );
1527     exit( 1 );
1528     }
1529    
1530     fdwatch_del_fd( hc->conn_fd );
1531     fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
1532     }
1533    
1534    
1535     static void
1536     handle_send( connecttab* c, struct timeval* tvP )
1537     {
1538     int sz, coast;
1539     ClientData client_data;
1540     time_t elapsed;
1541     httpd_conn* hc = c->hc;
1542    
1543     /* Do we need to write the headers first? */
1544     if ( hc->responselen == 0 )
1545     {
1546     /* No, just write the file. */
1547 root 1.2 #ifdef MMAP_MAX
1548     if (hc->file_address)
1549     {
1550     #endif
1551     sz = write(
1552     hc->conn_fd, &(hc->file_address[c->bytes_sent]),
1553     MIN( c->bytes_to_send - c->bytes_sent, c->limit ) );
1554     #ifdef MMAP_MAX
1555     }
1556     else
1557     {
1558     if (hc->write_ofs >= WRITE_BUFFER)
1559     {
1560 root 1.3 /* refill buffer */
1561 root 1.2 lseek (hc->file_fd, c->bytes_sent, SEEK_SET);
1562     read (hc->file_fd, hc->write_buf, WRITE_BUFFER);
1563     hc->write_ofs = 0;
1564     }
1565    
1566     sz = write(
1567     hc->conn_fd, hc->write_buf + hc->write_ofs,
1568     MIN( c->bytes_to_send - c->bytes_sent, MIN( WRITE_BUFFER - hc->write_ofs, c->limit ) ) );
1569    
1570     if (sz > 0)
1571     hc->write_ofs += sz;
1572    
1573     }
1574     #endif
1575 root 1.1 }
1576     else
1577     {
1578     /* Yes. We'll combine headers and file into a single writev(),
1579     ** hoping that this generates a single packet.
1580     */
1581     struct iovec iv[2];
1582 root 1.2 int ivc;
1583 root 1.1
1584     iv[0].iov_base = hc->response;
1585     iv[0].iov_len = hc->responselen;
1586 root 1.2
1587     #ifdef MMAP_MAX
1588     if (hc->file_address)
1589     #endif
1590     {
1591     iv[1].iov_base = &(hc->file_address[c->bytes_sent]);
1592     iv[1].iov_len = MIN( c->bytes_to_send - c->bytes_sent, c->limit );
1593     ivc = 2;
1594     }
1595     #ifdef MMAP_MAX
1596     else
1597     ivc = 1;
1598     #endif
1599     sz = writev( hc->conn_fd, iv, ivc );
1600 root 1.1 }
1601    
1602     if ( sz == 0 ||
1603     ( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) )
1604     {
1605     /* This shouldn't happen, but some kernels, e.g.
1606     ** SunOS 4.1.x, are broken and select() says that
1607     ** O_NDELAY sockets are always writable even when
1608     ** they're actually not.
1609     **
1610     ** Current workaround is to block sending on this
1611     ** socket for a brief adaptively-tuned period.
1612     ** Fortunately we already have all the necessary
1613     ** blocking code, for use with throttling.
1614     */
1615     c->wouldblock_delay += MIN_WOULDBLOCK_DELAY;
1616     c->conn_state = CNST_PAUSING;
1617     fdwatch_del_fd( hc->conn_fd );
1618     client_data.p = c;
1619     c->wakeup_timer = tmr_create(
1620     tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 );
1621     if ( c->wakeup_timer == (Timer*) 0 )
1622     {
1623     syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1624     exit( 1 );
1625     }
1626     return;
1627     }
1628     if ( sz < 0 )
1629     {
1630     /* Something went wrong, close this connection.
1631     **
1632     ** If it's just an EPIPE, don't bother logging, that
1633     ** just means the client hung up on us.
1634     **
1635     ** On some systems, write() occasionally gives an EINVAL.
1636     ** Dunno why, something to do with the socket going
1637     ** bad. Anyway, we don't log those either.
1638     **
1639     ** And ECONNRESET isn't interesting either.
1640     */
1641     if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET )
1642     syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
1643     clear_connection( c, tvP );
1644     return;
1645     }
1646    
1647     /* Ok, we wrote something. */
1648     tmr_reset( tvP, c->idle_send_timer );
1649     /* Was this a headers + file writev()? */
1650     if ( hc->responselen > 0 )
1651     {
1652     /* Yes; did we write only part of the headers? */
1653     if ( sz < hc->responselen )
1654     {
1655     /* Yes; move the unwritten part to the front of the buffer. */
1656     int newlen = hc->responselen - sz;
1657     (void) memcpy( hc->response, &(hc->response[sz]), newlen );
1658     hc->responselen = newlen;
1659     sz = 0;
1660     }
1661     else
1662     {
1663     /* Nope, we wrote the full headers, so adjust accordingly. */
1664     sz -= hc->responselen;
1665     hc->responselen = 0;
1666     }
1667     }
1668     /* And update how much of the file we wrote. */
1669     c->bytes_sent += sz;
1670     c->hc->bytes_sent += sz;
1671    
1672     /* Are we done? */
1673     if ( c->bytes_sent >= c->bytes_to_send )
1674     {
1675     /* This conection is finished! */
1676     clear_connection( c, tvP );
1677     return;
1678     }
1679    
1680     /* Tune the (blockheaded) wouldblock delay. */
1681     if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY )
1682     c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY;
1683    
1684     /* If we're throttling, check if we're sending too fast. */
1685     if ( c->limit != THROTTLE_NOLIMIT )
1686     {
1687     elapsed = tvP->tv_sec - c->started_at;
1688     if ( elapsed == 0 || c->hc->bytes_sent / elapsed > c->limit )
1689     {
1690     c->conn_state = CNST_PAUSING;
1691     fdwatch_del_fd( hc->conn_fd );
1692     /* When should we send the next c->limit bytes
1693     ** to get back on schedule? If less than a second
1694     ** (integer math rounding), use 1/8 second.
1695     */
1696     coast = ( c->hc->bytes_sent + c->limit ) / c->limit - elapsed;
1697     client_data.p = c;
1698     c->wakeup_timer = tmr_create(
1699     tvP, wakeup_connection, client_data,
1700     coast ? ( coast * 1000L ) : 125L, 0 );
1701     if ( c->wakeup_timer == (Timer*) 0 )
1702     {
1703     syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
1704     exit( 1 );
1705     }
1706     }
1707     }
1708     }
1709    
1710    
1711     static void
1712     handle_linger( connecttab* c, struct timeval* tvP )
1713     {
1714     char buf[1024];
1715     int r;
1716    
1717     /* In lingering-close mode we just read and ignore bytes. An error
1718     ** or EOF ends things, otherwise we go until a timeout.
1719     */
1720     r = read( c->hc->conn_fd, buf, sizeof(buf) );
1721     if ( r <= 0 )
1722     really_clear_connection( c, tvP );
1723     }
1724    
1725    
1726     static int
1727     check_throttles( connecttab* c )
1728     {
1729     int tnum;
1730    
1731     c->numtnums = 0;
1732     c->limit = THROTTLE_NOLIMIT;
1733     for ( tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS;
1734     ++tnum )
1735     if ( match( throttles[tnum].pattern, c->hc->expnfilename ) )
1736     {
1737     /* If we're way over the limit, don't even start. */
1738     if ( throttles[tnum].rate > throttles[tnum].limit * 2 )
1739     return 0;
1740     if ( throttles[tnum].num_sending < 0 )
1741     {
1742     syslog( LOG_ERR, "throttle sending count was negative - shouldn't happen!" );
1743     throttles[tnum].num_sending = 0;
1744     }
1745     c->tnums[c->numtnums++] = tnum;
1746     ++throttles[tnum].num_sending;
1747     c->limit = MIN(
1748     c->limit, throttles[tnum].limit / throttles[tnum].num_sending );
1749     }
1750     return 1;
1751     }
1752    
1753    
1754     static void
1755     clear_throttles( connecttab* c, struct timeval* tvP )
1756     {
1757     int i, tnum;
1758    
1759     for ( i = 0; i < c->numtnums; ++i )
1760     {
1761     tnum = c->tnums[i];
1762     --throttles[tnum].num_sending;
1763     throttles[tnum].bytes_since_avg += c->hc->bytes_sent;
1764     }
1765     }
1766    
1767    
1768     static void
1769     update_throttles( ClientData client_data, struct timeval* nowP )
1770     {
1771     int tnum;
1772    
1773     for ( tnum = 0; tnum < numthrottles; ++tnum )
1774     {
1775     throttles[tnum].rate =
1776     ( 3 * throttles[tnum].rate +
1777     throttles[tnum].bytes_since_avg / THROTTLE_TIME ) / 4;
1778     throttles[tnum].bytes_since_avg = 0;
1779     /* Log a warning message if necessary. */
1780     if ( throttles[tnum].rate > throttles[tnum].limit )
1781     {
1782     if ( throttles[tnum].rate > throttles[tnum].limit * 2 )
1783     syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld GREATLY exceeding limit %ld", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].limit );
1784     else
1785     syslog( LOG_NOTICE, "throttle #%d '%.80s' rate %ld exceeding limit %ld", tnum, throttles[tnum].pattern, throttles[tnum].rate, throttles[tnum].limit );
1786     }
1787     }
1788     }
1789    
1790    
1791     static void
1792     clear_connection( connecttab* c, struct timeval* tvP )
1793     {
1794     ClientData client_data;
1795    
1796     /* If we haven't actually sent the buffered response yet, do so now. */
1797     httpd_write_response( c->hc );
1798    
1799     if ( c->idle_read_timer != (Timer*) 0 )
1800     {
1801     tmr_cancel( c->idle_read_timer );
1802     c->idle_read_timer = 0;
1803     }
1804     if ( c->idle_send_timer != (Timer*) 0 )
1805     {
1806     tmr_cancel( c->idle_send_timer );
1807     c->idle_send_timer = 0;
1808     }
1809     if ( c->wakeup_timer != (Timer*) 0 )
1810     {
1811     tmr_cancel( c->wakeup_timer );
1812     c->wakeup_timer = 0;
1813     }
1814    
1815     /* This is our version of Apache's lingering_close() routine, which is
1816     ** their version of the often-broken SO_LINGER socket option. For why
1817     ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html
1818     ** What we do is delay the actual closing for a few seconds, while reading
1819     ** any bytes that come over the connection. However, we don't want to do
1820     ** this unless it's necessary, because it ties up a connection slot and
1821     ** file descriptor which means our maximum connection-handling rate
1822     ** is lower. So, elsewhere we set a flag when we detect the few
1823     ** circumstances that make a lingering close necessary. If the flag
1824     ** isn't set we do the real close now.
1825     */
1826     if ( c->hc->should_linger )
1827     {
1828     c->conn_state = CNST_LINGERING;
1829     fdwatch_del_fd( c->hc->conn_fd );
1830     fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
1831     /* Make sure we are still in no-delay mode. */
1832     httpd_set_ndelay( c->hc->conn_fd );
1833     client_data.p = c;
1834     c->linger_timer = tmr_create(
1835     tvP, linger_clear_connection, client_data, LINGER_TIME * 1000L, 0 );
1836     if ( c->linger_timer == (Timer*) 0 )
1837     {
1838     syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" );
1839     exit( 1 );
1840     }
1841     }
1842     else
1843     really_clear_connection( c, tvP );
1844     }
1845    
1846    
1847     static void
1848     really_clear_connection( connecttab* c, struct timeval* tvP )
1849     {
1850     stats_bytes += c->bytes_to_send;
1851     fdwatch_del_fd( c->hc->conn_fd );
1852     httpd_close_conn( c->hc, tvP );
1853     clear_throttles( c, tvP );
1854     if ( c->linger_timer != (Timer*) 0 )
1855     {
1856     tmr_cancel( c->linger_timer );
1857     c->linger_timer = 0;
1858     }
1859     c->conn_state = CNST_FREE;
1860     --numconnects;
1861     }
1862    
1863    
1864     static void
1865     idle_read_connection( ClientData client_data, struct timeval* nowP )
1866     {
1867     connecttab* c;
1868    
1869     c = (connecttab*) client_data.p;
1870     c->idle_read_timer = (Timer*) 0;
1871     if ( c->conn_state != CNST_FREE )
1872     {
1873     syslog( LOG_INFO,
1874     "%.80s connection timed out reading",
1875     httpd_ntoa( &c->hc->client_addr ) );
1876     httpd_send_err( c->hc, 408, httpd_err408title, "", httpd_err408form, "" );
1877     clear_connection( c, nowP );
1878     }
1879     }
1880    
1881    
1882     static void
1883     idle_send_connection( ClientData client_data, struct timeval* nowP )
1884     {
1885     connecttab* c;
1886    
1887     c = (connecttab*) client_data.p;
1888     c->idle_send_timer = (Timer*) 0;
1889     if ( c->conn_state != CNST_FREE )
1890     {
1891     syslog( LOG_INFO,
1892     "%.80s connection timed out sending",
1893     httpd_ntoa( &c->hc->client_addr ) );
1894     clear_connection( c, nowP );
1895     }
1896     }
1897    
1898    
1899     static void
1900     wakeup_connection( ClientData client_data, struct timeval* nowP )
1901     {
1902     connecttab* c;
1903    
1904     c = (connecttab*) client_data.p;
1905     c->wakeup_timer = (Timer*) 0;
1906     if ( c->conn_state == CNST_PAUSING )
1907     {
1908     c->conn_state = CNST_SENDING;
1909     fdwatch_add_fd( c->hc->conn_fd, c, FDW_WRITE );
1910     }
1911     }
1912    
1913     static void
1914     linger_clear_connection( ClientData client_data, struct timeval* nowP )
1915     {
1916     connecttab* c;
1917    
1918     c = (connecttab*) client_data.p;
1919     c->linger_timer = (Timer*) 0;
1920     really_clear_connection( c, nowP );
1921     }
1922    
1923    
1924     static void
1925     occasional( ClientData client_data, struct timeval* nowP )
1926     {
1927     mmc_cleanup( nowP );
1928     tmr_cleanup();
1929     }
1930    
1931    
1932     #ifdef STATS_TIME
1933     static void
1934     show_stats( ClientData client_data, struct timeval* nowP )
1935     {
1936     logstats( nowP );
1937     }
1938     #endif /* STATS_TIME */
1939    
1940    
1941     /* Generate debugging statistics syslog messages for all packages. */
1942     static void
1943     logstats( struct timeval* nowP )
1944     {
1945     struct timeval tv;
1946     time_t now;
1947     long up_secs, stats_secs;
1948    
1949     if ( nowP == (struct timeval*) 0 )
1950     {
1951     (void) gettimeofday( &tv, (struct timezone*) 0 );
1952     nowP = &tv;
1953     }
1954     now = nowP->tv_sec;
1955     up_secs = now - start_time;
1956     stats_secs = now - stats_time;
1957     if ( stats_secs == 0 )
1958     stats_secs = 1; /* fudge */
1959     stats_time = now;
1960     syslog( LOG_NOTICE,
1961     "up %ld seconds, stats for %ld seconds:", up_secs, stats_secs );
1962    
1963     thttpd_logstats( stats_secs );
1964     httpd_logstats( stats_secs );
1965     mmc_logstats( stats_secs );
1966     fdwatch_logstats( stats_secs );
1967     tmr_logstats( stats_secs );
1968     }
1969    
1970    
1971     /* Generate debugging statistics syslog message. */
1972     static void
1973     thttpd_logstats( long secs )
1974     {
1975     syslog( LOG_NOTICE,
1976     " thttpd - %ld connections (%g/sec), %d max simultaneous, %ld bytes (%g/sec), %d httpd_conns allocated",
1977     stats_connections, (float) stats_connections / secs,
1978     stats_simultaneous, stats_bytes, (float) stats_bytes / secs,
1979     httpd_conn_count );
1980     stats_connections = stats_bytes = 0L;
1981     stats_simultaneous = 0;
1982     }