Download | Plain Text | No Line Numbers


  1. /*
  2.  * Copyright (C) 2000-2002 David Jao <djao@dominia.org>
  3.  * "MaxConnPerUid", "MaxConnPerVhost" and "MaxLA*" portions by Maxim Chirkov <mc@tyumen.ru>
  4.  * per VHost settings, advanced content-type shm-cache and check, scoreboard dump,
  5.  * optional KeepAlive-Values, code reorganization and apache2 port by Manuel Mausz <manuel@mausz.at>
  6.  *
  7.  * Permission is hereby granted, free of charge, to any person
  8.  * obtaining a copy of this software and associated documentation
  9.  * files (the "Software"), to deal in the Software without
  10.  * restriction, including without limitation the rights to use, copy,
  11.  * modify, merge, publish, distribute, sublicense, and/or sell copies
  12.  * of the Software, and to permit persons to whom the Software is
  13.  * furnished to do so, subject to the following conditions:
  14.  *
  15.  * The above copyright notice, this permission notice, and the
  16.  * following disclaimer shall be included in all copies or substantial
  17.  * portions of the Software.
  18.  *
  19.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20.  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22.  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  23.  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  24.  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  26.  * DEALINGS IN THE SOFTWARE.
  27.  *
  28.  */
  29.  
  30. #define CORE_PRIVATE
  31. #include "httpd.h"
  32. #include "http_config.h"
  33. #include "http_request.h"
  34. #include "http_protocol.h"
  35. #include "http_core.h"
  36. #include "http_main.h"
  37. #include "http_log.h"
  38. #include "scoreboard.h"
  39. #include <sys/types.h>
  40.  
  41. /* apache2 specific includes */
  42. #include "unixd.h"
  43. #include "ap_mpm.h"
  44. #include "apr_strings.h"
  45.  
  46. #if HAVE_SHMGET || APR_HAS_SHARED_MEMORY
  47. # define CREATE_OWN_SCOREBOARD 1
  48. # include <sys/ipc.h>
  49. # include <sys/shm.h>
  50. #else
  51. # define CREATE_OWN_SCOREBOARD 0
  52. #endif
  53.  
  54. #define MODULE_NAME "mod_limitipconn"
  55. #define MODULE_VERSION "0.21-vhost"
  56. #define SHM_KEY IPC_PRIVATE
  57. //#define DEBUG 1
  58. //#define DUMP 1
  59.  
  60. #ifdef DUMP
  61. # include <sys/types.h>
  62. # include <unistd.h>
  63. # include <time.h>
  64. # ifndef DEFAULT_TIME_FORMAT
  65. # define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z"
  66. # endif
  67. #endif
  68.  
  69. #define LIPC_LOG(level,errno) level,APR_FROM_OS_ERROR(errno)
  70. module AP_MODULE_DECLARE_DATA limitipconn_module;
  71. static int server_limit, thread_limit;
  72.  
  73. #if CREATE_OWN_SCOREBOARD
  74. /* apache2 doesn't export ap_sb_handle_t via scoreboard.h */
  75. struct ap_sb_handle_t
  76. {
  77. int child_num;
  78. int thread_num;
  79. };
  80. #endif
  81.  
  82. #if CREATE_OWN_SCOREBOARD
  83. /* our own personal scoreboard */
  84. typedef struct {
  85. uid_t userid;
  86. char handler[100];
  87. } limit_scoreboard;
  88.  
  89. //static limit_scoreboard *sb = (limit_scoreboard *)-1;
  90. static void *sb = (void *)-1;
  91. static int limit_shmid;
  92. #endif
  93.  
  94. typedef struct
  95. {
  96. int limit; /* max number of connections per IP */
  97. int limit_ka; /* optional keepalive limit */
  98. #if CREATE_OWN_SCOREBOARD
  99. int limit_uid; /* max number of connections per user */
  100. int limit_uid_ka; /* optional keepalive limit */
  101. #endif
  102. int limit_vhost; /* max number of connections per virtual host */
  103. int limit_vhost_ka; /* optional keepalive limit */
  104. double limit_la1; /* maximum value of Load Average for 1 min. */
  105. double limit_la5; /* maximum value of Load Average for 5 min. */
  106. double limit_la15; /* maximum value of Load Average for 15 min. */
  107.  
  108. #if CREATE_OWN_SCOREBOARD
  109. apr_array_header_t *excl_limit; /* array of MIME types to limit check; all
  110.   other types are exempt */
  111. #endif
  112. } limitipconn_config;
  113.  
  114. static void *limitipconn_create_config(apr_pool_t *p, server_rec *s)
  115. {
  116. limitipconn_config *cfg = (limitipconn_config *)apr_pcalloc(p, sizeof(*cfg));
  117.  
  118. /* default configuration: no limit, and both arrays are empty */
  119. cfg->limit = -1;
  120. cfg->limit_ka = -1;
  121. #if CREATE_OWN_SCOREBOARD
  122. cfg->limit_uid = -1;
  123. cfg->limit_uid_ka = -1;
  124. #endif
  125. cfg->limit_vhost = -1;
  126. cfg->limit_vhost_ka = -1;
  127. cfg->limit_la1 = -1.0;
  128. cfg->limit_la5 = -1.0;
  129. cfg->limit_la15 = -1.0;
  130. #if CREATE_OWN_SCOREBOARD
  131. cfg->excl_limit = apr_array_make(p, 0, sizeof(char *));
  132. #endif
  133.  
  134. return cfg;
  135. }
  136.  
  137. #ifdef DUMP
  138. static const char *ap_scdump_fname = NULL;
  139.  
  140. static int dump_scoreboard(request_rec *r, const char *reason, int matches[server_limit][thread_limit])
  141. {
  142. worker_score *ws_record;
  143. process_score *ps_record;
  144. char buf[255];
  145. char *fname;
  146. FILE *fd;
  147. time_t nowtime = time(NULL);
  148. int i, j, dump;
  149. char *vhost = NULL;
  150. pid_t worker_pid;
  151. ap_generation_t worker_generation;
  152. uid_t userid = 0;
  153. #if CREATE_OWN_SCOREBOARD
  154. limit_scoreboard (* sb2)[thread_limit];
  155. sb2 = (limit_scoreboard (*)[thread_limit])sb;
  156. #endif
  157.  
  158. if (!ap_scdump_fname)
  159. {
  160. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "Error: Trying to dump scoreboard, but directive ScoreboardDump isn't set");
  161. return 1;
  162. }
  163.  
  164. sprintf(buf, "%s.%ld", ap_scdump_fname, (long)getpid());
  165. fname = ap_server_root_relative(r->pool, buf);
  166. if (!(fd = fopen(fname, "a")))
  167. {
  168. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, errno), r, "Error while opening: %s.", fname);
  169. return 1;
  170. }
  171.  
  172. fprintf(fd, "Scoreboard Dump for %s due to %s restriction\n", ap_get_server_name(r), reason);
  173. fprintf(fd, "Server Version: %s\n", ap_get_server_version());
  174. fprintf(fd, "Current Time: %s\n", ap_ht_time(r->pool, nowtime, DEFAULT_TIME_FORMAT, 0));
  175. fprintf(fd, "Request URI: %s [%s]\n", ap_escape_logitem(r->pool, r->unparsed_uri), ap_escape_logitem(r->pool, r->hostname));
  176. fprintf(fd, "Parent Server Generation: %d\n", (int)ap_my_generation);
  177. fputs("\n", fd);
  178. fflush(fd);
  179.  
  180. fputs("Server Details\n", fd);
  181. for (i = 0; i < server_limit; i++)
  182. {
  183. for (j = 0; j < thread_limit; j++)
  184. {
  185. ws_record = ap_get_scoreboard_worker(i, j);
  186. ps_record = ap_get_scoreboard_process(i);
  187. if (ps_record->generation == ap_my_generation)
  188. vhost = ws_record->vhost;
  189.  
  190. dump = 0;
  191. switch (ws_record->status)
  192. {
  193. case SERVER_BUSY_READ:
  194. case SERVER_BUSY_WRITE:
  195. case SERVER_BUSY_KEEPALIVE:
  196. case SERVER_BUSY_LOG:
  197. case SERVER_BUSY_DNS:
  198. case SERVER_GRACEFUL:
  199. if (matches[i][j])
  200. dump = 1;
  201. break;
  202. default:
  203. break;
  204. }
  205. if (!dump)
  206. continue;
  207.  
  208. /* MPM sets per-worker pid and generation */
  209. if (ws_record->pid)
  210. {
  211. worker_pid = ws_record->pid;
  212. worker_generation = ws_record->generation;
  213. }
  214. else
  215. {
  216. worker_pid = ps_record->pid;
  217. worker_generation = ps_record->generation;
  218. }
  219.  
  220.  
  221. if (ws_record->status == SERVER_DEAD)
  222. #ifdef TPF
  223. if (kill(ps_record->pid, 0) == 0)
  224. {
  225. /* on TPF show PIDs of the living dead */
  226. fprintf(fd, "Server %d-%d (%d): [", i, worker_generation, worker_pid);
  227. }
  228. else
  229. #endif
  230. fprintf(fd, "Server %d-%d (-): [", i, worker_generation);
  231. else
  232. fprintf(fd, "Server %d-%d (%d): [", i, worker_generation, worker_pid);
  233.  
  234. switch (ws_record->status)
  235. {
  236. case SERVER_READY:
  237. fputs("Ready", fd);
  238. break;
  239. case SERVER_STARTING:
  240. fputs("Starting", fd);
  241. break;
  242. case SERVER_BUSY_READ:
  243. fputs("Read", fd);
  244. break;
  245. case SERVER_BUSY_WRITE:
  246. fputs("Write", fd);
  247. break;
  248. case SERVER_BUSY_KEEPALIVE:
  249. fputs("Keepalive", fd);
  250. break;
  251. case SERVER_BUSY_LOG:
  252. fputs("Logging", fd);
  253. break;
  254. case SERVER_BUSY_DNS:
  255. fputs("DNS lookup", fd);
  256. break;
  257. case SERVER_DEAD:
  258. fputs("Dead", fd);
  259. break;
  260. case SERVER_GRACEFUL:
  261. fputs("Graceful", fd);
  262. break;
  263. default:
  264. fputs("?STATE?", fd);
  265. break;
  266. }
  267.  
  268. #if CREATE_OWN_SCOREBOARD
  269. userid = sb2[i][j].userid;
  270. #endif
  271.  
  272. fprintf(fd, "] %s {%s %s} [%s - %d]\n",
  273. ap_escape_html(r->pool, ws_record->client),
  274. ap_escape_html(r->pool, ap_escape_logitem(r->pool, ws_record->request)),
  275. #if CREATE_OWN_SCOREBOARD
  276. sb2[i][j].handler,
  277. #else
  278. "(unavailable)",
  279. #endif
  280. vhost ? ap_escape_html(r->pool, vhost) : "(unavailable)",
  281. userid);
  282. }
  283. }
  284.  
  285. fputs("\n-------------------------------------------------------------------------------\n\n", fd);
  286.  
  287. fclose(fd);
  288. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "Wrote scoreboard dump to: %s", fname);
  289.  
  290. return 0;
  291. }
  292. #endif
  293.  
  294. /* Simple merge: Per vhost entries overrides main server entries */
  295. static void *limitipconn_merge_config(apr_pool_t *p, void *BASE, void *ADD)
  296. {
  297. limitipconn_config *base = BASE;
  298. limitipconn_config *add = ADD;
  299. limitipconn_config *cfg = (limitipconn_config *)apr_pcalloc(p, sizeof(*cfg));
  300.  
  301. cfg->limit = (add->limit == -1) ? base->limit : add->limit;
  302. cfg->limit_ka = (add->limit_ka == -1) ? base->limit_ka : add->limit_ka;
  303. #if CREATE_OWN_SCOREBOARD
  304. cfg->limit_uid = (add->limit_uid == -1) ? base->limit_uid : add->limit_uid;
  305. cfg->limit_uid_ka = (add->limit_uid_ka == -1) ? base->limit_uid_ka : add->limit_uid_ka;
  306. #endif
  307. cfg->limit_vhost = (add->limit_vhost == -1) ? base->limit_vhost : add->limit_vhost;
  308. cfg->limit_vhost_ka = (add->limit_vhost_ka == -1) ? base->limit_vhost_ka : add->limit_vhost_ka;
  309. cfg->limit_la1 = (add->limit_la1 == -1.0) ? base->limit_la1 : add->limit_la1;
  310. cfg->limit_la5 = (add->limit_la5 == -1.0) ? base->limit_la5 : add->limit_la5;
  311. cfg->limit_la15 = (add->limit_la15 == -1.0) ? base->limit_la15 : add->limit_la15;
  312. #if CREATE_OWN_SCOREBOARD
  313. cfg->excl_limit = (add->excl_limit->nelts == 0) ? base->excl_limit : add->excl_limit;
  314. #endif
  315.  
  316. return cfg;
  317. }
  318.  
  319. static int limitipconn_handler(request_rec *r)
  320. {
  321. limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(r->server->module_config, &limitipconn_module);
  322. #if CREATE_OWN_SCOREBOARD
  323. limit_scoreboard (* sb2)[thread_limit];
  324. limit_scoreboard *sbrec = NULL;
  325. char **exlim = (char **)cfg->excl_limit->elts;
  326. int found;
  327. ap_unix_identity_t *identity;
  328. ap_sb_handle_t *sbh;
  329. #endif
  330. double current_la[3];
  331. int ip_req_count = 0;
  332. const char *address = NULL;
  333. #if CREATE_OWN_SCOREBOARD
  334. int uid_req_count = 0;
  335. uid_t current_uid = 0;
  336. #endif
  337. int vhost_req_count = 0;
  338. const char *current_vhost = NULL;
  339. worker_score *ws_record;
  340. process_score *ps_record;
  341. int i, j, k;
  342. #ifdef DUMP
  343. int matches[server_limit][thread_limit];
  344. #endif
  345.  
  346. /* A limit value of 0 by convention means no limit. */
  347. if (cfg->limit <= 0 && cfg->limit_ka <= 0
  348. #if CREATE_OWN_SCOREBOARD
  349. && cfg->limit_uid <= 0 && cfg->limit_uid_ka <= 0
  350. #endif
  351. && cfg->limit_vhost <= 0 && cfg->limit_vhost_ka <= 0
  352. && cfg->limit_la1 <= 0.0 && cfg->limit_la5 <= 0.0 && cfg->limit_la15 <= 0.0)
  353. {
  354. #ifdef DEBUG
  355. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r, "mod_limitipconn: OK: No limit");
  356. #endif
  357. return OK;
  358. }
  359.  
  360. #if CREATE_OWN_SCOREBOARD
  361. sb2 = (limit_scoreboard (*)[thread_limit])sb;
  362. sbh = r->connection->sbh;
  363. if (sbh == NULL)
  364. {
  365. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "mod_limitipconn: Warning: Request has no reference to scoreboard!");
  366. return OK;
  367. }
  368. sbrec = &sb2[sbh->child_num][sbh->thread_num];
  369.  
  370. /* Cycle through the exclusive list, if it exists; if our handler
  371.   * is not present, bail out */
  372. if (cfg->excl_limit->nelts > 0)
  373. {
  374. /* always update our scoreboard */
  375. if (r->handler)
  376. apr_cpystrn(sbrec->handler, r->handler, sizeof(sbrec->handler) - 1);
  377. else
  378. sbrec->handler[0] = 0;
  379.  
  380. # ifdef DEBUG
  381. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r, "mod_limitipconn: uri: %s handler: %s", r->uri, r->handler);
  382. # endif
  383.  
  384. found = 0;
  385. for (i = 0; r->handler && i < cfg->excl_limit->nelts && !found; i++)
  386. {
  387. if (strncmp(exlim[i], r->handler, strlen(exlim[i])) == 0)
  388. found = 1;
  389. }
  390.  
  391. if (!found)
  392. {
  393. # ifdef DEBUG
  394. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r, "mod_limitipconn: OK: %s not checked", r->handler);
  395. # endif
  396. return OK;
  397. }
  398. }
  399. #endif /* CREATE_OWN_SCOREBOARD */
  400.  
  401. /* Check Load Average overflow */
  402. if (cfg->limit_la1 > 0.0 || cfg->limit_la5 > 0.0 || cfg->limit_la15 > 0.0)
  403. {
  404. if (getloadavg(current_la, 3) != -1)
  405. {
  406. if ((current_la[0] >= cfg->limit_la1) && (current_la[1] >= cfg->limit_la5) && (current_la[2] >= cfg->limit_la15))
  407. {
  408. r->connection->keepalive = -1;
  409. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "Load Average limit exceeded (limit: %.2f, %.2f, %.2f)",
  410. cfg->limit_la1, cfg->limit_la5, cfg->limit_la15);
  411. return HTTP_SERVICE_UNAVAILABLE;
  412. }
  413. }
  414. }
  415.  
  416. /* get address */
  417. if (cfg->limit > 0 || cfg->limit_ka > 0)
  418. {
  419. #ifdef RECORD_FORWARD
  420. if ((address = ap_table_get(r->headers_in, "X-Forwarded-For")) == NULL)
  421. #endif
  422. address = r->connection->remote_ip;
  423. }
  424.  
  425. /* Get uid of current virual host for future use */
  426. if (r->server->is_virtual)
  427. {
  428. #if CREATE_OWN_SCOREBOARD
  429. if (cfg->limit_uid > 0 || cfg->limit_uid_ka > 0)
  430. {
  431. if ((identity = ap_run_get_suexec_identity(r)))
  432. current_uid = identity->uid;
  433. }
  434. sbrec->userid = current_uid;
  435. #endif
  436. if (cfg->limit_vhost > 0 || cfg->limit_vhost_ka > 0)
  437. current_vhost = r->server->server_hostname;
  438. }
  439.  
  440. /* Count up the number of connections we are handling right now from
  441.   * this IP address */
  442. for (j = 0; j < server_limit; j++)
  443. {
  444. for (k = 0; k < thread_limit; k++)
  445. {
  446. #ifdef DUMP
  447. matches[j][k] = 0;
  448. #endif
  449. ws_record = ap_get_scoreboard_worker(j, k);
  450. ps_record = ap_get_scoreboard_process(i);
  451. switch (ws_record->status)
  452. {
  453. case SERVER_BUSY_READ:
  454. case SERVER_BUSY_WRITE:
  455. case SERVER_BUSY_KEEPALIVE:
  456. case SERVER_BUSY_LOG:
  457. case SERVER_BUSY_DNS:
  458. case SERVER_GRACEFUL:
  459. #if CREATE_OWN_SCOREBOARD
  460. /* Cycle through the exclusive list, if it exists; if our handler
  461.   * is not present, skip */
  462. if (cfg->excl_limit->nelts > 0)
  463. {
  464. /* skip if no handler is set */
  465. if (!sb2[j][k].handler)
  466. continue;
  467.  
  468. found = 0;
  469. for (i = 0; i < cfg->excl_limit->nelts && !found; i++)
  470. {
  471. if (strncmp(exlim[i], sb2[j][k].handler, strlen(exlim[i])) == 0)
  472. found = 1;
  473. }
  474. if (!found)
  475. continue;
  476. }
  477. #endif
  478.  
  479. if (address && (strcmp(address, ws_record->client) == 0)
  480. #ifdef RECORD_FORWARD
  481. || (strcmp(address, ws_record->fwdclient) == 0)
  482. #endif
  483. )
  484. {
  485. ip_req_count++;
  486. #ifdef DUMP
  487. matches[j][k] = 1;
  488. #endif
  489. }
  490.  
  491. #if CREATE_OWN_SCOREBOARD
  492. if ((ps_record->generation == ap_my_generation))
  493. {
  494. if (current_uid && current_uid != unixd_config.user_id && sb2[j][k].userid == current_uid)
  495. uid_req_count++;
  496. if (current_vhost && (strcmp(ws_record->vhost, current_vhost) == 0))
  497. vhost_req_count++;
  498. }
  499. #endif
  500. break;
  501. default:
  502. break;
  503. }
  504. }
  505. }
  506.  
  507. /* turn off keepalive if keepalive-limit reached */
  508. if ((cfg->limit_ka > 0 && ip_req_count > cfg->limit_ka)
  509. #if CREATE_OWN_SCOREBOARD
  510. || (cfg->limit_uid_ka > 0 && uid_req_count > cfg->limit_uid_ka)
  511. #endif
  512. || (cfg->limit_vhost_ka > 0 && vhost_req_count > cfg->limit_vhost_ka))
  513. {
  514. #ifdef DEBUG
  515. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_DEBUG, 0), r, "mod_limitipconn: keepalive-limit reached. deactivating keepalive.");
  516. #endif
  517. r->connection->keepalive = -1;
  518. }
  519.  
  520. /* hard limit reached */
  521. if (cfg->limit > 0 && ip_req_count > cfg->limit)
  522. {
  523. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "Rejected, too many connections from address %s (limit: %d).",
  524. address, cfg->limit);
  525. apr_table_setn(r->subprocess_env, "LIMITIP", "1");
  526. #ifdef DUMP
  527. dump_scoreboard(r, "IP-Limit", matches);
  528. #endif
  529. return HTTP_SERVICE_UNAVAILABLE;
  530. }
  531. #if CREATE_OWN_SCOREBOARD
  532. else if (cfg->limit_uid > 0 && uid_req_count > cfg->limit_uid)
  533. {
  534. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "Rejected, too many connections for uid %u (limit: %d)",
  535. current_uid, cfg->limit_uid);
  536. #ifdef DUMP
  537. dump_scoreboard(r, "UID-Limit", matches);
  538. #endif
  539. return HTTP_SERVICE_UNAVAILABLE;
  540. }
  541. #endif
  542. else if (cfg->limit_vhost > 0 && vhost_req_count > cfg->limit_vhost)
  543. {
  544. ap_log_rerror(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_ERR, 0), r, "Rejected, too many connections for vhost %s (limit: %d)",
  545. current_vhost, cfg->limit_vhost);
  546. #ifdef DUMP
  547. dump_scoreboard(r, "Vhost-Limit", matches);
  548. #endif
  549. return HTTP_SERVICE_UNAVAILABLE;
  550. }
  551.  
  552. return OK;
  553. }
  554.  
  555. /* Parse the MaxConnPerIP directive */
  556. static const char *limit_config_cmd(cmd_parms *parms, void *mconfig, const char *arg1, const char *arg2)
  557. {
  558. limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module);
  559. long int limit, limit_ka;
  560.  
  561. limit = strtol(arg1, (char **)NULL, 10);
  562. if (limit < 0 || limit > INT_MAX)
  563. return "Integer overflow or invalid number";
  564.  
  565. if (arg2)
  566. {
  567. limit_ka = strtol(arg2, (char **)NULL, 10);
  568. if (limit_ka < 0 || limit_ka > INT_MAX)
  569. return "Integer overflow or invalid number";
  570. if (limit > 0 && limit_ka > limit)
  571. return "KeepAlive-limit has to be smaller than hard limit.";
  572. cfg->limit_ka = limit_ka;
  573. }
  574.  
  575. cfg->limit = limit;
  576. return NULL;
  577. }
  578.  
  579. #if CREATE_OWN_SCOREBOARD
  580. /* Parse the IPLimit directive */
  581. static const char *excl_limit_config_cmd(cmd_parms *parms, void *mconfig, const char *arg)
  582. {
  583. limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module);
  584. *(char **)apr_array_push(cfg->excl_limit) = apr_pstrdup(parms->pool, arg);
  585. return NULL;
  586. }
  587. #endif
  588.  
  589. /* Parse the MaxConnPerVhost directive */
  590. static const char *limit_vhost_config_cmd(cmd_parms *parms, void *mconfig, const char *arg1, const char *arg2)
  591. {
  592. limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module);
  593. long int limit, limit_ka;
  594.  
  595. limit = strtol(arg1, (char **)NULL, 10);
  596. if (limit < 0 || limit > INT_MAX)
  597. return "Integer overflow or invalid number";
  598.  
  599. if (arg2)
  600. {
  601. limit_ka = strtol(arg2, (char **)NULL, 10);
  602. if (limit_ka < 0 || limit_ka > INT_MAX)
  603. return "Integer overflow or invalid number";
  604. if (limit > 0 && limit_ka > limit)
  605. return "KeepAlive-limit has to be smaller than hard limit.";
  606. cfg->limit_vhost_ka = limit_ka;
  607. }
  608.  
  609. cfg->limit_vhost = limit;
  610. return NULL;
  611. }
  612.  
  613. #if CREATE_OWN_SCOREBOARD
  614. /* Parse the MaxConnPerUid directive */
  615. static const char *limit_uid_config_cmd(cmd_parms *parms, void *mconfig, const char *arg1, const char *arg2)
  616. {
  617. limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module);
  618. long int limit, limit_ka;
  619.  
  620. limit = strtol(arg1, (char **)NULL, 10);
  621. if (limit < 0 || limit > INT_MAX)
  622. return "Integer overflow or invalid number";
  623.  
  624. if (arg2)
  625. {
  626. limit_ka = strtol(arg2, (char **)NULL, 10);
  627. if (limit_ka < 0 || limit_ka > INT_MAX)
  628. return "Integer overflow or invalid number";
  629. if (limit > 0 && limit_ka > limit)
  630. return "KeepAlive-limit has to be smaller than hard limit.";
  631. cfg->limit_uid_ka = limit_ka;
  632. }
  633.  
  634. cfg->limit_uid = limit;
  635. return NULL;
  636. }
  637. #endif
  638.  
  639. /* Parse the MaxLA1 directive */
  640. static const char *limit_la1_config_cmd(cmd_parms *parms, void *mconfig, const char *arg)
  641. {
  642. limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module);
  643. double limit = strtod(arg, (char **)NULL);
  644.  
  645. if (limit < 0.0)
  646. return "Invalid LA1 value";
  647.  
  648. cfg->limit_la1 = limit;
  649. return NULL;
  650. }
  651.  
  652. /* Parse the MaxLA5 directive */
  653. static const char *limit_la5_config_cmd(cmd_parms *parms, void *mconfig, const char *arg)
  654. {
  655. limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module);
  656. double limit = strtod(arg, (char **)NULL);
  657.  
  658. if (limit < 0.0)
  659. return "Invalid LA5 value";
  660.  
  661. cfg->limit_la5 = limit;
  662. return NULL;
  663. }
  664.  
  665. /* Parse the MaxLA15 directive */
  666. static const char *limit_la15_config_cmd(cmd_parms *parms, void *mconfig, const char *arg)
  667. {
  668. limitipconn_config *cfg = (limitipconn_config *)ap_get_module_config(parms->server->module_config, &limitipconn_module);
  669. double limit = strtod(arg, (char **)NULL);
  670.  
  671. if (limit < 0.0)
  672. return "Invalid LA15 value";
  673.  
  674. cfg->limit_la15 = limit;
  675. return NULL;
  676. }
  677.  
  678. #ifdef DUMP
  679. static const char *set_scoreboard_dump(cmd_parms *parms, void *mconfig, const char *arg)
  680. {
  681. const char *err = ap_check_cmd_context(parms, GLOBAL_ONLY);
  682. if (err != NULL)
  683. return err;
  684.  
  685. if (parms->server->is_virtual)
  686. return "ScoreboardDump directive not allowed in <VirtualHost>";
  687.  
  688. ap_scdump_fname = arg;
  689. return NULL;
  690. }
  691. #endif
  692.  
  693. static command_rec limitipconn_cmds[] = {
  694. AP_INIT_TAKE12("MaxConnPerIP", limit_config_cmd, NULL, RSRC_CONF,
  695. "maximum simultaneous connections per IP address. Second value is optional. If reached, KeepAlive will be turned off."),
  696. #if CREATE_OWN_SCOREBOARD
  697. AP_INIT_ITERATE("IPLimit", excl_limit_config_cmd, NULL, RSRC_CONF,
  698. "restrict limit checking to these handlers/MIME types only."),
  699. #endif
  700. #if CREATE_OWN_SCOREBOARD
  701. AP_INIT_TAKE12("MaxConnPerUid", limit_uid_config_cmd, NULL, RSRC_CONF,
  702. "maximum simultaneous connections per user. Second value is optional. If reached, KeepAlive will be turned off."),
  703. #endif
  704. AP_INIT_TAKE12("MaxConnPerVhost", limit_vhost_config_cmd, NULL, RSRC_CONF,
  705. "maximum simultaneous connections per virtual host. Second value is optional. If reached, KeepAlive will be turned off."),
  706. AP_INIT_TAKE1("MaxLA1", limit_la1_config_cmd, NULL, RSRC_CONF,
  707. "maximum Load Overage value for the past 1 minute."),
  708. AP_INIT_TAKE1("MaxLA5", limit_la5_config_cmd, NULL, RSRC_CONF,
  709. "maximum Load Overage value for the past 5 minutes."),
  710. AP_INIT_TAKE1("MaxLA15", limit_la15_config_cmd, NULL, RSRC_CONF,
  711. "maximum Load Overage value for the past 15 minutes."),
  712. #ifdef DUMP
  713. AP_INIT_TAKE1("ScoreboardDump", set_scoreboard_dump, NULL, RSRC_CONF,
  714. "The filename of the scoreboard dumps. Will be appended by the process id."),
  715. #endif
  716. { NULL },
  717. };
  718.  
  719. #if CREATE_OWN_SCOREBOARD
  720. static apr_status_t limit_detach_shm(void *data)
  721. {
  722. shmdt(data);
  723. return APR_SUCCESS;
  724. }
  725. #endif
  726.  
  727. static int limitipconn_init(apr_pool_t *p, apr_pool_t *plog,
  728. apr_pool_t *ptemp, server_rec *s)
  729. {
  730. void *data;
  731. const char *userdata_key = "limitipconn_init";
  732.  
  733. /* initialize_module() will be called twice, and if it's a DSO
  734.   * then all static data from the first call will be lost. Only
  735.   * set up our static data on the second call. */
  736. apr_pool_userdata_get(&data, userdata_key, s->process->pool);
  737. if (!data)
  738. {
  739. apr_pool_userdata_set((const void *)1, userdata_key,
  740. apr_pool_cleanup_null, s->process->pool);
  741. return APR_SUCCESS;
  742. }
  743.  
  744. ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
  745. ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
  746.  
  747. #if CREATE_OWN_SCOREBOARD
  748. if ((limit_shmid = shmget(SHM_KEY, sizeof(limit_scoreboard) * server_limit * thread_limit, IPC_CREAT | SHM_R | SHM_W)) == -1)
  749. {
  750. ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_EMERG, errno), s, "could not initialize shared memory");
  751. exit(1);
  752. }
  753.  
  754. ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_NOTICE, 0), s, "mod_limitipconn: initialized shared memory.");
  755.  
  756. //if ((sb = (limit_scoreboard *) shmat(limit_shmid, NULL, 0)) == (limit_scoreboard *) -1)
  757. if ((sb = (void *)shmat(limit_shmid, NULL, 0)) == (void *) -1)
  758. ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_EMERG, errno), s, "shmat error");
  759. /* exit later after marking the segment to remove itself */
  760. else
  761. apr_pool_cleanup_register(p, (void *)sb, limit_detach_shm, apr_pool_cleanup_null);
  762.  
  763. /* Mark the segment for deletion once all attachments are detached */
  764. if (shmctl(limit_shmid, IPC_RMID, NULL) == -1)
  765. ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_EMERG, errno), s, "Could not mark shared segment for deletion, you must manually clean it up");
  766.  
  767. /* exit if we didn't attach successfully */
  768. if (sb == (void *) -1)
  769. exit(1);
  770. #endif
  771.  
  772. ap_log_error(APLOG_MARK, LIPC_LOG(APLOG_NOERRNO|APLOG_NOTICE, 0), s, MODULE_NAME " " MODULE_VERSION " started.");
  773. return APR_SUCCESS;
  774. }
  775.  
  776. static void register_hooks(apr_pool_t * p)
  777. {
  778. ap_hook_post_config(limitipconn_init, NULL, NULL, APR_HOOK_MIDDLE);
  779. ap_hook_fixups(limitipconn_handler, NULL, NULL, APR_HOOK_MIDDLE);
  780. }
  781.  
  782. module AP_MODULE_DECLARE_DATA limitipconn_module =
  783. {
  784. STANDARD20_MODULE_STUFF,
  785. NULL, /* per-directory config creator */
  786. NULL, /* dir config merger */
  787. limitipconn_create_config, /* server config creator */
  788. limitipconn_merge_config, /* server config merger */
  789. limitipconn_cmds, /* command table */
  790. register_hooks, /* set up other request processing hooks */
  791. };
  792.