"Fossies" - the Fresh Open Source Software Archive

Member "mod_throttle-3.1.2/mod_throttle.c" (3 Dec 2000, 97711 Bytes) of package /linux/www/apache_httpd_modules/old/mod_throttle312.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * mod_throttle.c for Apache 1.3
    3  *
    4  * Copyright 1999, 2000 by Anthony Howe.  All rights reserved.
    5  *
    6  *
    7  * LICENSE
    8  * -------
    9  *
   10  * This source distribution is made freely available and there is no charge
   11  * for its use, provided you retain this notice, disclaimers, author's
   12  * copyright, and credits.
   13  *
   14  *
   15  * DISCLAIMER
   16  * ----------
   17  *
   18  * THIS SOFTWARE IS PROVIDE "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
   20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO WAY SHALL THE
   21  * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
   22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
   27  * OF THE POSSIBILITY OF SUCH DAMAGE.
   28  *
   29  *
   30  * CREDITS
   31  * -------
   32  *
   33  * Original design for mod_throttle/1.0 goes to Mark Lovell
   34  * <mlovell@bigrock.com>.
   35  *
   36  * Elements of the critical & shared memory code, as of version 2.2,
   37  * originally derived from the Apache Web Server source code.
   38  *
   39  * Thank You to Lu Vo <LVo@SUPERB.NET> for providing a Solaris POSIX
   40  * compliant machine to test on; to Travis Doherty <travis@terradelta.net>
   41  * for a FreeBSD machine for testing and several suggestions.
   42  */
   43 
   44 /***********************************************************************
   45  *** Pick one that best suits your system.
   46  ***********************************************************************/
   47 
   48 /*
   49  * Special features
   50  */
   51 #define THROTTLE_CLIENT_IP
   52 #define THROTTLE_REMOTE_USER
   53 
   54 /*
   55  * Choice of API to be used for serialization (semaphores & mutexes).
   56  */
   57 #undef USE_POSIX_SERIALIZATION
   58 #undef USE_FCNTL_SERIALIZATION
   59 #undef USE_FLOCK_SERIALIZATION
   60 #define USE_SYSTEM_V_SERIALIZATION
   61 
   62 /*
   63  * Choice of API to be used for shared memory.
   64  */
   65 #undef USE_POSIX_SHARED_MEMORY
   66 #define USE_SYSTEM_V_SHARED_MEMORY
   67 
   68 /*
   69  * Define if your system supports setpwent(), getpwent(), and endpwent().
   70  */
   71 #define HAVE_GETPWENT       1
   72 
   73 #ifndef HAVE_GETPWENT
   74 #ifndef DEFAULT_PASSWD_FILE
   75 #define DEFAULT_PASSWD_FILE "/etc/passwd"
   76 #endif
   77 #endif
   78 
   79 /*
   80  * throttle-status refresh time in seconds.
   81  */
   82 #ifndef DEFAULT_REFRESH
   83 #define DEFAULT_REFRESH     SECONDS_PER_MINUTE
   84 #endif
   85 
   86 /*
   87  * Policy period in seconds used with some policies.
   88  */
   89 #ifndef DEFAULT_PERIOD
   90 #define DEFAULT_PERIOD      SECONDS_PER_MONTH
   91 #endif
   92 
   93 /*
   94  * Default maximum tolerated delay in seconds for those
   95  * policies implementing throttle delays.
   96  */
   97 #ifndef DEFAULT_MAXDELAY
   98 #define DEFAULT_MAXDELAY    30
   99 #endif
  100 
  101 /*
  102  * throttle-status indicator levels expressed as an integer percentage.
  103  */
  104 #ifndef WARN_GREEN
  105 #define WARN_GREEN      50
  106 #endif
  107 
  108 #ifndef WARN_YELLOW
  109 #define WARN_YELLOW     75
  110 #endif
  111 
  112 #ifndef WARN_RED
  113 #define WARN_RED        90
  114 #endif
  115 
  116 #ifndef WARN_CRITICAL
  117 #define WARN_CRITICAL       100
  118 #endif
  119 
  120 /***********************************************************************
  121  *** No configuration below this point.
  122  ***********************************************************************/
  123 
  124 #define MODULE          "mod_throttle"
  125 #define AUTHOR          "achowe@snert.com"
  126 #define VERSION         "3.1.2"
  127 #define NDEBUG
  128 
  129 #define SECONDS_PER_MINUTE  60
  130 #define SECONDS_PER_HOUR    3600
  131 #define SECONDS_PER_DAY     86400L
  132 #define SECONDS_PER_WEEK    (SECONDS_PER_DAY*7)
  133 #define SECONDS_PER_MONTH   (SECONDS_PER_DAY*30)
  134 
  135 #include <sys/types.h>
  136 #include <errno.h>
  137 #include <limits.h>
  138 #include <pwd.h>
  139 #include <signal.h>
  140 #include <stdio.h>
  141 #include <stdlib.h>
  142 #include <string.h>
  143 #include <time.h>
  144 #include <unistd.h>
  145 
  146 #include <fcntl.h>
  147 #include <sys/mman.h>
  148 
  149 #include "httpd.h"
  150 #include "http_core.h"
  151 #include "http_config.h"
  152 #include "http_conf_globals.h"
  153 #include "http_log.h"
  154 #include "http_protocol.h"
  155 #include "http_request.h"
  156 #include "ap_config.h"
  157 
  158 #define UNSET           -1
  159 #define MERGE(p, c)     (c == UNSET ? p : c)
  160 #define MERGE_PTR(p, c)     ((void *) c == (void *) 0 ? p : c)
  161 
  162 #define PUSH(top,obj)       ((obj)->next = (top), (top) = (obj))
  163 #define POP(top,var)        ((var) = (top), (top) = (top)->next)
  164 
  165 module MODULE_VAR_EXPORT throttle_module;
  166 
  167 typedef struct config t_config;
  168 typedef struct throttle t_throttle;
  169 
  170 typedef void (*t_adjust)(request_rec *, t_config *);
  171 typedef int (*t_apply)(request_rec *, t_config *);
  172 typedef int (*t_percent)(t_config *);
  173 
  174 typedef struct policy {
  175     const char *name;
  176     t_apply apply;
  177     t_adjust adjust;
  178     t_percent percent;
  179 } t_policy;
  180 
  181 /*
  182  * Throttle state information to be stored in shared memory.
  183  */
  184 struct throttle {
  185     time_t start;           /* Period start time. */
  186     time_t last;            /* Time of last request. */
  187     unsigned int delay;     /* Last delay applied. */
  188     unsigned long volume;       /* Running total of KBytes sent. */
  189     unsigned long refused;      /* Total requests refused. */
  190     unsigned long requests;     /* Total requests receieved. */
  191 
  192     unsigned int active;        /* Concurrent requests. */
  193 };
  194 
  195 /*
  196  * Per user, server, virtual host, directory information.
  197  */
  198 struct config {
  199     uid_t uid;
  200     long limit;         /* Policy limit. */
  201     long period;            /* Policy period. */
  202     t_policy *policy;       /* Policy functions. */
  203     const char *name;       /* User, directory, host name. */
  204     server_rec *server;     /* Associated server. */
  205     struct config *next;        /* Link list of configurations. */
  206     struct throttle *track;     /* This throttle's shared data. */
  207 };
  208 
  209 static t_config dummy_config;
  210 static t_throttle dummy_throttle;
  211 
  212 static const char true[] = "true";
  213 static const char dummy[] = "Dummy";
  214 static const char stype[] = "Server";
  215 static const char dtype[] = "Directory";
  216 static const char text_html[] = "text/html";
  217 static const char text_plain[] = "text/plain";
  218 static const char file_lock[] = "logs/mod_throttle.lock";
  219 static const char file_runtime[] = "logs/mod_throttle.runtime";
  220 
  221 static const char x_is_subrequest[] = "x-is-subrequest";
  222 static const char request_handler[] = "request-handler";
  223 static const char is_file_request[] = "is-file-request";
  224 static const char is_throttle_handler[] = "is-throttle-handler";
  225 static const char request_not_counted[] = "request-not-counted";
  226 static const char volume_not_counted[] = "volume-not-counted";
  227 static const char request_content_type[] = "request-content-type";
  228 
  229 static const char view_status[] = "status";
  230 static const char throttle_me_str[] = "throttle-me";
  231 static const char throttle_info_str[] = "throttle-info";
  232 static const char throttle_status_str[] = "throttle-status";
  233 
  234 #define ALERT_LEVELS    (sizeof alert_names / sizeof *alert_names)
  235 static char *alert_names[] = { "green", "yellow", "red", "critical" };
  236 
  237 static server_rec *main_server;
  238 static unsigned int refresh = DEFAULT_REFRESH;
  239 static unsigned int max_delay = DEFAULT_MAXDELAY;
  240 static const char *content_type = text_html;
  241 static const char *runtime_file = file_runtime;
  242 static const char *lock_file = file_lock;
  243 static unsigned int alert[ALERT_LEVELS] = {
  244     WARN_GREEN, WARN_YELLOW, WARN_RED, WARN_CRITICAL
  245 };
  246 
  247 #if defined(THROTTLE_CLIENT_IP) || defined(THROTTLE_REMOTE_USER)
  248 typedef struct visitor t_visitor;
  249 typedef struct visitor_list t_visitors;
  250 #endif
  251 
  252 #ifdef THROTTLE_CLIENT_IP
  253 static t_config client_ip_config;
  254 static t_visitors *client_ip_pool;
  255 static long client_ip_size = 0;
  256 static const char view_client_ip[] = "client-ip";
  257 static const char throttle_client_ip_str[] = "throttle-client-ip";
  258 
  259 static t_visitor *get_client_ip(t_visitors *, struct in_addr);
  260 static void reset_client_ip(t_visitors *, const char *, time_t);
  261 #endif
  262 
  263 #ifdef THROTTLE_REMOTE_USER
  264 static t_config remote_user_config;
  265 static t_visitors *remote_user_pool;
  266 static long remote_user_size = 0;
  267 static const char view_remote_user[] = "remote-user";
  268 static const char throttle_remote_user_str[] = "throttle-remote-user";
  269 
  270 static t_visitor *get_remote_user(t_visitors *, char *);
  271 static void reset_remote_user(t_visitors *, const char *, time_t);
  272 #endif
  273 
  274 /*
  275  * Policy Table & Functions
  276  */
  277 static int policy_over_limit(request_rec *, t_config *, unsigned long);
  278 static int policy_none(request_rec *, t_config *);
  279 static int policy_concurrent(request_rec *, t_config *);
  280 static int policy_document(request_rec *, t_config *);
  281 static int policy_idle(request_rec *, t_config *);
  282 static int policy_original(request_rec *, t_config *);
  283 static int policy_random(request_rec *, t_config *);
  284 static int policy_request(request_rec *, t_config *);
  285 static int policy_speed(request_rec *, t_config *);
  286 static int policy_volume(request_rec *, t_config *);
  287 
  288 static void adjust_reset(request_rec *, t_config *);
  289 static void adjust_original(request_rec *, t_config *);
  290 static void adjust_speed(request_rec *, t_config *);
  291 
  292 static int percent_none(t_config *);
  293 static int percent_concurrent(t_config *);
  294 static int percent_document(t_config *);
  295 static int percent_idle(t_config *);
  296 static int percent_random(t_config *);
  297 static int percent_request(t_config *);
  298 static int percent_volume(t_config *);
  299 
  300 static t_policy policy_table[] = {
  301     { "None", policy_none, adjust_reset, percent_none },
  302     { "Concurrent", policy_concurrent, adjust_reset, percent_concurrent },
  303     { "Document", policy_document, adjust_reset, percent_document },
  304     { "Idle", policy_idle, adjust_reset, percent_idle },
  305     { "Original", policy_original, adjust_original, percent_volume },
  306     { "Random", policy_random, adjust_reset, percent_random },
  307     { "Request", policy_request, adjust_reset, percent_request },
  308     { "Speed", policy_speed, adjust_speed, percent_volume },
  309     { "Volume", policy_volume, adjust_reset, percent_volume },
  310     { (const char *) 0, (t_apply) 0, (t_adjust) 0, (t_percent) 0 }
  311 };
  312 
  313 /*
  314  *
  315  */
  316 static int cmd_preserve(struct pool *, const char *);
  317 static int cmd_restore(struct pool *, const char *);
  318 
  319 /*
  320  * Semaphore interface wrappers.
  321  */
  322 typedef struct critical t_critical;
  323 static void critical_child_init(server_rec *, pool *);
  324 static t_critical *critical_create(struct pool *);
  325 static int critical_acquire(t_critical *);
  326 static int critical_release(t_critical *);
  327 
  328 static t_critical *critical;
  329 
  330 /*
  331  * Shared memory pool interface.
  332  */
  333 typedef struct sm_pool t_sm_pool;
  334 static void *sm_pool_alloc(t_sm_pool *, size_t);
  335 static t_sm_pool *sm_pool_create(pool *, size_t);
  336 
  337 /***********************************************************************
  338  *** Support Routines
  339  ***********************************************************************/
  340 
  341 static int
  342 isfalse(char *s)
  343 {
  344     if (ap_strcasecmp_match(s, "disable") == 0)
  345         return 1;
  346     if (ap_strcasecmp_match(s, "false") == 0)
  347         return 1;
  348     if (ap_strcasecmp_match(s, "no") == 0)
  349         return 1;
  350     if (ap_strcasecmp_match(s, "reset") == 0)
  351         return 1;
  352     if (ap_strcasecmp_match(s, "bogus") == 0)
  353         return 1;
  354     if (ap_strcasecmp_match(s, "off") == 0)
  355         return 1;
  356     if (ap_strcasecmp_match(s, "0") == 0)
  357         return 1;
  358 
  359     return 0;
  360 }
  361 
  362 /*
  363  * Local version of ap_uname2id() that doesn't kill the server
  364  * when the user is not found.
  365  */
  366 static uid_t
  367 uname2id(const char *user)
  368 {
  369     if (user == (const char *) 0)
  370         return (uid_t) -1;
  371 
  372 #if defined(WIN32) || defined(NETWARE)
  373     return (uid_t) 1;
  374 #else
  375 {
  376     struct passwd *pw;
  377 
  378     if ((pw = getpwnam(user)) == (struct passwd *) 0)
  379         return (uid_t) -1;
  380 
  381     return pw->pw_uid;
  382 }
  383 #endif
  384 }
  385 
  386 static const char *
  387 byte_size(pool *p, long size)
  388 {
  389     const char *units;
  390 
  391     /* When the size value is not multiple of 1024, display
  392      * this number in Kbyte units only, since it might be
  393      * representing something else unrelated to Kbytes.
  394      */
  395     if (size % 1024 != 0) {
  396         units = "";
  397     } else if ((size /= 1024) % 1024 != 0) {
  398         units = "M";
  399     } else {
  400         size /= 1024;
  401         units = "G";
  402     }
  403 
  404     return ap_psprintf(p, "%ld%s", size, units);
  405 }
  406 
  407 static const char *
  408 time_period(pool *p, long period)
  409 {
  410     int units;
  411 
  412     if (period % 60 != 0 || period < 60) {
  413         units = 's';
  414     } else if ((period /= 60) < 60) {
  415         units = 'm';
  416     } else if ((period /= 60) < 24) {
  417         units = 'h';
  418     } else if ((period /= 24) < 7) {
  419         units = 'd';
  420     } else {
  421         period /= 7;
  422         units = 'w';
  423     }
  424 
  425     return ap_psprintf(p, "%ld%c", period, units);
  426 }
  427 
  428 static const char *
  429 elapsed_time(pool * p, unsigned long s)
  430 {
  431     int h, m;
  432     unsigned long d;
  433 
  434     d = s / SECONDS_PER_DAY;
  435     s %= SECONDS_PER_DAY;
  436 
  437     h = s / SECONDS_PER_HOUR;
  438     s %= SECONDS_PER_HOUR;
  439 
  440     m = s / SECONDS_PER_MINUTE;
  441     s %= SECONDS_PER_MINUTE;
  442 
  443     if (0 < d)
  444         return ap_psprintf(p, "%lu+%02d:%02d.%02d", d, h, m, (int) s);
  445     if (0 < h)
  446         return ap_psprintf(p, "%d:%02d.%02d", h, m, (int) s);
  447     if (0 < m)
  448         return ap_psprintf(p, "%d.%02d", m, (int) s);
  449 
  450     return ap_psprintf(p, "%d", (int) s);
  451 }
  452 
  453 /***********************************************************************
  454  *** Critical Section Routines
  455  ***********************************************************************/
  456 
  457 /*
  458  * Define one of the following:
  459  *
  460  *  USE_POSIX_SERIALIZATION
  461  *  USE_FCNTL_SERIALIZATION
  462  *  USE_FLOCK_SERIALIZATION
  463  *  USE_SYSTEM_V_SERIALIZATION
  464  */
  465 #if defined(USE_POSIX_SERIALIZATION)
  466 
  467 #include <semaphore.h>
  468 
  469 struct critical {
  470     sem_t sem;
  471 };
  472 
  473 static void
  474 critical_child_init(server_rec *s, pool *p)
  475 {
  476     /* Do nothing. */
  477 }
  478 
  479 static void
  480 critical_cleanup(void *obj)
  481 {
  482     errno = 0;
  483     do {
  484         (void) sem_destroy(& ((t_critical *) obj)->sem);
  485     } while (errno == EBUSY);
  486 }
  487 
  488 static t_critical *
  489 critical_create(struct pool *p)
  490 {
  491     t_critical *mp = (t_critical *) ap_palloc(p, sizeof *mp);
  492 
  493     if (sem_init(&mp->sem, 1, 1) < 0) {
  494         perror("critical_create(): cannot initialise unnamed semaphore");
  495         exit(APEXIT_INIT);
  496     }
  497 
  498     ap_register_cleanup(p, mp, critical_cleanup, ap_null_cleanup);
  499 
  500     return mp;
  501 }
  502 
  503 static int
  504 critical_acquire(t_critical *mp)
  505 {
  506     int rc;
  507 
  508     for (errno = 0; (rc = sem_wait(&mp->sem)) < 0; ) {
  509         if (errno != EINTR) {
  510             /*** We really should kill the server here. ***/
  511             perror("critical_acquire() failed");
  512             ap_start_shutdown();
  513             return -1;
  514         }
  515     }
  516 
  517     return rc;
  518 }
  519 
  520 static int
  521 critical_release(t_critical *mp)
  522 {
  523     int rc;
  524 
  525     for (errno = 0; (rc = sem_post(&mp->sem)) < 0; ) {
  526         if (errno != EINTR) {
  527             /*** We really should kill the server here. ***/
  528             perror("critical_release() failed");
  529             ap_start_shutdown();
  530             return -1;
  531         }
  532     }
  533 
  534     return rc;
  535 }
  536 
  537 #elif defined(USE_FCNTL_SERIALIZATION)
  538 
  539 struct critical {
  540     int fd;
  541     const char *filename;
  542     struct flock lock_it;
  543     struct flock unlock_it;
  544 };
  545 
  546 static void
  547 critical_child_init(server_rec *s, pool *p)
  548 {
  549     /* Do nothing. */
  550 }
  551 
  552 static t_critical *
  553 critical_create(struct pool *p)
  554 {
  555     t_critical *mp = (t_critical *) ap_palloc(p, sizeof *mp);
  556 
  557     /* Set up the lock file name. */
  558     mp->filename = ap_psprintf(
  559         p, "%s.%lu", ap_server_root_relative(p, (char *) lock_file),
  560         (unsigned long) getpid()
  561     );
  562 
  563     mp->lock_it.l_whence = SEEK_SET;    /* from current point */
  564     mp->lock_it.l_start = 0;        /* -"- */
  565     mp->lock_it.l_len = 0;          /* until end of file */
  566     mp->lock_it.l_type = F_WRLCK;       /* set exclusive/write lock */
  567     mp->lock_it.l_pid = 0;          /* pid not actually interesting */
  568     mp->unlock_it.l_whence = SEEK_SET;  /* from current point */
  569     mp->unlock_it.l_start = 0;      /* -"- */
  570     mp->unlock_it.l_len = 0;        /* until end of file */
  571     mp->unlock_it.l_type = F_UNLCK;     /* set exclusive/write lock */
  572     mp->unlock_it.l_pid = 0;        /* pid not actually interesting */
  573 
  574     mp->fd = ap_popenf(p, mp->filename, O_CREAT|O_WRONLY|O_EXCL, 0600);
  575 
  576     if (mp->fd < 0) {
  577         perror("critical_create(): cannot create lock file");
  578         exit(APEXIT_INIT);
  579     }
  580 
  581     (void) unlink(mp->filename);
  582 
  583     return mp;
  584 }
  585 
  586 static int
  587 critical_acquire(t_critical *mp)
  588 {
  589     int rc;
  590 
  591     for (errno = 0; (rc = fcntl(mp->fd, F_SETLKW, &mp->lock_it)) < 0; ) {
  592         if (errno != EINTR) {
  593             /*** We really should kill the server here. ***/
  594             perror("critical_acquire() failed");
  595             ap_start_shutdown();
  596             return -1;
  597         }
  598     }
  599 
  600     return rc;
  601 }
  602 
  603 static int
  604 critical_release(t_critical *mp)
  605 {
  606     int rc;
  607 
  608     for (errno = 0; (rc = fcntl(mp->fd, F_SETLKW, &mp->unlock_it)) < 0; ) {
  609         if (errno != EINTR) {
  610             /*** We really should kill the server here. ***/
  611             perror("critical_release() failed");
  612             ap_start_shutdown();
  613             return -1;
  614         }
  615     }
  616 
  617     return rc;
  618 }
  619 
  620 #elif defined(USE_FLOCK_SERIALIZATION)
  621 
  622 struct critical {
  623     int fd;
  624     const char *filename;
  625 };
  626 
  627 static void
  628 critical_child_init(server_rec *s, pool *p)
  629 {
  630     /* Attach child process to inherited global critical. */
  631     critical->fd = ap_popenf(p, critical->filename, O_WRONLY, 0600);
  632 
  633     if (critical->fd < 0) {
  634         ap_log_error(
  635             APLOG_MARK, APLOG_EMERG, s,
  636             "Child cannot open lock file: %s",
  637             critical->filename
  638         );
  639 
  640         ap_start_shutdown();
  641     }
  642 }
  643 
  644 static void
  645 critical_cleanup(void *obj)
  646 {
  647     (void) unlink(((t_critical *) obj)->filename);
  648 }
  649 
  650 static t_critical *
  651 critical_create(struct pool *p)
  652 {
  653     t_critical *mp = (t_critical *) ap_palloc(p, sizeof *mp);
  654 
  655     /* Set up the lock file name. */
  656     mp->filename = ap_psprintf(
  657         p, "%s.%lu", ap_server_root_relative(p, (char *) lock_file),
  658         (unsigned long) getpid()
  659     );
  660     (void) unlink(mp->filename);
  661 
  662     /* Apache's lock file code creates the lock file owned by root
  663      * with 0600 permissions and each child opens it as root before
  664      * the change of uid & gid.
  665      *
  666      * We don't have this luxury, since the child_init() code executes
  667      * after the change in the child server's uid and gid. So chown()
  668      * the lock file in advance to belong to the child server uid.
  669      */
  670     mp->fd = ap_popenf(p, mp->filename, O_CREAT|O_WRONLY|O_EXCL, 0600);
  671 
  672     if (mp->fd < 0) {
  673         perror("critical_create(): cannot create lock file");
  674         exit(APEXIT_INIT);
  675     }
  676 
  677     if (chown(mp->filename, ap_user_id, ap_group_id) < 0) {
  678         perror("critical_create(): cannot set lock file ownership");
  679         exit(APEXIT_INIT);
  680     }
  681 
  682     ap_register_cleanup(p, mp, critical_cleanup, ap_null_cleanup);
  683 
  684     return mp;
  685 }
  686 
  687 static int
  688 critical_acquire(t_critical *mp)
  689 {
  690     int rc;
  691 
  692     for (errno = 0; (rc = flock(mp->fd, LOCK_EX)) < 0; ) {
  693         if (errno != EINTR) {
  694             /*** We really should kill the server here. ***/
  695             perror("critical_acquire() failed");
  696             ap_start_shutdown();
  697             return -1;
  698         }
  699     }
  700 
  701     return rc;
  702 }
  703 
  704 static int
  705 critical_release(t_critical *mp)
  706 {
  707     if (flock(mp->fd, LOCK_UN) < 0) {
  708         /*** We really should kill the server here. ***/
  709         perror("critical_release() failed");
  710         ap_start_shutdown();
  711         return -1;
  712     }
  713 
  714     return 0;
  715 }
  716 
  717 #elif defined(USE_SYSTEM_V_SERIALIZATION)
  718 
  719 #include <sys/ipc.h>
  720 #include <sys/sem.h>
  721 
  722 #if (defined(__GNU_LIBRARY__) && (!defined(_SEM_SEMUN_UNDEFINED))) || defined(__FreeBSD__) || defined(__NetBSD__)
  723 /* union semun is defined by including <sys/sem.h> */
  724 #else
  725 /* X/OPEN says we have to define it ourselves (twits). */
  726 union semun {
  727     int val;            /* value for SETVAL */
  728     struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
  729     unsigned short int *array;  /* array for GETALL, SETALL */
  730     struct seminfo *__buf;      /* buffer for IPC_INFO */
  731 };
  732 #endif
  733 
  734 struct critical {
  735     int id;
  736     struct sembuf on;
  737     struct sembuf off;
  738 };
  739 
  740 static void
  741 critical_child_init(server_rec *s, pool *p)
  742 {
  743     /* Do nothing. */
  744 }
  745 
  746 static void
  747 critical_cleanup(void *obj)
  748 {
  749     union semun ick;
  750     t_critical *mp = (t_critical *) obj;
  751 
  752     if (0 <= mp->id) {
  753         ick.val = 0;
  754         (void) semctl(mp->id, 0, IPC_RMID, ick);
  755     }
  756 }
  757 
  758 static t_critical *
  759 critical_create(struct pool *p)
  760 {
  761     union semun ick;
  762     t_critical *mp = (t_critical *) ap_palloc(p, sizeof *mp);
  763 
  764     if ((mp->id = semget(IPC_PRIVATE, 1, IPC_CREAT|0600)) < 0) {
  765         perror("critical_create(): semget() failed");
  766         exit(APEXIT_INIT);
  767     }
  768 
  769     ick.val = 1;
  770     if (semctl(mp->id, 0, SETVAL, ick) < 0) {
  771         perror("critical_create(): semctl(SETVAL) failed");
  772         exit(APEXIT_INIT);
  773     }
  774 
  775     if (getuid() == 0) {
  776         struct semid_ds buf;
  777 
  778         buf.sem_perm.uid = ap_user_id;
  779         buf.sem_perm.gid = ap_group_id;
  780         buf.sem_perm.mode = 0600;
  781         ick.buf = &buf;
  782 
  783         if (semctl(mp->id, 0, IPC_SET, ick) < 0) {
  784             perror("critical_create(): semctl(IPC_SET) failed");
  785             exit(APEXIT_INIT);
  786         }
  787     }
  788 
  789     ap_register_cleanup(p, (void *) mp, critical_cleanup, ap_null_cleanup);
  790 
  791     mp->on.sem_num = 0;
  792     mp->on.sem_op = -1;
  793     mp->on.sem_flg = SEM_UNDO;
  794     mp->off.sem_num = 0;
  795     mp->off.sem_op = 1;
  796     mp->off.sem_flg = SEM_UNDO;
  797 
  798     ap_log_error(
  799         APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, main_server,
  800         "allocated semaphore #%d", mp->id
  801     );
  802 
  803     return mp;
  804 }
  805 
  806 static int
  807 critical_acquire(t_critical *mp)
  808 {
  809     for (errno = 0; semop(mp->id, &mp->on, 1) < 0; ) {
  810         if (errno != EINTR) {
  811             /*** We really should kill the server here. ***/
  812             perror("critical_acquire() failed");
  813 
  814             /* Neither of these calls appear to shutdown the
  815              * server and its children; exit(APEXIT_CHILDFATAL),
  816              * appears to kill only the parent process.
  817              */
  818             ap_start_shutdown();
  819             return -1;
  820         }
  821     }
  822 
  823     return 0;
  824 }
  825 
  826 static int
  827 critical_release(t_critical *mp)
  828 {
  829     for (errno = 0; semop(mp->id, &mp->off, 1) < 0; ) {
  830         if (errno != EINTR) {
  831             /*** We really should kill the server here. ***/
  832             perror("critical_release() failed");
  833 
  834             /* Neither of these calls appear to shutdown the
  835              * server and its children; exit(APEXIT_CHILDFATAL),
  836              * appears to kill only the parent process.
  837              */
  838             ap_start_shutdown();
  839             return -1;
  840         }
  841     }
  842 
  843     return 0;
  844 }
  845 
  846 #else
  847 /*
  848  * Default, no serialization!
  849  */
  850 typedef int t_critical;
  851 
  852 t_critical *critical;
  853 
  854 static void
  855 critical_child_init(server_rec *s, pool *p)
  856 {
  857     /* Do nothing. */
  858 }
  859 
  860 static t_critical *
  861 critical_create(struct pool *p)
  862 {
  863     /* No critical defined. */
  864     return (t_critical *) -1;
  865 }
  866 
  867 static int
  868 critical_acquire(t_critical *mp)
  869 {
  870     return 0;
  871 }
  872 
  873 static int
  874 critical_release(t_critical *mp)
  875 {
  876     return 0;
  877 }
  878 
  879 #endif
  880 
  881 /***********************************************************************
  882  *** Shared Memory Routines
  883  ***********************************************************************/
  884 
  885 struct sm_pool {
  886     void *pool;
  887     size_t max;
  888     size_t length;
  889     t_critical critical;
  890 };
  891 
  892 static void *
  893 sm_pool_alloc(t_sm_pool *sm, size_t size)
  894 {
  895     void *mem;
  896 
  897     if (sm->max < sm->length + size)
  898         return (void *) 0;
  899 
  900     mem = sm->pool + sm->length;
  901     sm->length += size;
  902 
  903     return mem;
  904 }
  905 
  906 /*
  907  * Define one of the following:
  908  *
  909  *  USE_POSIX_SHARED_MEMORY
  910  *  USE_SYSTEM_V_SHARED_MEMORY
  911  */
  912 #if defined(USE_POSIX_SHARED_MEMORY)
  913 
  914 #ifdef USE_SHM_OPEN
  915 static void
  916 sm_pool_cleanup(void *data)
  917 {
  918     shm_unlink("/" MODULE);
  919 }
  920 #endif
  921 
  922 static t_sm_pool *
  923 sm_pool_create(pool *p, size_t size)
  924 {
  925     int fd;
  926     t_sm_pool *sm = (t_sm_pool *) ap_pcalloc(p, sizeof *sm);
  927 
  928     ap_block_alarms();
  929     errno = 0;
  930 
  931 #ifdef USE_SHM_OPEN
  932 /* For some reason, using shm_open() and mmap() on Solaris 5.7,
  933  * causes a SIGBUS when the shared memory address returned from
  934  * mmap() is read or written, despite the fact that we don't get
  935  * any errors from shm_open() and mmap().
  936  */
  937     if ((fd = shm_open("/" MODULE, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
  938         ap_log_error(
  939             APLOG_MARK, APLOG_EMERG, main_server,
  940             "Failed to create a shared memory object."
  941         );
  942         exit(APEXIT_INIT);
  943     }
  944 #else
  945 /* Since POSIX mmap() works with file objects and most systems
  946  * will probably come with a /dev/zero, we can use this instead,
  947  * which works happily on Solaris 5.7.
  948  */
  949     if ((fd = open("/dev/zero", O_RDWR, 0600)) < 0) {
  950         ap_log_error(
  951             APLOG_MARK, APLOG_EMERG, main_server,
  952             "Failed to open shared opbject."
  953         );
  954         exit(APEXIT_INIT);
  955     }
  956 #endif
  957 
  958     sm->pool = mmap((void *) 0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t) 0);
  959     if (sm->pool == MAP_FAILED) {
  960         shm_unlink("/" MODULE);
  961         ap_log_error(
  962             APLOG_MARK, APLOG_EMERG, main_server,
  963             "Failed to map the shared object."
  964         );
  965         exit(APEXIT_INIT);
  966     }
  967 
  968     sm->max = size;
  969     sm->length = 0;
  970     (void) close(fd);
  971     (void) memset(sm->pool, 0, size);
  972 
  973 #ifdef USE_SHM_OPEN
  974     ap_register_cleanup(p, NULL, sm_pool_cleanup, ap_null_cleanup);
  975 #endif
  976 
  977     ap_unblock_alarms();
  978 
  979     return sm;
  980 }
  981 
  982 #elif defined(USE_SYSTEM_V_SHARED_MEMORY)
  983 
  984 #include <sys/ipc.h>
  985 #include <sys/shm.h>
  986 
  987 static void
  988 sm_pool_cleanup(void *sm)
  989 {
  990     (void) shmdt(sm);
  991 }
  992 
  993 static t_sm_pool *
  994 sm_pool_create(pool *p, size_t size)
  995 {
  996     int id;
  997 #ifdef MOVEBREAK
  998     char *obrk;
  999 #endif
 1000     struct shmid_ds shmbuf;
 1001     t_sm_pool *sm = (t_sm_pool *) ap_pcalloc(p, sizeof *sm);
 1002 
 1003     if ((id = shmget(IPC_PRIVATE, size, IPC_CREAT|SHM_R|SHM_W)) < 0) {
 1004         ap_log_error(
 1005             APLOG_MARK, APLOG_EMERG, main_server,
 1006             "Failed to allocated shared memory."
 1007         );
 1008         exit(APEXIT_INIT);
 1009     }
 1010 
 1011     ap_log_error(
 1012         APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, main_server,
 1013         "Created shared memory segment #%d", id
 1014     );
 1015 
 1016 #ifdef MOVEBREAK
 1017     /*
 1018      * Some SysV systems place the shared segment WAY too close
 1019      * to the dynamic memory break point (sbrk(0)). This severely
 1020      * limits the use of malloc/sbrk in the program since sbrk will
 1021      * refuse to move past that point.
 1022      *
 1023      * To get around this, we move the break point "way up there",
 1024      * attach the segment and then move break back down. Ugly
 1025      */
 1026     if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
 1027         ap_log_error(
 1028             APLOG_MARK, APLOG_ERR, main_server,
 1029             "sbrk() could not move break"
 1030         );
 1031     }
 1032 #endif
 1033     sm->max = size;
 1034     sm->length = 0;
 1035 
 1036     if ((sm->pool = shmat(id, (char *) 0, 0)) == (void *) -1) {
 1037         ap_log_error(
 1038             APLOG_MARK, APLOG_EMERG, main_server,
 1039             "shmat() could not attach segment #%d", id
 1040         );
 1041     } else if (shmctl(id, IPC_STAT, &shmbuf) < 0) {
 1042         ap_log_error(
 1043             APLOG_MARK, APLOG_EMERG, main_server,
 1044             "shmctl() could not stat segment #%d", id
 1045         );
 1046     } else {
 1047         shmbuf.shm_perm.uid = ap_user_id;
 1048         shmbuf.shm_perm.gid = ap_group_id;
 1049 
 1050         if (shmctl(id, IPC_SET, &shmbuf) != 0) {
 1051             ap_log_error(
 1052                 APLOG_MARK, APLOG_ERR, main_server,
 1053                 "shmctl() could not set segment #%d", id
 1054             );
 1055         }
 1056     }
 1057 
 1058     /* We must avoid leaving segments in the kernel's (small) tables. */
 1059     if (shmctl(id, IPC_RMID, NULL) != 0) {
 1060         ap_log_error(
 1061             APLOG_MARK, APLOG_WARNING, main_server,
 1062             "shmctl() could not remove shared memory segment #%d",
 1063             id
 1064         );
 1065     }
 1066 
 1067     if (sm->pool == (void *) -1)
 1068         exit(APEXIT_INIT);
 1069 
 1070     (void) memset(sm->pool, 0, size);
 1071 
 1072 #ifdef MOVEBREAK
 1073     if (obrk == (char *) -1)
 1074         return sm;          /* nothing else to do */
 1075 
 1076     if (sbrk(-(MOVEBREAK)) == (char *) -1) {
 1077         ap_log_error(
 1078             APLOG_MARK, APLOG_ERR, main_server,
 1079             "sbrk() could not move break back"
 1080         );
 1081     }
 1082 #endif
 1083 
 1084     /* For restarts, detach the shared memory segment, then we
 1085      * safely reallocate a new segment during configuration phase.
 1086      */
 1087     ap_register_cleanup(p, sm->pool, sm_pool_cleanup, ap_null_cleanup);
 1088 
 1089     return sm;
 1090 }
 1091 
 1092 #else
 1093 /*
 1094  * Default, assume threads where all memory is shared!
 1095  */
 1096 static t_sm_pool *
 1097 sm_pool_create(pool *p, size_t size)
 1098 {
 1099     t_sm_pool *sm = (t_sm_pool *) ap_pcalloc(p, sizeof *sm);
 1100 
 1101     sm->pool = ap_pcalloc(p, size);
 1102     sm->max = size;
 1103     sm->length = 0;
 1104 
 1105     return sm;
 1106 }
 1107 #endif
 1108 
 1109 #if defined(THROTTLE_CLIENT_IP) || defined(THROTTLE_REMOTE_USER)
 1110 /***********************************************************************
 1111  *** Visitor Pool Routines
 1112  ***********************************************************************/
 1113 
 1114 #include <sys/socket.h>
 1115 #include <netdb.h>
 1116 #include <netinet/in.h>
 1117 #include <arpa/inet.h>
 1118 
 1119 struct visitor {
 1120     time_t start;           /* Period start time. */
 1121     time_t last;            /* Time of last request. */
 1122     unsigned int delay;     /* Last delay applied. */
 1123     unsigned long volume;       /* Running total of KBytes sent. */
 1124     unsigned long refused;      /* Total requests refused. */
 1125     unsigned long requests;     /* Total requests receieved. */
 1126 
 1127     union {
 1128         char user[16];      /* Authenticated remote user. */
 1129         struct in_addr ip;  /* IP of last connection. */
 1130 
 1131     } remote;
 1132 
 1133     struct visitor *next;
 1134 };
 1135 
 1136 struct visitor_list {
 1137     int used;
 1138     struct visitor *head;
 1139     struct visitor base[1];
 1140 };
 1141 
 1142 static t_visitor dummy_visitor;
 1143 
 1144 #ifdef THROTTLE_CLIENT_IP
 1145 /*
 1146  * NOTE this is a critical section.
 1147  */
 1148 static t_visitor *
 1149 get_client_ip(t_visitors *vp, struct in_addr ip)
 1150 {
 1151     t_visitor *v, **prev = &vp->head;
 1152 
 1153     if (vp->head == (t_visitor *) 0)
 1154         return &dummy_visitor;
 1155 
 1156     for (v = vp->head; v->next != (t_visitor *) 0; v = v->next) {
 1157         if (v->remote.ip.s_addr == 0 || v->remote.ip.s_addr == ip.s_addr)
 1158             break;
 1159         prev = &v->next;
 1160     }
 1161 
 1162     /* Move the entry to the front of the list. */
 1163     *prev = v->next;
 1164     PUSH(vp->head, v);
 1165 
 1166     /* The last entry in the list might match. */
 1167     if (v->remote.ip.s_addr != ip.s_addr) {
 1168         if (v->remote.ip.s_addr == 0) vp->used++;
 1169         v->volume = v->refused = v->requests = v->delay = 0;
 1170         v->start = v->last = time((time_t *) 0) - 1;
 1171         v->remote.ip = ip;
 1172     }
 1173 
 1174     return v;
 1175 }
 1176 
 1177 static void
 1178 reset_client_ip(t_visitors *vp, const char *args, time_t when)
 1179 {
 1180     int all;
 1181     t_visitor *v;
 1182     struct in_addr ip;
 1183 
 1184     ip.s_addr = inet_addr(args);
 1185     all = (args[0] == '*' && args[1] == '\0');
 1186 
 1187     (void) critical_acquire(critical);
 1188 
 1189     for (v = vp->head; v != (t_visitor *) 0; v = v->next) {
 1190         if (all || v->remote.ip.s_addr == ip.s_addr) {
 1191             v->volume = v->refused = v->requests = v->delay = 0;
 1192             v->start = v->last = when - 1;
 1193             if (!all) break;
 1194         }
 1195     }
 1196 
 1197     (void) critical_release(critical);
 1198 }
 1199 #endif
 1200 
 1201 #ifdef THROTTLE_REMOTE_USER
 1202 /*
 1203  * NOTE this is a critical section.
 1204  */
 1205 static t_visitor *
 1206 get_remote_user(t_visitors *vp, char *user)
 1207 {
 1208     t_visitor *v, **prev = &vp->head;
 1209 
 1210     if (vp->head == (t_visitor *) 0 || user == (char *) 0)
 1211         return (t_visitor *) 0;
 1212 
 1213     for (v = vp->head; v->next != (t_visitor *) 0; v = v->next) {
 1214         if (*v->remote.user == '\0' || ap_strcasecmp_match(v->remote.user, user) == 0)
 1215             break;
 1216         prev = &v->next;
 1217     }
 1218 
 1219     /* Move the entry to the front of the list. */
 1220     *prev = v->next;
 1221     PUSH(vp->head, v);
 1222 
 1223     /* The last entry in the list might match. */
 1224     if (ap_strcasecmp_match(v->remote.user, user) != 0) {
 1225         if (*v->remote.user == '\0') vp->used++;
 1226         (void) ap_cpystrn(v->remote.user, user, sizeof v->remote.user);
 1227         v->volume = v->refused = v->requests = v->delay = 0;
 1228         v->start = v->last = time((time_t *) 0) - 1;
 1229     }
 1230 
 1231     (void) critical_release(critical);
 1232 
 1233     return v;
 1234 }
 1235 
 1236 static void
 1237 reset_remote_user(t_visitors *vp, const char *args, time_t when)
 1238 {
 1239     int all;
 1240     t_visitor *v;
 1241 
 1242     all = strchr(args, '*') != (char *) 0;
 1243 
 1244     (void) critical_acquire(critical);
 1245 
 1246     for (v = vp->head; v != (t_visitor *) 0; v = v->next) {
 1247         if (ap_strcasecmp_match(v->remote.user, args) == 0) {
 1248             v->volume = v->refused = v->requests = v->delay = 0;
 1249             v->start = v->last = when - 1;
 1250             if (!all) break;
 1251         }
 1252     }
 1253 
 1254     (void) critical_release(critical);
 1255 }
 1256 #endif
 1257 
 1258 #endif /* defined(THROTTLE_CLIENT_IP) || defined(THROTTLE_REMOTE_USER) */
 1259 
 1260 /***********************************************************************
 1261  ***  Create, Merge, and Initialise Routines
 1262  ***********************************************************************/
 1263 
 1264 /* Link list/stack of the assorted things that we throttle. */
 1265 static t_config *config_stack = (t_config *) 0;
 1266 static t_config *stack_top = (t_config *) 0;
 1267 static unsigned int config_count = 0;
 1268 static unsigned int stack_count = 0;
 1269 
 1270 static void *
 1271 create_dir_config(struct pool *p, char *dir)
 1272 {
 1273     t_config *config = (t_config *) ap_pcalloc(p, sizeof *config);
 1274 
 1275     config->name = ap_pstrdup(p, dir);
 1276     config->uid = (uid_t) UNSET;
 1277     config->period = UNSET;
 1278     config->limit = UNSET;
 1279 
 1280     /* Push this new entry onto the stack. */
 1281     config->next = config_stack;
 1282     config_stack = config;
 1283 
 1284     ++config_count;
 1285 
 1286 #ifndef NDEBUG
 1287     fprintf(
 1288         stderr, "create_dir_config(%s) %lx\n",
 1289         dir == (char *) 0 ? "(null)" : dir, config
 1290     );
 1291 #endif
 1292     return (void *) config;
 1293 }
 1294 
 1295 static void *
 1296 merge_dir_config(struct pool *p, void *dad, void *kid)
 1297 {
 1298     t_config *child = (t_config *) kid;
 1299     t_config *parent = (t_config *) dad;
 1300 
 1301     if (child->name == (const char *) 0)
 1302         child->name = child->server->server_hostname;
 1303 
 1304     /* If a value in the child is unset, then inherit the setting of
 1305      * the parent, which may or may not be the default, otherwise use
 1306      * the child's value.
 1307      */
 1308     child->uid = MERGE(parent->uid, child->uid);
 1309     child->limit = MERGE(parent->limit, child->limit);
 1310     child->period = MERGE(parent->period, child->period);
 1311     child->policy = MERGE_PTR(parent->policy, child->policy);
 1312 
 1313     /* We don't bother merging the server & info fields, since every
 1314      * entry will have its own. See init_module().
 1315      */
 1316 
 1317 #ifndef NDEBUG
 1318     ap_log_error(
 1319         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, main_server,
 1320         "Merge parent=%s, child=%s, policy=%s\n",
 1321         parent->name, child->name, child->policy->name
 1322     );
 1323 #endif
 1324 
 1325     return (void *) child;
 1326 }
 1327 
 1328 static void
 1329 cleanup_module(void *data)
 1330 {
 1331     (void) cmd_preserve((struct pool *) data, (const char *) 0);
 1332 }
 1333 
 1334 static void
 1335 init_module(server_rec *server, struct pool *p)
 1336 {
 1337     int i;
 1338     size_t size;
 1339     void *sm_pool;
 1340     t_config *config = (t_config *) ap_get_module_config(
 1341         server->lookup_defaults, &throttle_module
 1342     );
 1343 
 1344 #ifndef NDEBUG
 1345     ap_log_error(
 1346         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, server,
 1347         "init_module server=%s; config_count=%u",
 1348         server->server_hostname, config_count
 1349     );
 1350 #endif
 1351 
 1352     /* For error logging. */
 1353     main_server = server;
 1354 
 1355     /* Dummy configuration used when no configuration specified. */
 1356     dummy_config.policy = &policy_table[0];
 1357     dummy_config.track = &dummy_throttle;
 1358     dummy_config.period = LONG_MAX;
 1359     dummy_config.limit = LONG_MAX;
 1360     dummy_config.server = server;
 1361     dummy_config.name = dummy;
 1362 
 1363     /* Our initial server configuration is unset, so at this point
 1364      * we set our defaults. These will be overridden by directives
 1365      * below. It will be this parent server data that will be later
 1366      * used to merge with or inherit from.
 1367      */
 1368     config->server = server;
 1369     config->name = server->server_hostname;
 1370 
 1371     if (config->policy == (t_policy *) 0) {
 1372         config->policy = &policy_table[0];
 1373         if (config->period == UNSET) {
 1374             /* When unset, default to 30 days for policy none,
 1375              * which is a reasonable period for the accumulation
 1376              * of data.
 1377              */
 1378             config->period = SECONDS_PER_MONTH;
 1379         }
 1380     }
 1381 
 1382     if (config->period == UNSET)
 1383         config->period = DEFAULT_PERIOD;
 1384 
 1385     if (config->limit == UNSET)
 1386         config->limit = 0;
 1387 
 1388     /* The first pass of the configuration which occurs prior to
 1389      * init_module() being called, will generate our count.
 1390      */
 1391     size = config_count * sizeof(t_throttle);
 1392 
 1393 #ifdef THROTTLE_CLIENT_IP
 1394     size += sizeof(t_visitors) + client_ip_size * sizeof(t_visitor);
 1395 #endif
 1396 #ifdef THROTTLE_REMOTE_USER
 1397     size += sizeof(t_visitors) + remote_user_size * sizeof(t_visitor);
 1398 #endif
 1399 
 1400     sm_pool = sm_pool_create(p, size);
 1401 
 1402     /* Allocate server, virtual host, directory, location, and
 1403      * local user throttles.
 1404      */
 1405     for (config = config_stack; config != (t_config *) 0; config = config->next) {
 1406         config->track = (t_throttle *) sm_pool_alloc(sm_pool, sizeof (t_throttle));
 1407         if (config->track != (t_throttle *) 0) {
 1408             config->track->start =
 1409             config->track->last = time((time_t *) 0)-1;
 1410         }
 1411     }
 1412 
 1413     /* Remember the stack head for in a runtime variable, and reset
 1414      * the configuration stack for the next restart.
 1415      */
 1416     stack_top = config_stack;
 1417     config_stack = (t_config *) 0;
 1418 
 1419     stack_count = config_count;
 1420     config_count = 0;
 1421 
 1422 #ifdef THROTTLE_CLIENT_IP
 1423 {
 1424     t_visitor *v;
 1425     long n = client_ip_size;
 1426 
 1427     client_ip_pool = sm_pool_alloc(
 1428         sm_pool, sizeof(t_visitors) + n * sizeof(t_visitor)
 1429     );
 1430 
 1431     /* Link up the list of client-ip entries. */
 1432     for (v = client_ip_pool->base; 0 <= n; --n, ++v) {
 1433         PUSH(client_ip_pool->head, v);
 1434     }
 1435 
 1436     client_ip_config.server = server;
 1437 
 1438     /* We use this field to identify regular throttle configurations
 1439      * vs. the global configuration used to throttle client-ip.
 1440      */
 1441     client_ip_config.name = throttle_client_ip_str;
 1442     client_ip_config.uid = UNSET;
 1443 
 1444     if (client_ip_config.policy == (t_policy *) 0)
 1445         client_ip_config.policy = &policy_table[0];
 1446 
 1447     if (client_ip_config.period <= 0)
 1448         client_ip_config.period = SECONDS_PER_HOUR;
 1449 }
 1450 #endif
 1451 #ifdef THROTTLE_REMOTE_USER
 1452 {
 1453     t_visitor *v;
 1454     long n = remote_user_size;
 1455 
 1456     remote_user_pool = sm_pool_alloc(
 1457         sm_pool, sizeof(t_visitors) + n * sizeof(t_visitor)
 1458     );
 1459 
 1460     /* Link up the list of remote-user entries. */
 1461     for (v = remote_user_pool->base; 0 <= n; --n, ++v) {
 1462         PUSH(remote_user_pool->head, v);
 1463     }
 1464 
 1465     remote_user_config.server = server;
 1466 
 1467     /* We use this field to identify regular throttle configurations
 1468      * vs. the global configuration used to throttle client-ip.
 1469      */
 1470     remote_user_config.name = throttle_remote_user_str;
 1471     remote_user_config.track = &dummy_throttle;
 1472     remote_user_config.uid = UNSET;
 1473 
 1474     if (remote_user_config.policy == (t_policy *) 0)
 1475         remote_user_config.policy = &policy_table[0];
 1476 
 1477     if (remote_user_config.period <= 0)
 1478         remote_user_config.period = SECONDS_PER_HOUR;
 1479 }
 1480 #endif
 1481 
 1482     critical = critical_create(p);
 1483     srand((unsigned int) time((time_t *) 0));
 1484         ap_add_version_component("mod_throttle/"VERSION);
 1485 
 1486     if (cmd_restore(p, (const char *) 0) < 0) {
 1487         ap_log_error(
 1488             APLOG_MARK, APLOG_ERR, server,
 1489             "restore from \"%s\" failed", runtime_file
 1490         );
 1491         exit(APEXIT_INIT);
 1492     }
 1493 
 1494     /* This must be last since cleanups are done in reverse order
 1495      * and we want to preserve the data before we release our
 1496      * shared memory and semaphores.
 1497      */
 1498     ap_register_cleanup(p, p, cleanup_module, ap_null_cleanup);
 1499 }
 1500 
 1501 static void
 1502 child_init(server_rec *s, pool *p)
 1503 {
 1504     critical_child_init(s, p);
 1505 }
 1506 
 1507 /***********************************************************************
 1508  ***  Special Runtime Commands
 1509  ***********************************************************************/
 1510 
 1511 static int
 1512 cmd_preserve(struct pool *p, const char *args)
 1513 {
 1514     FILE *fp;
 1515     const char *fn;
 1516     t_config *config;
 1517 
 1518     /* Preserve the runtime data in a file in order to save state
 1519      * information across shutdowns and restarts.
 1520      */
 1521     fn = ap_server_root_relative(p, (char *) runtime_file);
 1522     if ((fp = ap_pfopen(p, fn, "w")) == (FILE *) 0)
 1523         return -1;
 1524 
 1525     /* Write out the number of section entries. */
 1526     (void) fprintf(fp, "throttle=%lu\n", stack_count);
 1527 
 1528     for (config = stack_top; config != (t_config *) 0; config = config->next) {
 1529         if (config->track == (t_throttle *) 0)
 1530             continue;
 1531 
 1532         (void) fprintf(
 1533             fp, "%s\t%lx\t%lx\t%u\t%lu\t%lu\t%lu\n",
 1534             config->name,
 1535             config->track->start,
 1536             config->track->last,
 1537             config->track->delay,
 1538             config->track->volume,
 1539             config->track->refused,
 1540             config->track->requests
 1541         );
 1542     }
 1543 
 1544 #if defined(THROTTLE_CLIENT_IP) || defined(THROTTLE_REMOTE_USER)
 1545 {
 1546     t_visitor *v;
 1547 
 1548 #ifdef THROTTLE_CLIENT_IP
 1549     (void) fprintf(fp, "client-ip=%lu\n", client_ip_pool->used);
 1550 
 1551     for (v = client_ip_pool->head; v != (t_visitor *) 0; v = v->next) {
 1552         if (v->remote.ip.s_addr == 0)
 1553             break;
 1554 
 1555         (void) fprintf(
 1556             fp, "%s\t%lx\t%lx\t%u\t%lu\t%lu\t%lu\n",
 1557             inet_ntoa(v->remote.ip),
 1558             v->start,
 1559             v->last,
 1560             v->delay,
 1561             v->volume,
 1562             v->refused,
 1563             v->requests
 1564         );
 1565     }
 1566 #endif
 1567 #ifdef THROTTLE_REMOTE_USER
 1568     (void) fprintf(fp, "remote-user=%lu\n", remote_user_pool->used);
 1569 
 1570     for (v = remote_user_pool->head; v != (t_visitor *) 0; v = v->next) {
 1571         if (*v->remote.user == '\0')
 1572             break;
 1573 
 1574         (void) fprintf(
 1575             fp, "%s\t%lx\t%lx\t%u\t%lu\t%lu\t%lu\n",
 1576             v->remote.user,
 1577             v->start,
 1578             v->last,
 1579             v->delay,
 1580             v->volume,
 1581             v->refused,
 1582             v->requests
 1583         );
 1584     }
 1585 #endif
 1586 }
 1587 #endif /* defined(THROTTLE_CLIENT_IP) || defined(THROTTLE_REMOTE_USER) */
 1588 
 1589     if (ap_pfclose(p, fp) < 0)
 1590         return -1;
 1591 
 1592     /* Make sure that when we shutdown or restart, the file ownership
 1593      * of the runtime file is that of the child server process. This
 1594      * allows the child process to perform preserve & restore commands.
 1595      */
 1596     if (getuid() == 0)
 1597         return chown(fn, ap_user_id, ap_group_id);
 1598 
 1599     return 0;
 1600 }
 1601 
 1602 /*
 1603  * Restore the runtime data previoisly preserveded in a file prior to a
 1604  * shutdown or restart.
 1605  */
 1606 static int
 1607 cmd_restore(struct pool *p, const char *args)
 1608 {
 1609     int rc;
 1610     FILE *fp;
 1611     t_config *config;
 1612     unsigned long count;
 1613     const char *fn, *fmt, *buf;
 1614 
 1615     p = ap_make_sub_pool(p);
 1616     fn = ap_server_root_relative(p, (char *) runtime_file);
 1617     if ((fp = ap_pfopen(p, fn, "r")) == (FILE *) 0) {
 1618         ap_destroy_pool(p);
 1619         return 0;
 1620     }
 1621 
 1622     /* Create a buffer large enough to hold a pathname and terminating
 1623      * null byte. Then to avoid possible buffer overrun create the
 1624      * fscanf() format string specifying the buffer's byte length.
 1625      */
 1626     buf = ap_pcalloc(p, PATH_MAX+1);
 1627     fmt = ap_psprintf(p, "%%%lds", (long) PATH_MAX);
 1628 
 1629     if (fscanf(fp, "throttle=%lu ", &count) != 1)
 1630         return 1;
 1631 
 1632     /* Read the name at the start of this line. */
 1633     while (0 < count-- && fscanf(fp, fmt, buf) == 1) {
 1634         /* Find named configuration. */
 1635         for (config = stack_top; config != (t_config *) 0; config = config->next) {
 1636             if (ap_strcasecmp_match(buf, config->name) == 0)
 1637                 break;
 1638         }
 1639 
 1640         /* Has the configuration been removed? */
 1641         if (config == (t_config *) 0 || config->track == (t_throttle *) 0) {
 1642             /* Skip remainder of the line. */
 1643             (void) fscanf(fp, "%*[^\n]");
 1644             continue;
 1645         }
 1646 
 1647         /* Restore the configuration's previous running data. */
 1648         rc = fscanf(
 1649             fp, "%lx %lx %u %lu %lu %lu ",
 1650             &config->track->start,
 1651             &config->track->last,
 1652             &config->track->delay,
 1653             &config->track->volume,
 1654             &config->track->refused,
 1655             &config->track->requests
 1656         );
 1657 
 1658         if (rc != 6)
 1659             break;
 1660     }
 1661 
 1662 #ifdef THROTTLE_CLIENT_IP
 1663     if (fscanf(fp, "client-ip=%lu ", &count) != 1)
 1664         return 1;
 1665 
 1666     if (client_ip_size < count)
 1667         count = client_ip_size;
 1668 
 1669     (void) critical_acquire(critical);
 1670 
 1671     /* Read the name at the start of this line. */
 1672     while (0 < count-- && fscanf(fp, fmt, buf) == 1) {
 1673         t_visitor *v;
 1674         struct in_addr ip;
 1675 
 1676         ip.s_addr = inet_addr(buf);
 1677         v = get_client_ip(client_ip_pool, ip);
 1678 
 1679         rc = fscanf(
 1680             fp, "%lx %lx %u %lu %lu %lu ",
 1681             &v->start,
 1682             &v->last,
 1683             &v->delay,
 1684             &v->volume,
 1685             &v->refused,
 1686             &v->requests
 1687         );
 1688 
 1689         if (rc != 6)
 1690             break;
 1691     }
 1692 
 1693     (void) critical_release(critical);
 1694 #endif
 1695 #ifdef THROTTLE_REMOTE_USER
 1696     if (fscanf(fp, "remote-user=%lu ", &count) != 1)
 1697         return 1;
 1698 
 1699     if (remote_user_size < count)
 1700         count = remote_user_size;
 1701 
 1702     (void) critical_acquire(critical);
 1703 
 1704     /* Read the name at the start of this line. */
 1705     while (0 < count-- && fscanf(fp, fmt, buf) == 1) {
 1706         t_visitor *v;
 1707 
 1708         v = get_remote_user(remote_user_pool, (char *) buf);
 1709         if (v == (t_visitor *) 0)
 1710             continue;
 1711 
 1712         rc = fscanf(
 1713             fp, "%lx %lx %u %lu %lu %lu ",
 1714             &v->start,
 1715             &v->last,
 1716             &v->delay,
 1717             &v->volume,
 1718             &v->refused,
 1719             &v->requests
 1720         );
 1721 
 1722         if (rc != 6)
 1723             break;
 1724     }
 1725 
 1726     (void) critical_release(critical);
 1727 #endif
 1728 
 1729     (void) ap_pfclose(p, fp);
 1730     ap_destroy_pool(p);
 1731 
 1732     return 0;
 1733 }
 1734 
 1735 /***********************************************************************
 1736  ***  Directive Routines
 1737  ***********************************************************************/
 1738 
 1739 static const char *
 1740 set_policy3(pool *p, t_config *config, char *policy, char *limit, char *period)
 1741 {
 1742     t_policy *pp;
 1743 
 1744     for (pp = policy_table; pp->name != (const char *) 0; ++pp) {
 1745         if (ap_strcasecmp_match(policy, pp->name) == 0)
 1746             break;
 1747     }
 1748 
 1749     if (pp->name == (const char *) 0)
 1750         return "Invalid policy.";
 1751 
 1752     config->policy = pp;
 1753 
 1754     config->limit = strtol(limit, (char **) &limit, 10);
 1755     if (config->limit < 0)
 1756         config->limit = 0;
 1757 
 1758     /* The limit value will normally, but not necessarily, be an
 1759      * expression in kilo-bytes, mega-bytes, or giga-bytes. Even
 1760      * when the limit doesn't refer to kilo-byte units, treat it
 1761      * as such for the qualifiers. Ignore anything larger since
 1762      * it will overflow unsigned long on 32-bit systems.
 1763      */
 1764     switch (ap_toupper(*limit)) {
 1765     default:
 1766         return "Invalid units for limit.";
 1767     case 'G':
 1768         config->limit *= 1024;
 1769     case 'M':
 1770         config->limit *= 1024;
 1771     case 'K': case '\0':
 1772         break;
 1773     }
 1774 
 1775     if (config->policy->apply == policy_random && 100 < config->limit)
 1776         return "Random policy requires a percentage limit (0..100).";
 1777 
 1778     if (period != (char *) 0 && ap_isdigit(*period)) {
 1779         config->period = strtol(period, (char **) &period, 10);
 1780 
 1781         if (config->period <= 0)
 1782             config->period = 1;
 1783 
 1784         /* The period value can be expressed in terms of seconds,
 1785          * minutes, hours, days, and weeks.
 1786          */
 1787         switch (ap_tolower(*period)) {
 1788         default:
 1789             return "Invalid units for period.";
 1790         case 'w':
 1791             config->period *= 7;
 1792         case 'd':
 1793             config->period *= 24;
 1794         case 'h':
 1795             config->period *= 60;
 1796         case 'm':
 1797             config->period *= 60;
 1798         case 's': case '\0':
 1799             break;
 1800         }
 1801     } else if (config->policy->apply == policy_none) {
 1802         config->period = SECONDS_PER_MONTH;
 1803     } else {
 1804         config->period = DEFAULT_PERIOD;
 1805     }
 1806 
 1807     return (const char *) 0;
 1808 }
 1809 
 1810 static const char *
 1811 set_policy(pool *p, t_config *config, const char *args)
 1812 {
 1813     char *policy, *limit, *period;
 1814 
 1815     if (config == (t_config *) 0)
 1816         return (const char *) 0;
 1817 
 1818     /* First argument is required. */
 1819     if ((policy = ap_getword_white(p, &args)) == (char *) 0)
 1820         return "Policy not specified.";
 1821 
 1822     /* Second argument required for everything but "off". */
 1823     if ((limit = ap_getword_white(p, &args)) == (char *) 0)
 1824         return "Limit not specified.";
 1825 
 1826     /* Third argument optional. */
 1827     period = ap_getword_white(p, &args);
 1828 
 1829     return set_policy3(p, config, policy, limit, period);
 1830 }
 1831 
 1832 /*
 1833  * ThrottlePolicy {policy} [limit [period]]
 1834  *
 1835  * Context: server
 1836  */
 1837 static const char *
 1838 throttle_policy(cmd_parms *cmd, void *dconfig, const char *args)
 1839 {
 1840     t_config *config = (t_config *) dconfig;
 1841 
 1842     if (config == (t_config *) 0)
 1843         return (const char *) 0;
 1844 
 1845     config->server = cmd->server;
 1846 #ifndef NDEBUG
 1847 {
 1848     const char *what, *where;
 1849 
 1850     if (cmd->path == (char *) 0 || *cmd->path == '\0') {
 1851         where = config->server->server_hostname;
 1852         what = stype;
 1853     } else {
 1854         where = cmd->path;
 1855         what = dtype;
 1856     }
 1857 
 1858     ap_log_error(
 1859         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, cmd->server,
 1860         "%s %s: ThrottlePolicy %s", what, where, args
 1861     );
 1862 }
 1863 #endif
 1864     return set_policy(cmd->temp_pool, config, args);
 1865 }
 1866 
 1867 static const char *
 1868 throttle_single_user(cmd_parms *cmd, const char *user, const char *args)
 1869 {
 1870     uid_t uid;
 1871     t_config *config;
 1872 
 1873     /* Skip users who are not defined in the system user database. */
 1874     if ((uid = uname2id(user)) == (uid_t) -1)
 1875         return (const char *) 0;
 1876 
 1877     /* Has this user been previously defined? */
 1878     for (config = config_stack; config != (t_config *) 0; config = config->next) {
 1879         if (config->uid == uid)
 1880             break;
 1881     }
 1882 
 1883     if (config == (t_config *) 0)
 1884         config = create_dir_config(cmd->pool, (char *) user);
 1885 
 1886     config->server = cmd->server;
 1887     config->uid = uid;
 1888 
 1889 #ifndef NDEBUG
 1890     ap_log_error(
 1891         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, cmd->server,
 1892         "ThrottleUser %s %s", user, args
 1893     );
 1894 #endif
 1895     return set_policy(cmd->temp_pool, config, args);
 1896 }
 1897 
 1898 /*
 1899  * ThrottleUser {user} {policy} [limit [period]]
 1900  * ThrottleUser    *   {policy} [limit [period]]
 1901  * ThrottleUser /path  {policy} [limit [period]]
 1902  *
 1903  * Context: server
 1904  */
 1905 static const char *
 1906 throttle_user(cmd_parms *cmd, void *dconfig, const char *args)
 1907 {
 1908     const char *user, *buf, *fmt, *err;
 1909 
 1910     if ((user = ap_getword_white(cmd->temp_pool, &args)) == (char *) 0)
 1911         return "User ID, *, or pathname not specified.";
 1912 
 1913     /* Create a throttle for all the users that appear in the
 1914      * system user database.
 1915      */
 1916     if (user[0] == '*' && user[1] == '\0') {
 1917 #ifdef HAVE_GETPWENT
 1918         struct passwd *pw;
 1919 
 1920         setpwent();
 1921         while ((pw = getpwent()) != (struct passwd *) 0) {
 1922             err = throttle_single_user(cmd, pw->pw_name, args);
 1923             if (err != (const char *) 0)
 1924                 break;
 1925         }
 1926         endpwent();
 1927 
 1928         return err;
 1929 #else
 1930         user = DEFAULT_PASSWD_FILE;
 1931 #endif
 1932     }
 1933 
 1934     /* Create a throttle for all the users that appear in the first
 1935      * field of each line of the file. The field separator is a colon
 1936      * (:). This format is common to both /etc/passwd and .htpasswd
 1937      * files.
 1938      */
 1939     if (ap_os_is_path_absolute(user)) {
 1940         FILE *fp;
 1941 
 1942         if ((fp = ap_pfopen(cmd->temp_pool, user, "r")) == (FILE *) 0)
 1943             return "ThrottleUser file not found.";
 1944 
 1945         /* Create a buffer large enough to hold a line and terminating
 1946          * null byte. Then to avoid possible buffer overrun create the
 1947          * fscanf() format string specifying the buffer's byte length.
 1948          */
 1949         buf = ap_pcalloc(cmd->temp_pool, BUFSIZ);
 1950         fmt = ap_psprintf(cmd->temp_pool, "%%%ld[^:]%%*[^\n] ", (long) BUFSIZ-1);
 1951 
 1952         /* Read the user name at the start of this line. */
 1953         while (fscanf(fp, fmt, buf) == 1) {
 1954             err = throttle_single_user(cmd, buf, args);
 1955             if (err != (const char *) 0)
 1956                 break;
 1957         }
 1958 
 1959         (void) ap_pfclose(cmd->temp_pool, fp);
 1960 
 1961         return err;
 1962     }
 1963 
 1964     return throttle_single_user(cmd, user, args);
 1965 }
 1966 
 1967 #ifdef THROTTLE_CLIENT_IP
 1968 /*
 1969  * ThrottleClientIP {size} {policy} [limit [period]]
 1970  *
 1971  * Context: server
 1972  */
 1973 static const char *
 1974 throttle_client_ip(cmd_parms *cmd, void *dconfig, const char *args)
 1975 {
 1976     client_ip_size = strtol(args, (char **) &args, 10);
 1977 
 1978 #ifndef NDEBUG
 1979     ap_log_error(
 1980         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, cmd->server,
 1981         "ThrottleClientIP %s", args
 1982     );
 1983 #endif
 1984 
 1985     if (client_ip_size == 0 && !ap_isspace(*args))
 1986         return "Client IP pool size not specified.";
 1987 
 1988     /* Remove one from the count, since the visitor list structure
 1989      * declares one entry already for us.
 1990      */
 1991     client_ip_size--;
 1992 
 1993     while (ap_isspace(*args)) ++args;
 1994 
 1995     return set_policy(cmd->temp_pool, &client_ip_config, args);
 1996 }
 1997 #endif
 1998 
 1999 #ifdef THROTTLE_REMOTE_USER
 2000 /*
 2001  * ThrottleRemoteUser {size} {policy} [limit [period]]
 2002  *
 2003  * Context: server
 2004  */
 2005 static const char *
 2006 throttle_remote_user(cmd_parms *cmd, void *dconfig, const char *args)
 2007 {
 2008     remote_user_size = strtol(args, (char **) &args, 10);
 2009 
 2010 #ifndef NDEBUG
 2011     ap_log_error(
 2012         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, cmd->server,
 2013         "ThrottleRemoteUser %s", args
 2014     );
 2015 #endif
 2016 
 2017     if (remote_user_size == 0 && !ap_isspace(*args))
 2018         return "Remote user pool size not specified.";
 2019 
 2020     /* Remove one from the count, since the visitor list structure
 2021      * declares one entry already for us.
 2022      */
 2023     remote_user_size--;
 2024 
 2025     while (ap_isspace(*args)) ++args;
 2026 
 2027     return set_policy(cmd->temp_pool, &remote_user_config, args);
 2028 }
 2029 #endif
 2030 
 2031 /*
 2032  * ThrottleRuntimeFile {filename}
 2033  *
 2034  * Context: server
 2035  */
 2036 static const char *
 2037 throttle_runtime_file(cmd_parms *cmd, void *dconfig, char *filename)
 2038 {
 2039     runtime_file = ap_pstrdup(cmd->pool, filename);
 2040 
 2041     return (const char *) 0;
 2042 }
 2043 
 2044 /*
 2045  * ThrottleLockFile {filename}
 2046  *
 2047  * Context: server
 2048  */
 2049 static const char *
 2050 throttle_lock_file(cmd_parms *cmd, void *dconfig, char *filename)
 2051 {
 2052     lock_file = ap_pstrdup(cmd->pool, filename);
 2053 
 2054     return (const char *) 0;
 2055 }
 2056 
 2057 /*
 2058  * ThrottleMaxDelay {seconds}
 2059  *
 2060  * Context: server
 2061  */
 2062 static const char *
 2063 throttle_max_delay(cmd_parms *cmd, void *dconfig, char *seconds)
 2064 {
 2065     long secs = strtol(seconds, (char **) 0, 10);
 2066 
 2067     if (secs < 0)
 2068         max_delay = 0;
 2069     else if (UINT_MAX < secs)
 2070         max_delay = UINT_MAX;
 2071     else
 2072         max_delay = (unsigned int) secs;
 2073 
 2074     return (const char *) 0;
 2075 }
 2076 
 2077 /*
 2078  * ThrottleRefresh {seconds}
 2079  *
 2080  * Context: server
 2081  */
 2082 static const char *
 2083 throttle_refresh(cmd_parms *cmd, void *dconfig, char *seconds)
 2084 {
 2085     long secs = strtol(seconds, (char **) 0, 10);
 2086 
 2087     if (secs < 0)
 2088         refresh = 0;
 2089     else if (UINT_MAX < secs)
 2090         refresh = UINT_MAX;
 2091     else
 2092         refresh = (unsigned int) secs;
 2093 
 2094     return (const char *) 0;
 2095 }
 2096 
 2097 /*
 2098  * ThrottleContentType {mime_type}
 2099  *
 2100  * where mime_type is either "text/html" or "text/plain"
 2101  *
 2102  * Context: server
 2103  */
 2104 static const char *
 2105 throttle_content_type(cmd_parms *cmd, void *dconfig, char *mime_type)
 2106 {
 2107     if (ap_strcasecmp_match(mime_type, text_html) == 0)
 2108         content_type = text_html;
 2109     else if (ap_strcasecmp_match(mime_type, text_plain) == 0)
 2110         content_type = text_plain;
 2111     else
 2112         return "Supported content-types are: text/html, text/plain";
 2113 
 2114     return (const char *) 0;
 2115 }
 2116 
 2117 /*
 2118  * ThrottleIndicator {green | yellow | red | critical} {percentage}
 2119  *
 2120  * Context: server
 2121  */
 2122 static const char *
 2123 throttle_indicator(cmd_parms *cmd, void *dconfig, char *indicator, char *num)
 2124 {
 2125     int i;
 2126 
 2127     for (i = 0; i < ALERT_LEVELS; ++i) {
 2128         if (ap_strcasecmp_match(indicator, alert_names[i]) == 0) {
 2129             alert[i] = strtol(num, (char **) 0, 10);
 2130             return (const char *) 0;
 2131         }
 2132     }
 2133 
 2134     return "Invalid indicator";
 2135 }
 2136 
 2137 /***********************************************************************
 2138  ***  Policy Handlers
 2139  ***********************************************************************/
 2140 
 2141 /*
 2142  * Reset the period and collected data.
 2143  *
 2144  * NOTE that this function is a *critical section* and should be wrapped
 2145  * by critical_acquire() & critical_release(). I use to do it within this
 2146  * function, which is called within a loop sometimes, but moved it out
 2147  * a level or two to improve speed (even just a little).
 2148  */
 2149 static void
 2150 reset_info(t_config *config, time_t when)
 2151 {
 2152     if (config->track == (t_throttle *) 0)
 2153         return;
 2154 
 2155     /* Avoid possible divide by zero errors when we calculate a
 2156      * time difference and compute something per second.
 2157      */
 2158     config->track->start = config->track->last = when - 1;
 2159     config->track->requests = 0;
 2160     config->track->refused = 0;
 2161     config->track->volume = 0;
 2162     config->track->delay = 0;
 2163 }
 2164 
 2165 /*
 2166  * Reset a throttle by its name be it user, directory, or host.
 2167  * If the name is "*", then all the throttles will be reset.
 2168  */
 2169 static int
 2170 reset_info_match(const char *args, time_t when)
 2171 {
 2172     t_config *config;
 2173 
 2174     (void) critical_acquire(critical);
 2175 
 2176     for (config = stack_top; config != (t_config *) 0; config = config->next) {
 2177         if (ap_strcasecmp_match(config->name, args) == 0) {
 2178             reset_info(config, when);
 2179 
 2180             if (args[0] != '*' || args[1] != '\0')
 2181                 break;
 2182         }
 2183     }
 2184 
 2185     (void) critical_release(critical);
 2186 
 2187     return 0;
 2188 }
 2189 
 2190 static int
 2191 busy_signal(request_rec *r, t_config *config)
 2192 {
 2193     (void) critical_acquire(critical);
 2194     config->track->refused++;
 2195     (void) critical_release(critical);
 2196 
 2197     return HTTP_SERVICE_UNAVAILABLE;
 2198 }
 2199 
 2200 /*
 2201  * Check value against the limit and refuse the connection if exceeded.
 2202  */
 2203 static int
 2204 policy_over_limit(request_rec *r, t_config *config, unsigned long value)
 2205 {
 2206 #ifndef NDEBUG
 2207     ap_log_rerror(
 2208         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 2209         "(%ld) policy_over_limit server=%s, request=%s",
 2210         (long) getpid(), ap_get_server_name(r), r->the_request
 2211     );
 2212 #endif
 2213 
 2214     if (config->limit < value) {
 2215         ap_log_rerror(
 2216             APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
 2217             "%s policy %s limit of %ld exceeded, %ld",
 2218             config->name, config->policy->name, config->limit, value
 2219         );
 2220         return busy_signal(r, config);
 2221     }
 2222 
 2223     return DECLINED;
 2224 }
 2225 
 2226 /*
 2227  * NOTE is a critical section. See access_handler().
 2228  */
 2229 static void
 2230 adjust_reset(request_rec *r, t_config *config)
 2231 {
 2232     reset_info(config, r->request_time);
 2233 }
 2234 
 2235 /*
 2236  * ThrottlePolicy none {ignored} {period}
 2237  *
 2238  * No policy enforced, just monitoring. The period is used only to
 2239  * periodically reset the counters.
 2240  */
 2241 static int
 2242 policy_none(request_rec *r, t_config *config)
 2243 {
 2244     return policy_over_limit(r, config, 0);
 2245 }
 2246 
 2247 /*
 2248  * Return 0 percent.
 2249  */
 2250 static int
 2251 percent_none(t_config *config)
 2252 {
 2253     /* Do nothing. */
 2254     return 0;
 2255 }
 2256 
 2257 /*
 2258  * ThrottlePolicy concurrent {limit} {period}
 2259  *
 2260  * Impose a limit on the number of concurrent requests at any one time.
 2261  * The period is used only to periodically reset the counters.
 2262  *
 2263  * TEST COMMENTS:
 2264  *
 2265  * As a policy, this one doesn't appear to work so well, possibly because
 2266  * the requests from one client may happen sequentially; when refused, a
 2267  * client may make a certain number of attempts before giving up; a client
 2268  * may only maintain a limited number of requests / connection at any one
 2269  * time which is less than the limit.
 2270  *
 2271  * To test this properly, browsers (IE, NS, Opera, wget) on multiple machines
 2272  * must make requests at the same time. The number of refused requests should
 2273  * increase.
 2274  */
 2275 static int
 2276 policy_concurrent(request_rec *r, t_config *config)
 2277 {
 2278 #ifdef THROTTLE_CLIENT_IP
 2279     if (config->name == throttle_client_ip_str)
 2280         return DECLINED;
 2281 #endif
 2282 #ifdef THROTTLE_REMOTE_USER
 2283     if (config->name == throttle_remote_user_str)
 2284         return DECLINED;
 2285 #endif
 2286 
 2287     return policy_over_limit(r, config, config->track->active);
 2288 }
 2289 
 2290 /*
 2291  * Return percentage of requests relative to our limit.
 2292  */
 2293 static int
 2294 percent_concurrent(t_config *config)
 2295 {
 2296     if (config->limit <= 0)
 2297         return 0;
 2298 
 2299     return config->track->active * 100 / config->limit;
 2300 }
 2301 
 2302 /*
 2303  * ThrottlePolicy request {limit} {period}
 2304  *
 2305  * Impose a limit on the number of requests per period. When this limit is
 2306  * exceeded all further requests are refused, until the end of the period at
 2307  * which time the period and request count are reset.
 2308  */
 2309 static int
 2310 policy_request(request_rec *r, t_config *config)
 2311 {
 2312     return policy_over_limit(r, config, config->track->requests);
 2313 }
 2314 
 2315 /*
 2316  * Return percentage of requests relative to our limit.
 2317  */
 2318 static int
 2319 percent_request(t_config *config)
 2320 {
 2321     if (config->limit <= 0)
 2322         return 0;
 2323 
 2324     return config->track->requests * 100 / config->limit;
 2325 }
 2326 
 2327 /*
 2328  * ThrottlePolicy document {limit} {period}
 2329  *
 2330  * Excluding requests for HTML page elements such as images and style sheets,
 2331  * impose a limit on the number of requests per period. When this limit is
 2332  * exceeded all further requests are refused, until the end of the period at
 2333  * which time the period and request count are reset.
 2334  */
 2335 static int
 2336 policy_document(request_rec *r, t_config *config)
 2337 {
 2338     const char **pe;
 2339     request_rec *sub;
 2340     static const char *page_elements[] = {
 2341         "image/*", "audio/*", "text/css", "text/*script", (char *) 0
 2342     };
 2343 
 2344 #ifndef NDEBUG
 2345     ap_log_rerror(
 2346         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 2347         "(%ld) policy_document server=%s, request=%s",
 2348         (long) getpid(), ap_get_server_name(r), r->the_request
 2349     );
 2350 #endif
 2351 
 2352     /* Is it a page element request? */
 2353     ap_table_setn(r->headers_in, x_is_subrequest, true);
 2354     sub = ap_sub_req_lookup_uri(r->uri, r);
 2355     if (sub->content_type != (const char *) 0) {
 2356         for (pe = page_elements; *pe != (const char *) 0; ++pe) {
 2357             if (ap_strcasecmp_match(sub->content_type, *pe) == 0) {
 2358                 ap_table_setn(r->notes, request_not_counted, true);
 2359                 break;
 2360             }
 2361         }
 2362     }
 2363     ap_destroy_sub_req(sub);
 2364     ap_table_unset(r->headers_in, x_is_subrequest);
 2365 
 2366     /* Have we exceeded our request limit?. */
 2367     if (0 < config->limit && config->limit < config->track->requests) {
 2368         ap_log_rerror(
 2369             APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
 2370             "%s limit of %ld documents exceeded",
 2371             config->name, config->limit
 2372         );
 2373         return busy_signal(r, config);
 2374     }
 2375 
 2376     return DECLINED;
 2377 }
 2378 
 2379 /*
 2380  * Return percentage of active requests relative to our limit.
 2381  */
 2382 static int
 2383 percent_document(t_config *config)
 2384 {
 2385     if (config->limit <= 0)
 2386         return 0;
 2387 
 2388     return config->track->requests * 100 / config->limit;
 2389 }
 2390 
 2391 /*
 2392  * ThrottlePolicy random {percent} {period}
 2393  *
 2394  * Randomly accept a percentage of the requests. If the percentage
 2395  * is zero (0), then every request is refused; if the percentage
 2396  * is 100, then all requests are accepted. The period is used only
 2397  * to periodically reset the counters.
 2398  */
 2399 static int
 2400 policy_random(request_rec *r, t_config *config)
 2401 {
 2402     return policy_over_limit(r, config, rand() % 100);
 2403 }
 2404 
 2405 /*
 2406  * Return percentage of requests accepted.
 2407  */
 2408 static int
 2409 percent_random(t_config *config)
 2410 {
 2411     if (config->track->requests <= 0)
 2412         return 0;
 2413 
 2414     return 100 - config->track->refused * 100 / config->track->requests;
 2415 }
 2416 
 2417 /*
 2418  * ThrottlePolicy volume {kbytes} {period}
 2419  *
 2420  * Impose a limit on the volume (kbytes sent) per period. When this limit is
 2421  * exceeded all further requests are refused, until the end of the period at
 2422  * which time the period and volume count are reset.
 2423  */
 2424 static int
 2425 policy_volume(request_rec *r, t_config *config)
 2426 {
 2427     return policy_over_limit(r, config, config->track->volume);
 2428 }
 2429 
 2430 /*
 2431  * Return percentage of volume relative to our limit.
 2432  */
 2433 static int
 2434 percent_volume(t_config *config)
 2435 {
 2436     if (config->limit <= 0)
 2437         return 0;
 2438 
 2439     return config->track->volume * 100 / config->limit;
 2440 }
 2441 
 2442 /*
 2443  * ThrottlePolicy idle {minimum} {period}
 2444  *
 2445  * Impose a mimimum idle time between requests. When the miminum is not
 2446  * reached, then the request incurs a calculated delay penalty or is
 2447  * refused.
 2448  *
 2449  * First, whenever the elapsed time exceeds the period length, then the
 2450  * counters are reset.
 2451  *
 2452  * Second, if the idle time between requests exceeds the minimum, then the
 2453  * the request proceeds without delay. Otherwise the request is delayed
 2454  * between one and ThrottleMaxDelay seconds. If the delay would exceed
 2455  * ThrottleMaxDelay, then we refuse the request entirely to avoid occupying
 2456  * servers unnecessarily.
 2457  *
 2458  * The delay is computed as the minimum less the idle time between requests.
 2459  */
 2460 static int
 2461 policy_idle(request_rec *r, t_config *config)
 2462 {
 2463     unsigned long idle;
 2464 
 2465 #ifndef NDEBUG
 2466     ap_log_rerror(
 2467         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 2468         "(%ld) policy_idle server=%s, request=%s",
 2469         (long) getpid(), ap_get_server_name(r), r->the_request
 2470     );
 2471 #endif
 2472 
 2473     idle = r->request_time - config->track->last;
 2474 
 2475     if (idle < config->limit) {
 2476         (void) critical_acquire(critical);
 2477         config->track->delay = config->limit - idle;
 2478         (void) critical_release(critical);
 2479 
 2480         if (0 < max_delay && max_delay < config->track->delay) {
 2481 #ifdef POLICY_DELAY_ONLY
 2482             (void) critical_acquire(critical);
 2483             config->track->delay = max_delay;
 2484             (void) critical_release(critical);
 2485 #else
 2486             ap_log_rerror(
 2487                 APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
 2488                 "%s delay=%lu too large",
 2489                 config->name, config->track->delay
 2490             );
 2491             return busy_signal(r, config);
 2492 #endif
 2493         }
 2494 
 2495         sleep(config->track->delay);
 2496     } else if (0 < config->track->delay) {
 2497         (void) critical_acquire(critical);
 2498         config->track->delay = 0;
 2499         (void) critical_release(critical);
 2500     }
 2501 
 2502     return DECLINED;
 2503 }
 2504 
 2505 static int
 2506 percent_idle(t_config *config)
 2507 {
 2508     return config->track->delay * 100 / config->limit;
 2509 }
 2510 
 2511 /*
 2512  * ThrottlePolicy speed {kbytes} {period}
 2513  *
 2514  * Impose a limit on the volume (kbytes sent) per period, which when exceeded
 2515  * the request incurs a calculated delay penalty or is refused.
 2516  *
 2517  * First, whenever the elapsed time exceeds the period length, then the limit
 2518  * (allowance) is deducted from the volume, which cannot be a negative result;
 2519  * also the period length is deducted from the elapse time.
 2520  *
 2521  * Second, if the volume is below the limit, in which case the request
 2522  * proceeds without delay. Otherwise the request is delayed between one and
 2523  * ThrottleMaxDelay seconds. If the delay would exceed ThrottleMaxDelay, then
 2524  * we refuse the request entirely to avoid occupying servers unnecessarily.
 2525  *
 2526  * The delay is currently computed as one plus the integer result of the
 2527  * volume times 10 divided by the limit.
 2528  */
 2529 static int
 2530 policy_speed(request_rec *r, t_config *config)
 2531 {
 2532 #ifndef NDEBUG
 2533     ap_log_rerror(
 2534         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 2535         "(%ld) policy_speed server=%s, request=%s",
 2536         (long) getpid(), ap_get_server_name(r), r->the_request
 2537     );
 2538 #endif
 2539 
 2540     /* Second, have we exceeded our limit (allowance)? */
 2541     if (0 < config->limit && config->limit < config->track->volume) {
 2542         /* Compute the delay required to maintain the speed limit.
 2543          * We keep track of it only for the throttle status, since
 2544          * the field is there for policy_original().
 2545          */
 2546         (void) critical_acquire(critical);
 2547         config->track->delay = (config->track->volume * 10) / config->limit + 1;
 2548         (void) critical_release(critical);
 2549 
 2550         /* When we exeed our upper bounds for an acceptable
 2551          * delay, then refuse the request instead. When
 2552          * this starts happening too often either our max. delay
 2553          * is set too low or our speed limit is too low.
 2554          */
 2555         if (0 < max_delay && max_delay < config->track->delay) {
 2556 #ifdef POLICY_DELAY_ONLY
 2557             (void) critical_acquire(critical);
 2558             config->track->delay = max_delay;
 2559             (void) critical_release(critical);
 2560 #else
 2561             ap_log_rerror(
 2562                 APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
 2563                 "%s delay=%lu too large",
 2564                 config->name, config->track->delay
 2565             );
 2566             return busy_signal(r, config);
 2567 #endif
 2568         }
 2569 
 2570         sleep(config->track->delay);
 2571     } else if (0 < config->track->delay) {
 2572         /* We have fallen below the limit and must update the delay
 2573          * for the status display.
 2574          */
 2575         (void) critical_acquire(critical);
 2576         config->track->delay = 0;
 2577         (void) critical_release(critical);
 2578     }
 2579 
 2580     return DECLINED;
 2581 }
 2582 
 2583 /*
 2584  * NOTE is a critical section. See access_handler().
 2585  */
 2586 static void
 2587 adjust_speed(request_rec *r, t_config *config)
 2588 {
 2589     /* Reset these every period. */
 2590     config->track->delay = 0;
 2591     config->track->refused = 0;
 2592     config->track->requests = 0;
 2593 
 2594     /* Deduct limit from the volume for this period. We keep
 2595      * the excess and count it towards the next period.
 2596      */
 2597     if (config->limit < config->track->volume)
 2598         config->track->volume -= config->limit;
 2599     else
 2600         config->track->volume = 0;
 2601 
 2602     /* Advance to the next period. */
 2603     config->track->start += config->period;
 2604 }
 2605 
 2606 /*
 2607  * ThrottlePolicy original {KBps} {period}
 2608  *
 2609  * Original mod_throttle 2.0 heuristic.
 2610  *
 2611  * Impose a limit on the volume (kbytes sent) per period, which when exceeded
 2612  * the request incurs a counter-based delay penalty or is refused.
 2613  *
 2614  * First, whenever the elapsed time exceeds the period length, then the volume
 2615  * and elapsed time are halved.
 2616  *
 2617  * Second, if the volume is below the limit, then the delay counter is
 2618  * decreased by one second if it is not yet zero. Otherwise, when the limit
 2619  * is exeeded, the delay counter is increased by one second. The delay can be
 2620  * between zero and ThrottleMaxDelay seconds, after which the request will
 2621  * be refused to avoid occupying servers unnecessarily.
 2622  */
 2623 static int
 2624 policy_original(request_rec *r, t_config *config)
 2625 {
 2626     long slack;
 2627 
 2628 #ifndef NDEBUG
 2629     ap_log_rerror(
 2630         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 2631         "(%ld) policy_original server=%s, request=%s",
 2632         (long) getpid(), ap_get_server_name(r), r->the_request
 2633     );
 2634 #endif
 2635 
 2636     /* The original policy had a slack time applied so that the
 2637      * delay would not kick in so quickly after a restart. We
 2638      * have dropped ThrottleSlack in favour of using 1/5 of the
 2639      * period for the slack, because ThrottleSlack was a global
 2640      * and the period can now vary, its too confusing to try
 2641      * and find a suitable value that is less than all the period.
 2642      */
 2643     slack = config->period / 5;
 2644 
 2645     /* When we are within our limit, decrease the delay; otherwise
 2646      * increase the delay upto but not exceeding max_delay seconds
 2647      * when max_delay is > 0.
 2648      */
 2649     (void) critical_acquire(critical);
 2650 
 2651     if (config->track->volume <= config->limit) {
 2652         if (0 < config->track->delay)
 2653             --config->track->delay;
 2654     } else if (config->track->delay <= max_delay || max_delay <= 0) {
 2655         ++config->track->delay;
 2656     }
 2657 
 2658     (void) critical_release(critical);
 2659 
 2660     /* Are we monitoring only? */
 2661     if (config->limit <= 0)
 2662         return DECLINED;
 2663 
 2664 #ifndef POLICY_DELAY_ONLY
 2665     /* Allow the delay to adjusted (hopefully downwards) before
 2666      * considering to refuse the connection.
 2667      */
 2668     if (0 < max_delay && max_delay < config->track->delay) {
 2669         return busy_signal(r, config);
 2670     }
 2671 #endif
 2672 
 2673     /* Muhahaha - throttle the buggers! */
 2674     if (0 < config->track->delay)
 2675         sleep(config->track->delay);
 2676 
 2677     return DECLINED;
 2678 }
 2679 
 2680 /*
 2681  * NOTE is a critical section. See access_handler().
 2682  */
 2683 static void
 2684 adjust_original(request_rec *r, t_config *config)
 2685 {
 2686     config->track->start += (r->request_time - config->track->start) / 2;
 2687     config->track->volume /= 2;
 2688 }
 2689 
 2690 /***********************************************************************
 2691  ***  Request Phase Handlers
 2692  ***********************************************************************/
 2693 
 2694 /*
 2695  * Return true if this a simple file request, as oppose to a directory,
 2696  * dynamic document, CGI, etc.
 2697  */
 2698 static int
 2699 is_request_for_file(request_rec *r)
 2700 {
 2701     const char *handler = ap_table_get(r->notes, request_handler);
 2702     const char *mime = ap_table_get(r->notes, request_content_type);
 2703 
 2704     /* Is it something other than a regular file? */
 2705     if (r->finfo.st_mode == 0 || !S_ISREG(r->finfo.st_mode))
 2706         return 0;
 2707 
 2708     /* Do we have a mime type? */
 2709     if (mime == (const char *) 0)
 2710         return 0;
 2711 
 2712     /* Do we have a special handler for it? */
 2713     if (handler != (const char *) 0)
 2714         return 0;
 2715 
 2716     /* Some special mime type that is a parsed file? */
 2717     if (ap_strcmp_match(mime, "application/x-httpd-*") == 0)
 2718         return 0;
 2719 
 2720     /* Assume its a simple file. */
 2721     return 1;
 2722 }
 2723 
 2724 /*
 2725  * Determine who our content-response handler is and parse the query string.
 2726  */
 2727 static int
 2728 uri_handler(request_rec *r)
 2729 {
 2730     int not_for_us;
 2731     request_rec *sub;
 2732     char *arg, *key, *value;
 2733 
 2734     if (!ap_is_initial_req(r))
 2735         return DECLINED;
 2736 
 2737     /* Find out what handler is going to be called. */
 2738     ap_table_setn(r->headers_in, x_is_subrequest, true);
 2739     sub = ap_sub_req_lookup_uri(r->uri, r);
 2740 
 2741     not_for_us = sub->handler == (char *) 0
 2742         || ap_strcmp_match(sub->handler, "throttle-*") != 0;
 2743     ap_table_set(r->notes, request_handler, sub->handler);
 2744     ap_table_set(r->notes, request_content_type, sub->content_type);
 2745 
 2746     if (is_request_for_file(sub))
 2747         ap_table_setn(r->notes, is_file_request, true);
 2748 
 2749     ap_destroy_sub_req(sub);
 2750     ap_table_unset(r->headers_in, x_is_subrequest);
 2751 
 2752     /* Check for arguments only when its for our handler. */
 2753     if (not_for_us)
 2754         return DECLINED;
 2755 
 2756     /* Remember this fact for later phases. */
 2757     ap_table_setn(r->notes, is_throttle_handler, true);
 2758 
 2759     /* Parse the query string into the notes table. */
 2760     if (r->args != (char *) 0) {
 2761         for (arg = r->args; *arg != '\0'; ) {
 2762             value = ap_getword_nc(r->pool, &arg, '&');
 2763             if (value == (char *) 0)
 2764                 break;
 2765 
 2766             key = ap_getword_nc(r->pool, &value, '=');
 2767             if (key == (char *) 0 || ap_unescape_url(key) != OK)
 2768                 continue;
 2769 
 2770             if (ap_unescape_url(value) != OK)
 2771                 continue;
 2772 
 2773             ap_table_setn(r->notes, key, value);
 2774         }
 2775     }
 2776 
 2777     return OK;
 2778 }
 2779 
 2780 #ifdef THROTTLE_CLIENT_IP
 2781 /*
 2782  * Throttle based on the client IP address' usage.
 2783  */
 2784 static int
 2785 access_handler(request_rec *r)
 2786 {
 2787     /* Bypass access check on subrequest we make. */
 2788     if (ap_table_get(r->headers_in, x_is_subrequest) == true)
 2789         return OK;
 2790 
 2791     /* Avoid throttling status requests, but subject them to
 2792      * other access controls.
 2793      */
 2794     if (ap_table_get(r->notes, is_throttle_handler) == true)
 2795         return DECLINED;
 2796 
 2797     if (client_ip_size <= 0 || !ap_is_initial_req(r))
 2798         return DECLINED;
 2799 
 2800 #ifndef NDEBUG
 2801     ap_log_rerror(
 2802         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 2803         "(%ld) access_handler ip=%s, request=%s",
 2804         (long) getpid(), r->remote_ip, r->the_request
 2805     );
 2806 #endif
 2807 
 2808     (void) critical_acquire(critical);
 2809 
 2810     /* Lookup any throttle information for this client IP address for
 2811      * which we will apply the global policy for client connections.
 2812      */
 2813     client_ip_config.track = (t_throttle *) get_client_ip(
 2814         client_ip_pool, r->connection->remote_addr.sin_addr
 2815     );
 2816 
 2817     /* Is it time for the period adjustment? */
 2818     if (client_ip_config.period <= (r->request_time - client_ip_config.track->start)) {
 2819         (*client_ip_config.policy->adjust)(r, &client_ip_config);
 2820     }
 2821 
 2822     /* Add in the request size now if we can. */
 2823     if (ap_table_get(r->notes, is_file_request) == true) {
 2824         unsigned long kbytes = (r->finfo.st_size + 512) / 1024;
 2825         ap_table_setn(r->notes, volume_not_counted, true);
 2826         client_ip_config.track->volume += kbytes;
 2827     }
 2828 
 2829     (void) critical_release(critical);
 2830 
 2831     return (*client_ip_config.policy->apply)(r, &client_ip_config);
 2832 }
 2833 #else
 2834 /*
 2835  * Bypass access checks on subrequest generated by us.
 2836  */
 2837 static int
 2838 access_handler(request_rec *r)
 2839 {
 2840     if (ap_table_get(r->headers_in, x_is_subrequest) == true)
 2841         return OK;
 2842 
 2843     return DECLINED;
 2844 }
 2845 #endif
 2846 
 2847 /*
 2848  * Bypass authentication checks on subrequest generated by us.
 2849  */
 2850 static int
 2851 authentication_handler(request_rec *r)
 2852 {
 2853     if (ap_table_get(r->headers_in, x_is_subrequest) == true)
 2854         return OK;
 2855 
 2856     return DECLINED;
 2857 }
 2858 
 2859 #ifdef THROTTLE_REMOTE_USER
 2860 /*
 2861  * Throttle based on the supplied remote user name
 2862  */
 2863 static int
 2864 authorization_handler(request_rec *r)
 2865 {
 2866     t_visitor *remote_user;
 2867 
 2868     /* Bypass authorization check on subrequest we make. */
 2869     if (ap_table_get(r->headers_in, x_is_subrequest) == true)
 2870         return OK;
 2871 
 2872     /* Avoid throttling status requests, but subject them to
 2873      * other authorization checks.
 2874      */
 2875     if (ap_table_get(r->notes, is_throttle_handler) == true)
 2876         return DECLINED;
 2877 
 2878     if (remote_user_size <= 0 || !ap_is_initial_req(r))
 2879         return DECLINED;
 2880 
 2881 #ifndef NDEBUG
 2882     ap_log_rerror(
 2883         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 2884         "(%ld) authorization_handler user=%s request=%s",
 2885         (long) getpid(), r->connection->user, r->the_request
 2886     );
 2887 #endif
 2888 
 2889     (void) critical_acquire(critical);
 2890 
 2891     remote_user = get_remote_user(remote_user_pool, r->connection->user);
 2892     if (remote_user == (t_visitor *) 0)
 2893         return DECLINED;
 2894 
 2895     remote_user_config.track = (t_throttle *) remote_user;
 2896 
 2897     /* Is it time for the period adjustment? */
 2898     if (remote_user_config.period <= (r->request_time - remote_user_config.track->start)) {
 2899         (*remote_user_config.policy->adjust)(r, &remote_user_config);
 2900     }
 2901 
 2902     /* Add in the request size now if we can. */
 2903     if (ap_table_get(r->notes, is_file_request) == true) {
 2904         unsigned long kbytes = (r->finfo.st_size + 512) / 1024;
 2905         ap_table_setn(r->notes, volume_not_counted, true);
 2906         remote_user_config.track->volume += kbytes;
 2907     }
 2908 
 2909     (void) critical_release(critical);
 2910 
 2911     return (*remote_user_config.policy->apply)(r, &remote_user_config);
 2912 }
 2913 #else
 2914 /*
 2915  * Bypass authorization checks on subrequest generated by us.
 2916  */
 2917 static int
 2918 authorization_handler(request_rec *r)
 2919 {
 2920     if (ap_table_get(r->headers_in, x_is_subrequest) == true)
 2921         return OK;
 2922 
 2923     return DECLINED;
 2924 }
 2925 #endif
 2926 
 2927 static t_config *
 2928 get_config_by_name(char *name)
 2929 {
 2930     t_config *config;
 2931 
 2932     for (config = stack_top; config != (t_config *) 0; config = config->next) {
 2933         if (ap_strcasecmp_match(name, config->name) == 0)
 2934             return config;
 2935     }
 2936 
 2937     return &dummy_config;
 2938 }
 2939 
 2940 static t_config *
 2941 get_config_by_uid(uid_t uid)
 2942 {
 2943     t_config *config;
 2944 
 2945     for (config = stack_top; config != (t_config *) 0; config = config->next) {
 2946         if (uid == config->uid)
 2947             return config;
 2948     }
 2949 
 2950     return &dummy_config;
 2951 }
 2952 
 2953 static t_config *
 2954 get_config_by_dir(request_rec *r)
 2955 {
 2956     t_config *config = (t_config *) ap_get_module_config(
 2957         r->per_dir_config, &throttle_module
 2958     );
 2959 
 2960     if (config == (t_config *) 0)
 2961         return &dummy_config;
 2962 
 2963     return config;
 2964 }
 2965 
 2966 /*
 2967  * Specify content-type of throttle status page requests or apply policies
 2968  * specified by ThrottleUser and ThrottlePolicy for all other requests.
 2969  */
 2970 static int
 2971 mime_handler(request_rec *r)
 2972 {
 2973     int rc;
 2974     const char *arg;
 2975     t_config *user, *host;
 2976 
 2977     /* Only specify the content-type of our throttle status pages. */
 2978     if (ap_table_get(r->notes, is_throttle_handler) == true) {
 2979         /* On a previous subrequest we found out what our
 2980          * handler would be from mod_mime. Restore that handler
 2981          * here, since mod_mime will be bypassed.
 2982          */
 2983         r->handler = ap_table_get(r->notes, request_handler);
 2984 
 2985         /* Determine the response's content-type. */
 2986         arg = ap_table_get(r->notes, "content-type");
 2987         if (arg == (const char *) 0)
 2988             r->content_type = content_type;
 2989         else if (arg == text_plain)
 2990             r->content_type = text_plain;
 2991         else
 2992             r->content_type = text_html;
 2993 
 2994         return OK;
 2995     }
 2996 
 2997     if (!ap_is_initial_req(r))
 2998         return DECLINED;
 2999 
 3000     /* Get configurations for ThrottleUser and/or ThrottlePolicy. */
 3001     user = get_config_by_uid(r->finfo.st_uid);
 3002     host = get_config_by_dir(r);
 3003 
 3004     /* Count concurrent requests & perform adjustments. */
 3005     (void) critical_acquire(critical);
 3006 
 3007     user->track->active++;
 3008     if (user->period < (r->request_time - user->track->start))
 3009         (*user->policy->adjust)(r, user);
 3010 
 3011     host->track->active++;
 3012     if (host->period < (r->request_time - host->track->start))
 3013         (*host->policy->adjust)(r, host);
 3014 
 3015     /* Add in the request size now if we can. */
 3016     if (ap_table_get(r->notes, is_file_request) == true) {
 3017         unsigned long kbytes = (r->finfo.st_size + 512) / 1024;
 3018         ap_table_setn(r->notes, volume_not_counted, true);
 3019         user->track->volume += kbytes;
 3020         host->track->volume += kbytes;
 3021     }
 3022 
 3023     (void) critical_release(critical);
 3024 
 3025     /* Apply ThrottleUser and/or ThrottlePolicy. */
 3026     rc = (*user->policy->apply)(r, user);
 3027     if (ap_is_HTTP_ERROR(rc))
 3028         return rc;
 3029 
 3030     return (*host->policy->apply)(r, host);
 3031 }
 3032 
 3033 static int
 3034 fixup_handler(request_rec *r)
 3035 {
 3036     unsigned int rsec;
 3037     char *key, *value, *arg, *view;
 3038 
 3039     if (!ap_is_initial_req(r))
 3040         return DECLINED;
 3041 
 3042     if (ap_table_get(r->notes, is_throttle_handler) != true)
 3043         return DECLINED;
 3044 
 3045     /* Response's refresh time. */
 3046     if ((arg = (char *) ap_table_get(r->notes, "refresh")) == (char *) 0)
 3047         rsec = refresh;
 3048     else
 3049         rsec = (unsigned int) strtol(arg, (char **) 0, 10);
 3050 
 3051     arg = ap_psprintf(r->pool, "%u", rsec);
 3052     ap_table_setn(r->notes, "refresh", arg);
 3053     if (0 < rsec)
 3054         ap_table_setn(r->headers_out, "Refresh", arg);
 3055 
 3056     /* The throttle-me handler is intend for induhvidual users,
 3057      * who are not allowed to preform any commands what so ever.
 3058      */
 3059     if (ap_strcmp_match(r->handler, throttle_me_str) == 0)
 3060         return OK;
 3061 
 3062     /* The view is specified when one display handler links to another
 3063      * without being able to change the URI used to build the URL. We
 3064      * cannot change the URI, because we don't know if the handler
 3065      * has been declared in httpd.conf nor the location assigned to it.
 3066      */
 3067     if ((arg = (char *) ap_table_get(r->notes, "view")) != (char *) 0) {
 3068         if (ap_strcasecmp_match(arg, view_status) == 0)
 3069             r->handler = throttle_status_str;
 3070 #ifdef THROTTLE_CLIENT_IP
 3071         else if (ap_strcasecmp_match(arg, view_client_ip) == 0)
 3072             r->handler = throttle_client_ip_str;
 3073 #endif
 3074 #ifdef THROTTLE_REMOTE_USER
 3075         else if (ap_strcasecmp_match(arg, view_remote_user) == 0)
 3076             r->handler = throttle_remote_user_str;
 3077 #endif
 3078     }
 3079 
 3080 #ifdef THROTTLE_CLIENT_IP
 3081     if (ap_strcmp_match(r->handler, throttle_client_ip_str) == 0)
 3082         view = (char *) view_client_ip;
 3083     else
 3084 #endif
 3085 #ifdef THROTTLE_REMOTE_USER
 3086     if (ap_strcmp_match(r->handler, throttle_remote_user_str) == 0)
 3087         view = (char *) view_remote_user;
 3088     else
 3089 #endif
 3090         view = (char *) view_status;
 3091 
 3092     /* Any commands to process? */
 3093     if ((value = (char *) ap_table_get(r->notes, "command")) == (char *) 0)
 3094         return OK;
 3095 
 3096     /* Lookup command. */
 3097     key = ap_getword_nc(r->pool, &value, ',');
 3098 
 3099     if (ap_strcasecmp_match(key, "preserve") == 0) {
 3100         (void) cmd_preserve(r->pool, value);
 3101     } else if (ap_strcasecmp_match(key, "restore") == 0) {
 3102         (void) cmd_restore(r->pool, value);
 3103     } else if (ap_strcasecmp_match(key, "reset") == 0) {
 3104 #ifdef THROTTLE_CLIENT_IP
 3105         if (view == view_client_ip)
 3106             reset_client_ip(client_ip_pool, value, r->request_time);
 3107         else
 3108 #endif
 3109 #ifdef THROTTLE_REMOTE_USER
 3110         if (view == view_remote_user)
 3111             reset_remote_user(remote_user_pool, value, r->request_time);
 3112         else
 3113 #endif
 3114             reset_info_match(value, r->request_time);
 3115     } else if (ap_strcasecmp_match(key, "restart") == 0) {
 3116         /* Restart the parent & children. */
 3117     } else if (ap_strcasecmp_match(key, "shutdown") == 0) {
 3118         /* Terminate the parent & children cleanly. */
 3119     }
 3120 
 3121     /* Once we have processed the command, we have to do a redirection
 3122      * that the client browser doesn't repeatedly refresh and redo the
 3123      * the command.
 3124      */
 3125     arg = ap_psprintf(
 3126         r->pool, "%s?content-type=%s&refresh=%u&view=%s",
 3127         r->uri, r->content_type, rsec, view
 3128     );
 3129     r->content_type = text_html;
 3130     arg = ap_construct_url(r->pool, arg, r);
 3131     ap_table_setn(r->headers_out, "Location", arg);
 3132     ap_table_setn(r->notes, volume_not_counted, true);
 3133     ap_table_setn(r->notes, request_not_counted, true);
 3134 
 3135     return HTTP_MOVED_PERMANENTLY;
 3136 }
 3137 
 3138 /*
 3139  * Track the total number of kbytes sent.
 3140  */
 3141 int
 3142 log_handler(request_rec *r)
 3143 {
 3144 #ifdef THROTTLE_CLIENT_IP
 3145     t_visitor *client_ip;
 3146 #endif
 3147 #ifdef THROTTLE_REMOTE_USER
 3148     t_visitor *remote_user;
 3149 #endif
 3150     unsigned long kbytes;
 3151     t_config *user, *host;
 3152 
 3153 #ifdef NDEBUG
 3154     /* Don't count the throttle status pages in the stats. */
 3155     if (ap_table_get(r->notes, is_throttle_handler) == true)
 3156         return DECLINED;
 3157 #endif
 3158 
 3159     if (!ap_is_initial_req(r))
 3160         return DECLINED;
 3161 
 3162     /* In the case of internal redirects, from directory to index.html,
 3163      * the final sub-request should be the actual number of kbytes sent.
 3164      */
 3165     while (r->next != (request_rec *) 0)
 3166         r = r->next;
 3167 
 3168 #ifndef NDEBUG
 3169     ap_log_rerror(
 3170         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 3171         "(%ld) log_handler bytes_sent=%lu, request=%s",
 3172         (long) getpid(), r->bytes_sent, r->the_request
 3173     );
 3174 #endif
 3175 
 3176     kbytes = (r->bytes_sent + 512) / 1024;
 3177 
 3178     /* We have four levels of throttles to update: client-ip,
 3179      * remote-user, local-user, server/location.
 3180      */
 3181     user = get_config_by_uid(r->finfo.st_uid);
 3182 
 3183     host = get_config_by_dir(r);
 3184 
 3185     (void) critical_acquire(critical);
 3186 
 3187 #ifdef THROTTLE_CLIENT_IP
 3188     client_ip = get_client_ip(client_ip_pool, r->connection->remote_addr.sin_addr);
 3189 #endif
 3190 #ifdef THROTTLE_REMOTE_USER
 3191     remote_user = get_remote_user(remote_user_pool, r->connection->user);
 3192     if (remote_user == (t_visitor *) 0)
 3193         remote_user = &dummy_visitor;
 3194 #endif
 3195 
 3196     /* Policies can use notes to control whether or not to count
 3197      * bytes_sent and/or requests received. Intended for certain
 3198      * policies that exclude certain types of requests from the
 3199      * stats.
 3200      */
 3201 
 3202     if (ap_table_get(r->notes, volume_not_counted) != true) {
 3203         host->track->volume += kbytes;
 3204         user->track->volume += kbytes;
 3205 #ifdef THROTTLE_REMOTE_USER
 3206         remote_user->volume += kbytes;
 3207 #endif
 3208 #ifdef THROTTLE_CLIENT_IP
 3209         client_ip->volume += kbytes;
 3210 #endif
 3211     }
 3212 
 3213     if (ap_table_get(r->notes, request_not_counted) != true) {
 3214         host->track->requests++;
 3215         user->track->requests++;
 3216 #ifdef THROTTLE_REMOTE_USER
 3217         remote_user->requests++;
 3218 #endif
 3219 #ifdef THROTTLE_CLIENT_IP
 3220         client_ip->requests++;
 3221 #endif
 3222     }
 3223 
 3224     host->track->active--;
 3225     host->track->last = r->request_time;
 3226 
 3227     user->track->active--;
 3228     user->track->last = r->request_time;
 3229 
 3230 #ifdef THROTTLE_REMOTE_USER
 3231     remote_user->last = r->request_time;
 3232 #endif
 3233 #ifdef THROTTLE_CLIENT_IP
 3234     client_ip->last = r->request_time;
 3235 #endif
 3236 
 3237     (void) critical_release(critical);
 3238 
 3239     return DECLINED;
 3240 }
 3241 
 3242 /***********************************************************************
 3243  ***  Content Handlers
 3244  ***********************************************************************/
 3245 
 3246 static const char header[] =
 3247     "<html>\n<head>\n<title>%s - %s</title>\n"
 3248     "<style type=\"text/css\">\n"
 3249     ".small { font-family: sans-serif; font-size: 8pt }\n"
 3250     ".normal, th { font-family: sans-serif; font-size: 10pt }\n"
 3251     ".big, h2 { font-family: sans-serif; font-size: 14pt }\n"
 3252     ".green { color: #00dd00; font-family: sans-serif; font-size: 10pt; font-weight: bold }\n"
 3253     ".yellow { color: #ff9900; font-family: sans-serif; font-size: 10pt; font-weight: bold }\n"
 3254     ".red { color: #cc0000; font-family: sans-serif; font-size: 10pt; font-weight: bold }\n"
 3255     ".critical { color: #ff0000; font-family: sans-serif; font-size: 10pt; font-weight: bold }\n"
 3256     "</style>\n</head>\n"
 3257     "<body bgcolor=\"#ffffff\" text=\"#000000\" class=\"normal\">\n"
 3258     "<center>\n"
 3259 ;
 3260 static const char footer[] =
 3261     "<p class=\"small\">" MODULE "/" VERSION
 3262     "<br>Copyright 1999, 2000 by <a href=\"mailto:"
 3263     AUTHOR "?subject=" MODULE "/" VERSION
 3264     "\">Anthony Howe</a>.  All rights reserved."
 3265     "</p>\n</center>\n</body>\n</html>\n"
 3266 ;
 3267 
 3268 static void
 3269 status_html_header(request_rec *r)
 3270 {
 3271     const char *url;
 3272 
 3273     url = ap_psprintf(
 3274         r->pool, "%s?content-type=text/html&refresh=%s",
 3275         r->uri, ap_table_get(r->notes, "refresh")
 3276     );
 3277     url = ap_construct_url(r->pool, url, r);
 3278 
 3279     ap_rprintf(r, header, ap_get_server_name(r), "Throttle Status");
 3280 
 3281     ap_rprintf(
 3282         r,
 3283         "<table width=\"100%\">\n<tr valign=\"middle\">\n"
 3284         "<th align=\"left\"><h2>%s</h2></th>\n"
 3285         "<th><h2>Server Uptime&nbsp;&nbsp;&nbsp;%s</h2></th>\n"
 3286         "<th align=\"right\"><h2>Throttle Status</h2></th>\n"
 3287         "</tr>\n</table>\n"
 3288 
 3289         "<table cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n"
 3290         "<tr valign=\"bottom\">\n"
 3291             "\t<td colspan=\"2\" class=\"small\">",
 3292         ap_get_server_name(r),
 3293         elapsed_time(r->pool, r->request_time - ap_restart_time)
 3294     );
 3295 
 3296     /* Only the full status display gets the command links. */
 3297     if (ap_strcmp_match(r->handler, throttle_me_str) != 0) {
 3298         ap_rprintf(
 3299             r,
 3300             "<a href=\"%s&view=status&command=reset,*\">Reset All</a>&nbsp;&nbsp;&nbsp;"
 3301             "<a href=\"%s&view=status&command=preserve\">Preserve</a>&nbsp;&nbsp;&nbsp;"
 3302             "<a href=\"%s&view=status&command=restore\">Restore</a>&nbsp;&nbsp;&nbsp;",
 3303              url, url, url
 3304         );
 3305 #ifdef THROTTLE_CLIENT_IP
 3306         if (0 < client_ip_size)
 3307             ap_rprintf(
 3308                 r,
 3309                 "<a href=\"%s&view=client-ip\">Client-IP</a>&nbsp;&nbsp;&nbsp;",
 3310                 url
 3311             );
 3312 #endif
 3313 #ifdef THROTTLE_REMOTE_USER
 3314         if (0 < remote_user_size)
 3315             ap_rprintf(
 3316                 r,
 3317                 "<a href=\"%s&view=remote-user\">Remote-User</a>&nbsp;&nbsp;&nbsp;",
 3318                 url
 3319             );
 3320 #endif
 3321     }
 3322 
 3323     ap_rprintf(
 3324         r,
 3325             "</td>\n"
 3326             "\t<th>%%</th>\n"
 3327 
 3328             "\t<th>Hits</th>\n"
 3329             "\t<th>Refused</th>\n"
 3330             "\t<th>KBytes<br>sent</th>\n"
 3331             "\t<th>KBytes<br>per hit</th>\n"
 3332             "\t<th>Delay<br>(&lt;=%d)</th>\n"
 3333 
 3334             "\t<th>Policy</th>\n"
 3335             "\t<th>Limit</th>\n"
 3336             "\t<th>Period</th>\n"
 3337             "\t<th>Period<br>Elapsed</th>\n"
 3338             "\t<th>Idle<br>Time</th>\n"
 3339         "</tr>\n",
 3340         max_delay
 3341     );
 3342 }
 3343 
 3344 static void
 3345 status_html_line(request_rec *r, t_config *config, int row)
 3346 {
 3347     int i, percent;
 3348     const char *url, *level;
 3349 
 3350     /* Compute the policy specific percentage of the limit. */
 3351     percent = (*config->policy->percent)(config);
 3352 
 3353     for (i = 0; i < ALERT_LEVELS-1; ++i) {
 3354         if (percent < alert[i])
 3355             break;
 3356     }
 3357     level = alert_names[i];
 3358 
 3359     /* Start table row, alternating background colour. */
 3360     ap_rprintf(
 3361         r, "<tr align=\"right\"%s>\n",
 3362         (row & 1) ? " bgcolor=\"#eeeeff\"" : ""
 3363     );
 3364 
 3365     /* Display row number. */
 3366     if (ap_strcmp_match(r->handler, throttle_me_str) == 0) {
 3367         ap_rprintf(r, "<td class=\"normal\">%d.&nbsp;</td>\n", row);
 3368     } else {
 3369         url = ap_psprintf(
 3370             r->pool, "%s?content-type=text/html&refresh=%s&view=status&command=reset,%s",
 3371             r->uri, ap_table_get(r->notes, "refresh"), config->name
 3372         );
 3373         url = ap_construct_url(r->pool, url, r);
 3374         ap_rprintf(r, "<td class=\"normal\"><a href=\"%s\">%d.</a>&nbsp;</td>\n", url, row);
 3375     }
 3376 
 3377     /* Display server, directory, or user being throttled. */
 3378     ap_rprintf(r, "<td align=\"left\" class=\"normal\">");
 3379     if (config->uid == (uid_t) -2 || ap_os_is_path_absolute(config->name)) {
 3380         ap_rprintf(r, config->name);
 3381     } else if (config->uid != UNSET) {
 3382         url = ap_psprintf(r->pool, "/~%s/", config->name);
 3383         url = ap_construct_url(r->pool, url, r);
 3384         ap_rprintf(r, "<a href=\"%s\">%s</a>", url, config->name);
 3385     } else {
 3386         ap_rprintf(
 3387             r, "<a href=\"http://%s:%d/\">%s</a>",
 3388             config->name, config->server->port, config->name
 3389         );
 3390     }
 3391     ap_rprintf(r, "</td>\n");
 3392 
 3393     /* Display stats. */
 3394     ap_rprintf(
 3395         r,
 3396         "<td class=\"%s\">%u</td>\n"
 3397 
 3398         "<td class=\"%s\">%lu</td>\n"
 3399         "<td class=\"%s\">%lu</td>\n"
 3400         "<td class=\"%s\">%lu</td>\n"
 3401         "<td class=\"%s\">%lu</td>\n"
 3402         "<td class=\"%s\">%u</td>\n"
 3403 
 3404         "<td class=\"%s\">%s</td>\n"
 3405         "<td class=\"%s\">%s</td>\n"
 3406         "<td class=\"%s\">%s</td>\n"
 3407         "<td class=\"%s\">%s</td>\n"
 3408         "<td class=\"%s\">%s</td>\n",
 3409         level, percent,
 3410 
 3411         level, config->track->requests,
 3412         level, config->track->refused,
 3413         level, config->track->volume,
 3414         level, 0 < config->track->requests
 3415             ? config->track->volume / config->track->requests
 3416             : 0,
 3417         level, config->track->delay,
 3418 
 3419         level, config->policy->name,
 3420         level, byte_size(r->pool, config->limit),
 3421         level, time_period(r->pool, config->period),
 3422         level, elapsed_time(r->pool, r->request_time - config->track->start),
 3423         level, elapsed_time(
 3424             r->pool,
 3425             config->track->last < r->request_time
 3426                 ? r->request_time - config->track->last
 3427                 : 0
 3428         )
 3429     );
 3430 
 3431     /* End table row. */
 3432     ap_rprintf(r, "</tr>\n");
 3433 }
 3434 
 3435 static void
 3436 status_html_footer(request_rec *r)
 3437 {
 3438     ap_rprintf(r, "</table>\n");
 3439     ap_rprintf(r, footer);
 3440 }
 3441 
 3442 static void
 3443 status_text_line(request_rec *r, t_config *config, int row)
 3444 {
 3445     ap_rprintf(
 3446         r,
 3447         "%u. %s"
 3448         "\t%u"
 3449 
 3450         "\t%lu"
 3451         "\t%lu"
 3452         "\t%lu"
 3453         "\t%lu"
 3454         "\t%u"
 3455 
 3456         "\t%s"
 3457         "\t%s"
 3458         "\t%s"
 3459         "\t%s"
 3460         "\t%s"
 3461         "\r\n",
 3462         row, config->name,
 3463         config->policy->percent(config),
 3464 
 3465         config->track->requests,
 3466         config->track->refused,
 3467         config->track->volume,
 3468         0 < config->track->requests
 3469             ? config->track->volume / config->track->requests
 3470             : 0,
 3471         config->track->delay,
 3472 
 3473         config->policy->name,
 3474         byte_size(r->pool, config->limit),
 3475         time_period(r->pool, config->period),
 3476         elapsed_time(
 3477             r->pool, r->request_time - config->track->start
 3478         ),
 3479         elapsed_time(
 3480             r->pool,
 3481             config->track->last < r->request_time
 3482                 ? r->request_time - config->track->last
 3483                 : 0
 3484         )
 3485     );
 3486 }
 3487 
 3488 static void
 3489 status_footer_text(request_rec *r)
 3490 {
 3491     /* Do nothing. */
 3492 }
 3493 
 3494 static int
 3495 server_status(request_rec *r)
 3496 {
 3497     int rc, row;
 3498     const char *arg;
 3499     t_config *config;
 3500 
 3501     if (!ap_is_initial_req(r))
 3502         return DECLINED;
 3503 
 3504 #ifndef NDEBUG
 3505     ap_log_rerror(
 3506         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 3507         "(%ld) server_status server=%s, request=%s",
 3508         (long) getpid(), ap_get_server_name(r), r->the_request
 3509     );
 3510 #endif
 3511 
 3512     if ((rc = ap_discard_request_body(r)) != OK)
 3513         return rc;
 3514 
 3515     ap_send_http_header(r);
 3516 
 3517     if (r->header_only)
 3518         return OK;
 3519 
 3520     if (r->content_type != text_plain)
 3521         status_html_header(r);
 3522 
 3523     row = 1;
 3524 
 3525     for (config = stack_top; config != (t_config *) 0; config = config->next, ++row) {
 3526         if (config->track == (t_throttle *) 0)
 3527             continue;
 3528 
 3529         if (r->content_type == text_plain)
 3530             status_text_line(r, config, row);
 3531         else
 3532             status_html_line(r, config, row);
 3533     }
 3534 
 3535     if (r->content_type != text_plain)
 3536         status_html_footer(r);
 3537 
 3538     return OK;
 3539 }
 3540 
 3541 /*
 3542  * Generate an individual throttle status for either a ~user
 3543  * or the server under which the request was made. For example
 3544  * if httpd.conf is configured with:
 3545  *
 3546  *  <Location /throttle-me>
 3547  *  SetHandler throttle-me
 3548  *  </Location>
 3549  *
 3550  * Then a URL of the form "http://my.domain.com/throttle-me" will
 3551  * display the throttle status of "my.domain.com", if it has a
 3552  * ThrottlePolicy or return HTTP_NOT_FOUND.
 3553  */
 3554 static int
 3555 me_status(request_rec *r)
 3556 {
 3557     int rc;
 3558     t_config *config;
 3559 
 3560     if (!ap_is_initial_req(r))
 3561         return DECLINED;
 3562 
 3563 #ifndef NDEBUG
 3564     ap_log_rerror(
 3565         APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
 3566         "(%ld) me_status server=%s, request=%s",
 3567         (long) getpid(), ap_get_server_name(r), r->the_request
 3568     );
 3569 #endif
 3570 
 3571     if ((rc = ap_discard_request_body(r)) != OK)
 3572         return rc;
 3573 
 3574     if (r->uri[0] == '/' && r->uri[1] == '~' && ap_isalnum(r->uri[2])) {
 3575         /* Lookup user throttle. */
 3576         uid_t uid;
 3577         const char *user, *remainder;
 3578 
 3579         remainder = r->uri + 2;
 3580         user = ap_getword(r->pool, &remainder, '/');
 3581 
 3582         if ((uid = uname2id(user)) == (uid_t) -1)
 3583             return HTTP_NOT_FOUND;
 3584 
 3585         config = get_config_by_uid(uid);
 3586     } else {
 3587         /* Lookup server throttle. */
 3588         for (config = stack_top; config != (t_config *) 0; config = config->next) {
 3589             if (config->name == r->server->server_hostname)
 3590                 break;
 3591         }
 3592 
 3593         if (config == (t_config *) 0)
 3594             return HTTP_NOT_FOUND;
 3595     }
 3596 
 3597     ap_send_http_header(r);
 3598 
 3599     if (r->header_only)
 3600         return OK;
 3601 
 3602     if (r->content_type == text_plain) {
 3603         status_text_line(r, config, 1);
 3604     } else {
 3605         status_html_header(r);
 3606         status_html_line(r, config, 1);
 3607         status_html_footer(r);
 3608     }
 3609 
 3610     return OK;
 3611 }
 3612 
 3613 #if defined(THROTTLE_CLIENT_IP) || defined(THROTTLE_REMOTE_USER)
 3614 
 3615 static void
 3616 general_html_header(request_rec *r)
 3617 {
 3618     t_config *config;
 3619     const char *url, *title, *view;
 3620 
 3621     url = ap_psprintf(
 3622         r->pool, "%s?content-type=text/html&refresh=%s",
 3623         r->uri, ap_table_get(r->notes, "refresh")
 3624     );
 3625     url = ap_construct_url(r->pool, url, r);
 3626 
 3627 #ifdef THROTTLE_CLIENT_IP
 3628     if (r->handler == throttle_client_ip_str) {
 3629         config = &client_ip_config;
 3630         view = view_client_ip;
 3631     }
 3632 #endif
 3633 #ifdef THROTTLE_REMOTE_USER
 3634     if (r->handler == throttle_remote_user_str) {
 3635         config = &remote_user_config;
 3636         view = view_remote_user;
 3637     }
 3638 #endif
 3639 
 3640     ap_rprintf(r, header, ap_get_server_name(r), view);
 3641 
 3642     ap_rprintf(
 3643         r,
 3644         "<table width=\"100%\">\n<tr valign=\"middle\">\n"
 3645         "<th align=\"left\"><h2>%s</h2></th>\n"
 3646         "<th><h2>Policy: %s&nbsp;&nbsp;&nbsp;&nbsp;Limit: %s&nbsp;&nbsp;&nbsp;&nbsp;Period: %s</h2></th>\n"
 3647         "<th align=\"right\"><h2>%s</h2></th>\n"
 3648         "</tr>\n</table>\n"
 3649 
 3650         "<table cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n"
 3651         "<tr valign=\"bottom\">\n"
 3652             "\t<td colspan=\"2\" class=\"small\">",
 3653         ap_get_server_name(r),
 3654         config->policy->name,
 3655         byte_size(r->pool, config->limit),
 3656         time_period(r->pool, config->period),
 3657         view
 3658     );
 3659 
 3660     /* Only the full status display gets the command links. */
 3661     ap_rprintf(
 3662         r,
 3663         "<a href=\"%s&view=%s&command=reset,*\">Reset All</a>&nbsp;&nbsp;&nbsp;"
 3664         "<a href=\"%s&view=status\">Status</a>&nbsp;&nbsp;&nbsp;",
 3665         url, view, url
 3666     );
 3667 
 3668 #if defined(THROTTLE_CLIENT_IP) && defined(THROTTLE_REMOTE_USER)
 3669     if (r->handler == throttle_client_ip_str) {
 3670         ap_rprintf(
 3671             r,
 3672             "<a href=\"%s&view=remote-user\">Remote-User</a>&nbsp;&nbsp;&nbsp;",
 3673             url
 3674         );
 3675     }
 3676     if (r->handler == throttle_remote_user_str) {
 3677         ap_rprintf(
 3678             r,
 3679             "<a href=\"%s&view=client-ip\">Client-IP</a>&nbsp;&nbsp;&nbsp;",
 3680             url
 3681         );
 3682     }
 3683 #endif
 3684 
 3685     ap_rprintf(
 3686         r,
 3687             "</td>\n"
 3688             "\t<th>%%</th>\n"
 3689             "\t<th>Requests</th>\n"
 3690             "\t<th>Refused</th>\n"
 3691             "\t<th>KBytes<br>Sent</th>\n"
 3692             "\t<th>KBytes<br>per hit</th>\n"
 3693             "\t<th>Delay<br>(&lt;=%d)</th>\n"
 3694             "\t<th>Period<br>Elapsed</th>\n"
 3695             "\t<th>Idle<br>Time</th>\n"
 3696         "</tr>\n",
 3697         max_delay
 3698     );
 3699 }
 3700 
 3701 static void
 3702 general_html_line(request_rec *r, t_visitor *v, int row)
 3703 {
 3704     int i, percent;
 3705     t_config *config;
 3706     const char *url, *visitor, *view, *level;
 3707 
 3708 #ifdef THROTTLE_CLIENT_IP
 3709     if (r->handler == throttle_client_ip_str) {
 3710         config = &client_ip_config;
 3711         visitor = inet_ntoa(v->remote.ip);
 3712         view = view_client_ip;
 3713     }
 3714 #endif
 3715 #ifdef THROTTLE_REMOTE_USER
 3716     if (r->handler == throttle_remote_user_str) {
 3717         config = &remote_user_config;
 3718         visitor = v->remote.user;
 3719         view = view_remote_user;
 3720     }
 3721 #endif
 3722 
 3723     /* Compute the policy specific percentage of the limit. */
 3724     config->track = (t_throttle *) v;
 3725     percent = (*config->policy->percent)(config);
 3726 
 3727     for (i = 0; i < ALERT_LEVELS-1; ++i) {
 3728         if (percent < alert[i])
 3729             break;
 3730     }
 3731     level = alert_names[i];
 3732 
 3733     /* Start table row, alternating background colour. */
 3734     ap_rprintf(
 3735         r, "<tr align=\"right\"%s>\n",
 3736         (row & 1) ? " bgcolor=\"#eeeeff\"" : ""
 3737     );
 3738 
 3739     /* Display row number. */
 3740     url = ap_psprintf(
 3741         r->pool, "%s?content-type=text/html&refresh=%s&view=%s&command=reset,%s",
 3742         r->uri, ap_table_get(r->notes, "refresh"), view, visitor
 3743     );
 3744     url = ap_construct_url(r->pool, url, r);
 3745     ap_rprintf(r, "<td class=\"normal\"><a href=\"%s\">%d.</a>&nbsp;</td>\n", url, row);
 3746 
 3747     ap_rprintf(r, "<td align=\"left\" class=\"normal\">%s</td>\n", visitor);
 3748 
 3749     /* Display stats. */
 3750     ap_rprintf(
 3751         r,
 3752         "<td class=\"%s\">%u</td>\n"
 3753 
 3754         "<td class=\"%s\">%lu</td>\n"
 3755         "<td class=\"%s\">%lu</td>\n"
 3756         "<td class=\"%s\">%lu</td>\n"
 3757         "<td class=\"%s\">%lu</td>\n"
 3758         "<td class=\"%s\">%u</td>\n"
 3759 
 3760         "<td class=\"%s\">%s</td>\n"
 3761         "<td class=\"%s\">%s</td>\n",
 3762         level, percent,
 3763 
 3764         level, v->requests,
 3765         level, v->refused,
 3766         level, v->volume,
 3767         level, 0 < v->requests ? v->volume / v->requests : 0,
 3768         level, v->delay,
 3769 
 3770         level, elapsed_time(r->pool, r->request_time - v->start),
 3771         level, elapsed_time(r->pool, r->request_time - v->last)
 3772     );
 3773 }
 3774 
 3775 static void
 3776 general_text_line(request_rec *r, t_visitor *v, int row)
 3777 {
 3778     t_config *config;
 3779     const char *visitor;
 3780 
 3781 #ifdef THROTTLE_CLIENT_IP
 3782     if (r->handler == throttle_client_ip_str) {
 3783         visitor = inet_ntoa(v->remote.ip);
 3784         config = &client_ip_config;
 3785     }
 3786 #endif
 3787 #ifdef THROTTLE_REMOTE_USER
 3788     if (r->handler == throttle_remote_user_str) {
 3789         visitor = v->remote.user;
 3790         config = &remote_user_config;
 3791     }
 3792 #endif
 3793     config->track = (t_throttle *) v;
 3794 
 3795     ap_rprintf(
 3796         r,
 3797         "%u. %s"
 3798         "\t%u"
 3799 
 3800         "\t%lu"
 3801         "\t%lu"
 3802         "\t%lu"
 3803         "\t%u"
 3804 
 3805         "\t%s"
 3806         "\t%s"
 3807         "\r\n",
 3808         row,
 3809         visitor,
 3810         config->policy->percent(config),
 3811 
 3812         v->requests,
 3813         v->refused,
 3814         v->volume,
 3815         v->delay,
 3816 
 3817         elapsed_time(r->pool, r->request_time - v->start),
 3818         elapsed_time(r->pool, r->request_time - v->last)
 3819     );
 3820 }
 3821 
 3822 static int
 3823 visitor_status(request_rec *r)
 3824 {
 3825     int rc, row = 0;
 3826     t_visitor *v;
 3827     t_visitors *vp;
 3828 
 3829     if (!ap_is_initial_req(r))
 3830         return DECLINED;
 3831 
 3832     if ((rc = ap_discard_request_body(r)) != OK)
 3833         return rc;
 3834 
 3835     ap_send_http_header(r);
 3836 
 3837     if (r->header_only)
 3838         return OK;
 3839 
 3840 #ifdef THROTTLE_CLIENT_IP
 3841     if (r->handler == throttle_client_ip_str) {
 3842         vp = client_ip_pool;
 3843     }
 3844 #endif
 3845 #ifdef THROTTLE_REMOTE_USER
 3846     if (r->handler == throttle_remote_user_str) {
 3847         vp = remote_user_pool;
 3848     }
 3849 #endif
 3850 
 3851     if (r->content_type == text_plain) {
 3852         for (v = vp->head; v != (t_visitor *) 0; v = v->next) {
 3853             if (vp->used <= row)
 3854                 break;
 3855             general_text_line(r, v, ++row);
 3856         }
 3857     } else {
 3858         general_html_header(r);
 3859         for (v = vp->head; v != (t_visitor *) 0; v = v->next) {
 3860             if (vp->used <= row)
 3861                 break;
 3862             general_html_line(r, v, ++row);
 3863         }
 3864         status_html_footer(r);
 3865     }
 3866 
 3867     return OK;
 3868 }
 3869 #endif
 3870 
 3871 static handler_rec content_handlers[] = {
 3872     { throttle_me_str, me_status },
 3873     { throttle_info_str, server_status },
 3874     { throttle_status_str, server_status },
 3875 #ifdef THROTTLE_CLIENT_IP
 3876     { throttle_client_ip_str, visitor_status },
 3877 #endif
 3878 #ifdef THROTTLE_REMOTE_USER
 3879     { throttle_remote_user_str, visitor_status },
 3880 #endif
 3881     { NULL }
 3882 };
 3883 
 3884 static command_rec command_table[] = {
 3885 #ifdef THROTTLE_CLIENT_IP
 3886     { "ThrottleClientIP", throttle_client_ip, NULL, RSRC_CONF, RAW_ARGS,
 3887     "Specify global throttle pool size, policy, limit, and period for all client IP connections." },
 3888 #endif
 3889     { "ThrottleContentType", throttle_content_type, NULL, RSRC_CONF, TAKE1,
 3890     "Content-Type of throttle display, or use /throttle-status?content-type=text/plain" },
 3891 
 3892     { "ThrottleIndicator", throttle_indicator, NULL, RSRC_CONF, TAKE2,
 3893     "Set % threshold for green, yellow, red, and critical alerts." },
 3894 
 3895     { "ThrottleLockFile", throttle_lock_file, NULL, RSRC_CONF, TAKE1,
 3896     "The lock file used with fcntl() or flock() serialization. Must be stored on a local disk." },
 3897 
 3898     { "ThrottleMaxDelay", throttle_max_delay, NULL, RSRC_CONF, TAKE1,
 3899     "Set max. delay threshold in seconds before refusing connections." },
 3900 
 3901     { "ThrottlePolicy", throttle_policy, NULL, RSRC_CONF|ACCESS_CONF, RAW_ARGS,
 3902     "Select policy, limit, and period." },
 3903 
 3904     { "ThrottleRefresh", throttle_refresh, NULL, RSRC_CONF, TAKE1,
 3905     "Refresh time in seconds, or use /throttle-status?refresh=sec" },
 3906 
 3907 #ifdef THROTTLE_REMOTE_USER
 3908     { "ThrottleRemoteUser", throttle_remote_user, NULL, RSRC_CONF, RAW_ARGS,
 3909     "Specify global throttle pool size, policy, limit, and period for all authenticated remote users." },
 3910 #endif
 3911     { "ThrottleRuntimeFile", throttle_runtime_file, NULL, RSRC_CONF, TAKE1,
 3912     "The runtime data file used across shutdowns and restarts." },
 3913 
 3914     { "ThrottleUser", throttle_user, NULL, RSRC_CONF, RAW_ARGS,
 3915     "Specify user to throttle according to policy, limit, and period." },
 3916 
 3917     { NULL }
 3918 };
 3919 
 3920 module MODULE_VAR_EXPORT throttle_module = {
 3921     STANDARD_MODULE_STUFF,
 3922     init_module,        /* module initializer                  */
 3923     create_dir_config,  /* create per-dir    config structures */
 3924     merge_dir_config,   /* merge  per-dir    config structures */
 3925     NULL,           /* create per-server config structures */
 3926     NULL,           /* merge  per-server config structures */
 3927     command_table,      /* table of config file commands       */
 3928     content_handlers,   /* [#8] MIME-typed-dispatched handlers */
 3929     uri_handler,        /* [#1] URI to filename translation    */
 3930     authentication_handler, /* [#4] validate user id from request  */
 3931     authorization_handler,  /* [#5] check if the user is ok _here_ */
 3932     access_handler,     /* [#3] check access by host address   */
 3933     mime_handler,       /* [#6] determine MIME type            */
 3934     fixup_handler,      /* [#7] pre-run fixups                 */
 3935     log_handler,        /* [#9] log a transaction              */
 3936 #if MODULE_MAGIC_NUMBER >= 19970103
 3937     NULL,           /* [#2] header parser                  */
 3938 #endif
 3939 #if MODULE_MAGIC_NUMBER >= 19970719
 3940     child_init,     /* child_init                  */
 3941 #endif
 3942 #if MODULE_MAGIC_NUMBER >= 19970728
 3943     NULL,           /* child_exit                  */
 3944 #endif
 3945 #if MODULE_MAGIC_NUMBER >= 19970902
 3946     NULL            /* [#0] post read-request              */
 3947 #endif
 3948 };