summaryrefslogtreecommitdiff
blob: 60ac757754135fb90627352a4e8cc713050c332f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
--- mod_limitipconn.c-localip	2005-02-09 16:29:55.525726056 +0100
+++ mod_limitipconn.c	2005-02-09 16:31:44.319186936 +0100
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2000-2002 David Jao <djao@dominia.org>
+ * "MaxConnPerUid", "MaxConnPerVhost" and "MaxLA*" portions by Maxim Chirkov <mc@tyumen.ru>
  *
  * 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
@@ -55,6 +63,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 *));
     cfg->local_ip = ap_make_array(p, 0, sizeof(char *));
@@ -75,12 +88,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;
 
@@ -108,8 +134,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 HTTP_SERVICE_UNAVAILABLE;
+		}
+	    }
+	}
+    }
+
+    /* 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;
     }
 
@@ -168,6 +230,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:
@@ -191,6 +266,14 @@
 	ap_table_setn(r->subprocess_env, "LIMITIP", "1");
 	/* return 503 */
 	return HTTP_SERVICE_UNAVAILABLE;
+    } 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 HTTP_SERVICE_UNAVAILABLE;
+    } 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 vhost limit.", r->uri, r);
+	return HTTP_SERVICE_UNAVAILABLE;
     } else {
 	return OK;
     }
@@ -242,6 +325,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,
@@ -252,6 +417,16 @@
      "restrict limit checking to these MIME types only"},
     {"LocalIP", local_ip_config_cmd, NULL, OR_LIMIT, ITERATE,
      "no checking on local IP"},
+    {"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},
 };