📄 thttpd.c.bak
字号:
/* thttpd.c - tiny/turbo/throttling HTTP server**** Copyright (C)1995,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.**** Redistribution and use in source and binary forms, with or without** modification, are permitted provided that the following conditions** are met:** 1. Redistributions of source code must retain the above copyright** notice, this list of conditions and the following disclaimer.** 2. Redistributions in binary form must reproduce the above copyright** notice, this list of conditions and the following disclaimer in the** documentation and/or other materials provided with the distribution.**** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF** SUCH DAMAGE.*/#include "config.h"#include "version.h"#include <sys/param.h>#include <sys/types.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/uio.h>#include <errno.h>#ifdef HAVE_FCNTL_H#include <fcntl.h>#endif#include <pwd.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <syslog.h>#ifdef TIME_WITH_SYS_TIME#include <time.h>#endif#include <unistd.h>#include "libhttpd.h"#include "mmc.h"#include "timers.h"#include "match.h"#ifndef FD_SET#define NFDBITS 32#define FD_SETSIZE 32#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))#endif /* !FD_SET */#define EMBED#undef HAVE_MMAPstatic char* argv0;typedef struct { char* pattern; long limit; long rate; time_t last_avg; off_t bytes_since_avg; int num_sending; } throttletab;static throttletab* throttles;static int numthrottles, maxthrottles;typedef struct { int conn_state; httpd_conn hc; int tnums[MAXTHROTTLENUMS]; /* throttle indexes */ int numtnums; long limit; time_t started_at; Timer* idle_read_timer; Timer* idle_send_timer; Timer* wakeup_timer; Timer* linger_timer; long wouldblock_delay; off_t bytes; off_t bytes_sent; off_t bytes_to_send; } connecttab;static connecttab* connects;static int numconnects, maxconnects;static int recompute_fdsets;/* The connection states. */#define CNST_FREE 0#define CNST_READING 1#define CNST_SENDING 2#define CNST_PAUSING 3#define CNST_LINGERING 4static httpd_server* hs = (httpd_server*) 0;#ifdef STATS_TIMEint stats_connections, stats_simultaneous;#endif /* STATS_TIME *//* Forwards. */static void usage( void );static void read_throttlefile( char* throttlefile );static void shut_down( void );static int handle_newconnect( struct timeval* tvP );static void handle_read( connecttab* c, struct timeval* tvP );static void handle_send( connecttab* c, struct timeval* tvP );static void handle_linger( connecttab* c, struct timeval* tvP );static int check_throttles( connecttab* c );static void clear_throttles( connecttab* c, struct timeval* tvP );static void clear_connection( connecttab* c, struct timeval* tvP );static void really_clear_connection( connecttab* c, struct timeval* tvP );static void idle_read_connection( ClientData client_data, struct timeval* nowP );static void idle_send_connection( ClientData client_data, struct timeval* nowP );static void wakeup_connection( ClientData client_data, struct timeval* nowP );static void linger_clear_connection( ClientData client_data, struct timeval* nowP );static void occasional( ClientData client_data, struct timeval* nowP );#ifdef STATS_TIMEstatic void show_stats( ClientData client_data, struct timeval* nowP );#endif /* STATS_TIME */static voidhandle_kill( int sig ) { shut_down(); syslog( LOG_NOTICE, "exiting due to signal %d", sig ); closelog(); exit( 1 ); }intmain( int argc, char** argv ) { char* cp; int argn; int debug; int port; char* dir; int do_chroot;#ifndef EMBED struct passwd* pwd;#endif uid_t uid; gid_t gid; char* cgi_pattern; char* throttlefile; char* hostname; char* logfile; char* user; char cwd[MAXPATHLEN]; FILE* logfp; int nfiles; fd_set master_rfdset; fd_set master_wfdset; fd_set working_rfdset; fd_set working_wfdset; int num_ready; int cnum; connecttab* c; u_int addr; struct timeval tv; struct hostent* he; argv0 = argv[0]; cp = strrchr( argv0, '/' ); if ( cp != (char*) 0 ) ++cp; else cp = argv0; openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY ); (void) signal( SIGINT, handle_kill ); (void) signal( SIGTERM, handle_kill ); (void) signal( SIGPIPE, SIG_IGN ); /* get EPIPE instead */ debug = 0; port = DEFAULT_PORT;#ifdef EMBED dir = DEFAULT_DIR;#else dir = (char*) 0;#endif#ifdef ALWAYS_CHROOT do_chroot = 1;#else /* ALWAYS_CHROOT */ do_chroot = 0;#endif /* ALWAYS_CHROOT */#ifdef CGI_PATTERN cgi_pattern = CGI_PATTERN;#else /* CGI_PATTERN */ cgi_pattern = (char*) 0;#endif /* CGI_PATTERN */ throttlefile = (char*) 0; hostname = (char*) 0; logfile = (char*) 0; user = DEFAULT_USER; argn = 1; while ( argn < argc && argv[argn][0] == '-' ) { if ( strcmp( argv[argn], "-D" ) == 0 ) debug = 1; else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc ) { ++argn; port = atoi( argv[argn] ); } else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc ) { ++argn; dir = argv[argn]; } else if ( strcmp( argv[argn], "-r" ) == 0 ) do_chroot = 1; else if ( strcmp( argv[argn], "-nor" ) == 0 ) do_chroot = 0; else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc ) { ++argn; user = argv[argn]; } else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc ) { ++argn; cgi_pattern = argv[argn]; } else if ( strcmp( argv[argn], "-t" ) == 0 && argn + 1 < argc ) { ++argn; throttlefile = argv[argn]; } else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc ) { ++argn; hostname = argv[argn]; } else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc ) { ++argn; logfile = argv[argn]; } else usage(); ++argn; } if ( argn != argc ) usage(); /* Lookup hostname now in case we're going to chroot(). */ if ( hostname == (char*) 0 ) addr = htonl( INADDR_ANY ); else { addr = inet_addr( hostname ); if ( (int) addr == -1 ) { he = gethostbyname( hostname ); if ( he == (struct hostent*) 0 ) {#ifdef HAVE_HSTRERROR syslog( LOG_CRIT, "gethostbyname %.80s - %s", hostname, hstrerror( h_errno ) ); (void) fprintf( stderr, "gethostbyname %.80s - %s", hostname, hstrerror( h_errno ) );#else /* HAVE_HSTRERROR */ syslog( LOG_CRIT, "gethostbyname %.80s", hostname ); (void) fprintf( stderr, "gethostbyname %.80s\n", hostname );#endif /* HAVE_HSTRERROR */ exit( 1 ); } if ( he->h_addrtype != AF_INET || he->h_length != sizeof(addr) ) { syslog( LOG_CRIT, "%.80s - non-IP network address", hostname ); (void) fprintf( stderr, "%.80s - non-IP network address\n", hostname ); exit( 1 ); } (void) memcpy( &addr, he->h_addr, sizeof(addr) ); } } /* Check port number. */ if ( port <= 0 ) { syslog( LOG_CRIT, "illegal port number" ); (void) fprintf( stderr, "illegal port number\n" ); exit( 1 ); } /* Throttle file. */ numthrottles = 0; maxthrottles = 0; if ( throttlefile != (char*) 0 ) read_throttlefile( throttlefile ); /* Log file. */ if ( logfile != (char*) 0 ) { logfp = fopen( logfile, "a" ); if ( logfp == (FILE*) 0 ) { syslog( LOG_CRIT, "%.80s - %m", logfile ); perror( logfile ); exit( 1 ); } (void) fcntl( fileno( logfp ), F_SETFD, 1 ); } else logfp = (FILE*) 0;#ifdef EMBED uid = 0; gid = 0;#else /* Figure out uid/gid from user. */ pwd = getpwnam( user ); if ( pwd == (struct passwd*) 0 ) { syslog( LOG_CRIT, "unknown user - '%.80s'", user ); (void) fprintf( stderr, "unknown user - '%s'\n", user ); exit( 1 ); } uid = pwd->pw_uid; gid = pwd->pw_gid;#endif /* Switch directories if requested. */ if ( dir != (char*) 0 ) { if ( chdir( dir ) < 0 ) { syslog( LOG_CRIT, "chdir - %m" ); perror( "chdir" ); exit( 1 ); } }#ifdef USE_USER_DIR else if ( getuid() == 0 ) { /* No explicit directory was specified, we're root, and the ** USE_USER_DIR option is set - switch to the specified user's ** home dir. */ if ( chdir( pwd->pw_dir ) < 0 ) { syslog( LOG_CRIT, "chdir - %m" ); perror( "chdir" ); exit( 1 ); } }#endif /* USE_USER_DIR */ /* Get current directory. */ (void) getcwd( cwd, sizeof(cwd) ); if ( cwd[strlen( cwd ) - 1] != '/' ) (void) strcat( cwd, "/" ); /* Chroot if requested. */ if ( do_chroot ) { if ( chroot( cwd ) < 0 ) { syslog( LOG_CRIT, "chroot - %m" ); perror( "chroot" ); exit( 1 ); } (void) strcpy( cwd, "/" ); } /* Figure out how many file descriptors we can use. */ nfiles = MIN( httpd_get_nfiles(), FD_SETSIZE ); if ( ! debug ) { /* We're not going to use stdin stdout or stderr from here on, so close ** them to save file descriptors. */#ifndef EMBED (void) fclose( stdin ); (void) fclose( stdout ); (void) fclose( stderr ); /* Daemonize - make ourselves a subprocess. */ switch ( fork() ) { case 0: break; case -1: syslog( LOG_CRIT, "fork - %m" ); exit( 1 ); default: exit( 0 ); }#endif } /* Initialize the HTTP layer. Got to do this before giving up root, ** so that we can bind to a privileged port. */ hs = httpd_initialize( hostname, addr, port, cgi_pattern, cwd, logfp, do_chroot ); if ( hs == (httpd_server*) 0 ) exit( 1 ); /* Set up the occasional timer. */ (void) tmr_create( (struct timeval*) 0, occasional, (ClientData) 0, OCCASIONAL_TIME * 1000L, 1 );#ifdef STATS_TIME /* Set up the stats timer. */ (void) tmr_create( (struct timeval*) 0, show_stats, (ClientData) 0, STATS_TIME * 1000L, 1 ); stats_connections = stats_simultaneous = 0;#endif /* STATS_TIME */ /* If we're root, try to become someone else. */ if ( getuid() == 0 ) { if ( setgroups( 0, (const gid_t*) 0 ) < 0 ) { syslog( LOG_CRIT, "setgroups - %m" ); exit( 1 ); } if ( setgid( gid ) < 0 ) { syslog( LOG_CRIT, "setgid - %m" ); exit( 1 ); } if ( setuid( uid ) < 0 ) { syslog( LOG_CRIT, "setuid - %m" ); exit( 1 ); } /* Check for unnecessary security exposure. */ if ( ! do_chroot ) syslog( LOG_CRIT, "started as root without requesting chroot(), warning only" ); } /* Initialize our connections table. */ maxconnects = nfiles - SPARE_FDS; connects = NEW( connecttab, maxconnects ); if ( connects == (connecttab*) 0 ) { syslog( LOG_CRIT, "out of memory" ); exit( 1 ); } for ( cnum = 0; cnum < maxconnects; ++cnum ) { connects[cnum].conn_state = CNST_FREE; connects[cnum].hc.initialized = 0; } numconnects = 0; recompute_fdsets = 1; /* Main loop. */ (void) gettimeofday( &tv, (struct timezone*) 0 ); for (;;) { if ( recompute_fdsets ) { /* Set up the fdsets. */ FD_ZERO( &master_rfdset ); FD_ZERO( &master_wfdset ); FD_SET( hs->listen_fd, &master_rfdset ); if ( numconnects > 0 ) for ( cnum = 0; cnum < maxconnects; ++cnum ) switch ( connects[cnum].conn_state ) { case CNST_READING: case CNST_LINGERING: FD_SET( connects[cnum].hc.conn_fd, &master_rfdset ); break; case CNST_SENDING: FD_SET( connects[cnum].hc.conn_fd, &master_wfdset ); break; } recompute_fdsets = 0; } /* Do the select. */ working_rfdset = master_rfdset; working_wfdset = master_wfdset; num_ready = select( nfiles, &working_rfdset, &working_wfdset, (fd_set*) 0, tmr_timeout( &tv ) ); if ( num_ready < 0 ) { if ( errno == EINTR ) continue; /* try again */ syslog( LOG_ERR, "select - %m" ); exit( 1 ); } (void) gettimeofday( &tv, (struct timezone*) 0 ); if ( num_ready == 0 ) { /* No fd's are ready - run the timers. */ tmr_run( &tv ); continue; } /* Is it a new connection? */ if ( FD_ISSET( hs->listen_fd, &working_rfdset ) ) { --num_ready; if ( handle_newconnect( &tv ) ) /* Go around the loop and do another select, rather than ** dropping through and processing existing connections. ** New connections always get priority. */ continue; /* Only if handle_newconnect() fails in a particular way do ** we fall through. */ } /* Find the connections that need servicing. */ for ( cnum = 0; num_ready > 0 && cnum < maxconnects; ++cnum ) { c = &connects[cnum]; if ( c->conn_state == CNST_READING && FD_ISSET( c->hc.conn_fd, &working_rfdset ) ) { --num_ready; handle_read( c, &tv ); } else if ( c->conn_state == CNST_SENDING && FD_ISSET( c->hc.conn_fd, &working_wfdset ) ) { --num_ready; handle_send( c, &tv ); } else if ( c->conn_state == CNST_LINGERING && FD_ISSET( c->hc.conn_fd, &working_rfdset ) ) { --num_ready; handle_linger( c, &tv ); } } tmr_run( &tv ); } }static voidusage( void ) { (void) fprintf( stderr,"usage: %s [-p port] [-d dir] [-r|-nor] [-u user] [-c cgipat] [-t throttles] [-h host] [-l logfile]\n", argv0 ); exit( 1 ); }static voidread_throttlefile( char* throttlefile ) { FILE* fp; char buf[5000]; char* cp; int len; char pattern[5000]; long limit; struct timeval tv; fp = fopen( throttlefile, "r" ); if ( fp == (FILE*) 0 ) { syslog( LOG_CRIT, "%.80s - %m", throttlefile ); perror( throttlefile ); exit( 1 ); } (void) gettimeofday( &tv, (struct timezone*) 0 ); while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -