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