diff -uNr mod_limitipconn-0.03/mod_limitipconn.c mod_limitipconn-patched/mod_limitipconn.c --- mod_limitipconn-0.03/mod_limitipconn.c Tue Mar 27 16:03:27 2001 +++ mod_limitipconn-patched/mod_limitipconn.c Mon Feb 2 19:09:20 2004 @@ -1,5 +1,6 @@ /* * Copyright (C) 2000 David Jao + * "MaxConnPerUid", "MaxConnPerVhost" and "MaxLA*" portions by Maxim Chirkov * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -40,6 +41,13 @@ typedef struct { unsigned int limit; /* max number of connections per IP */ + + unsigned int limit_uid; /* max number of connections per user */ + unsigned int limit_vhost; /* max number of connections per virtual host */ + double limit_la1; /* maximum value of Load Average for 1 min. */ + double limit_la5; /* maximum value of Load Average for 5 min. */ + double limit_la15; /* maximum value of Load Average for 15 min. */ + array_header *no_limit; /* array of MIME types exempt from limit checking */ array_header *excl_limit; /* array of MIME types to limit check; all @@ -53,6 +61,11 @@ /* default configuration: no limit, and both arrays are empty */ cfg->limit = 0; + cfg->limit_uid = 0; + cfg->limit_vhost = 0; + cfg->limit_la1 = 0.0; + cfg->limit_la5 = 0.0; + cfg->limit_la15 = 0.0; cfg->no_limit = ap_make_array(p, 0, sizeof(char *)); cfg->excl_limit = ap_make_array(p, 0, sizeof(char *)); @@ -71,12 +84,25 @@ const char *address; + /* load average */ + double current_la[3]; + /* loop index variable */ int i; /* running count of number of connections from this address */ int ip_count = 0; + /* count of runnung requests for current uid and virtual host*/ + int uid_req_count = 0; + int vhost_req_count = 0; + + /* uid of current virtual host */ + uid_t current_uid = 0; + + /* current virtual host id */ + char *current_vhost = NULL; + /* Content-type of the current request */ const char *content_type; @@ -104,8 +130,44 @@ #endif address = r->connection->remote_ip; +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "DEBUG: %s handler for requiest uri(%s) args(%s) host(%s) uid(%d)",r->handler, r->uri, r->args, r->server->server_hostname, r->server->server_uid); +#endif + + /* Check Load Average overflow */ + if (cfg->limit_la1 != 0 || cfg->limit_la5 != 0 || cfg->limit_la15 != 0){ + + /* Blocking only cgi or php scripts if LA limit exceeded */ + /* Handlers: cgi-script perl-script application/x-httpd-php application/x-httpd-cgi */ + /* Check for cgi and php in r->uri too simple then r->handler */ + if ((r->args != NULL) + || (ap_strcasestr(r->uri, "cgi") != NULL) + || (ap_strcasestr(r->uri, "php") != NULL)){ + + if( getloadavg(current_la, 3) != -1 ){ + + if ((current_la[0] >= cfg->limit_la1) + && (current_la[1] >= cfg->limit_la5) + && (current_la[2] >= cfg->limit_la15)){ + + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Load Average limit exceeded (%.2f, %.2f, %.2f)", + current_la[0], current_la[1], current_la[2]); + ap_log_reason("System exceeded LA limit.", r->uri, r); + + return FORBIDDEN; + } + } + } + } + + /* Get uid of current virual host for future use */ + if (r->server->is_virtual){ + current_uid = r->server->server_uid; + current_vhost = r->server->server_hostname; + } + /* A limit value of 0 by convention means no limit. */ - if (cfg->limit == 0) { + if (cfg->limit == 0 && cfg->limit_uid == 0 && cfg->limit_vhost == 0) { return OK; } @@ -155,6 +217,19 @@ ) { ip_count++; } + if ((score_record.vhostrec != NULL) && + (score_record.vhostrec->is_virtual)){ + + if (score_record.vhostrec->server_uid == current_uid){ + /* Same user */ + uid_req_count++; + } + if ((cfg->limit_vhost != 0) + && (strcmp(score_record.vhostrec->server_hostname, current_vhost) == 0)){ + /* Same host name */ + vhost_req_count++; + } + } break; case SERVER_DEAD: @@ -175,6 +250,14 @@ #endif ap_log_reason("Client exceeded connection limit.", r->uri, r); return FORBIDDEN; + } else if ((uid_req_count > cfg->limit_uid) && (cfg->limit_uid)){ + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Rejecting vhost=%s, uid=%u", r->server->server_hostname, r->server->server_uid); + ap_log_reason("Client exceeded request per user limit.", r->uri, r); + return FORBIDDEN; + } else if ((vhost_req_count > cfg->limit_vhost) && (cfg->limit_vhost)){ + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "Rejecting vhost=%s, uid=%u", r->server->server_hostname, r->server->server_uid); + ap_log_reason("Client exceeded request per user limit.", r->uri, r); + return FORBIDDEN; } else { return OK; } @@ -216,6 +299,88 @@ return NULL; } +/* Parse the MaxConnPerVhost directive */ +static const char *limit_vhost_config_cmd(cmd_parms *parms, void *mconfig, + const char *arg) +{ + limitipconn_dir_config *cfg = (limitipconn_dir_config *) mconfig; + + unsigned long int limit = strtol(arg, (char **) NULL, 10); + + if (limit == LONG_MAX) { + return "Integer overflow or invalid number"; + } + + cfg->limit_vhost = limit; + return NULL; +} + +/* Parse the MaxConnPerUid directive */ +static const char *limit_uid_config_cmd(cmd_parms *parms, void *mconfig, + const char *arg) +{ + limitipconn_dir_config *cfg = (limitipconn_dir_config *) mconfig; + + unsigned long int limit = strtol(arg, (char **) NULL, 10); + + if (limit == LONG_MAX) { + return "Integer overflow or invalid number"; + } + + cfg->limit_uid = limit; + return NULL; +} + +/* Parse the MaxLA1 directive */ +static const char *limit_la1_config_cmd(cmd_parms *parms, void *mconfig, + const char *arg) +{ + limitipconn_dir_config *cfg = (limitipconn_dir_config *) mconfig; + + double limit = strtod(arg, (char **) NULL); + + if (limit < 0.0) { + return "Invalid LA1 value"; + } + + cfg->limit_la1 = limit; + return NULL; +} + +/* Parse the MaxLA5 directive */ +static const char *limit_la5_config_cmd(cmd_parms *parms, void *mconfig, + const char *arg) +{ + limitipconn_dir_config *cfg = (limitipconn_dir_config *) mconfig; + + double limit = strtod(arg, (char **) NULL); + + if (limit < 0.0) { + return "Invalid LA5 value"; + } + + cfg->limit_la5 = limit; + return NULL; +} + + +/* Parse the MaxLA15 directive */ +static const char *limit_la15_config_cmd(cmd_parms *parms, void *mconfig, + const char *arg) +{ + limitipconn_dir_config *cfg = (limitipconn_dir_config *) mconfig; + + double limit = strtod(arg, (char **) NULL); + + if (limit < 0.0) { + return "Invalid LA15 value"; + } + + cfg->limit_la15 = limit; + return NULL; +} + + /* Array describing structure of configuration directives */ static command_rec limitipconn_cmds[] = { {"MaxConnPerIP", limit_config_cmd, NULL, OR_LIMIT, TAKE1, @@ -224,6 +389,16 @@ "MIME types for which limit checking is disabled"}, {"OnlyIPLimit", excl_limit_config_cmd, NULL, OR_LIMIT, ITERATE, "restrict limit checking to these MIME types only"}, + {"MaxConnPerUid", limit_uid_config_cmd, NULL, OR_LIMIT, TAKE1, + "maximum simultaneous connections per user"}, + {"MaxConnPerVhost", limit_vhost_config_cmd, NULL, OR_LIMIT, TAKE1, + "maximum simultaneous connections per virtual host"}, + {"MaxLA1", limit_la1_config_cmd, NULL, OR_LIMIT, TAKE1, + "maximum Load Overage value for the past 1 minute"}, + {"MaxLA5", limit_la5_config_cmd, NULL, OR_LIMIT, TAKE1, + "maximum Load Overage value for the past 5 minutes"}, + {"MaxLA15", limit_la15_config_cmd, NULL, OR_LIMIT, TAKE1, + "maximum Load Overage value for the past 15 minutes"}, {NULL}, };