aboutsummaryrefslogtreecommitdiff
path: root/network/mod_limitipconn
diff options
context:
space:
mode:
Diffstat (limited to 'network/mod_limitipconn')
-rw-r--r--network/mod_limitipconn/README69
-rw-r--r--network/mod_limitipconn/mod_limitipconn.SlackBuild69
-rw-r--r--network/mod_limitipconn/mod_limitipconn.c320
-rw-r--r--network/mod_limitipconn/mod_limitipconn.info8
-rw-r--r--network/mod_limitipconn/slack-desc19
-rw-r--r--network/mod_limitipconn/test.pl18
6 files changed, 503 insertions, 0 deletions
diff --git a/network/mod_limitipconn/README b/network/mod_limitipconn/README
new file mode 100644
index 0000000000000..34bf2a7aabfe1
--- /dev/null
+++ b/network/mod_limitipconn/README
@@ -0,0 +1,69 @@
+From:
+http://www.mail-archive.com/dev@httpd.apache.org/msg37189.html
+
+Hi!
+
+
+Attached is a version of mod_limitipconn.c that works in conjunction with
+mod_cache and httpd-2.2. We've been using this on ftp.acc.umu.se for some
+time now without any unwanted issues.
+
+The main problem with mod_limitipconn-0.22 was that since mod_cache runs as
+a quick handler, mod_limitipconn also must run as a quick handler with all
+those benefits and drawbacks.
+
+Download the tarball from http://dominia.org/djao/limitipconn2.html , extract
+it, and replace mod_limitipconn.c with this version and follow the build
+instructions.
+
+I would really wish that this was made part of httpd, it's really needed when
+running a file-download site due to the scarily large amount of demented
+download manager clients out there.
+
+However, I have not received any response from the original author on the
+matter. From what I have understood of the license it should be OK to merge
+into httpd if you want though, but I think that you guys are way more clued
+in that matter than me.
+
+This is a summary of the changes made:
+* Rewritten to run as a Quick Handler, before mod_cache.
+* Configuration directives are now set per VHost (Directory/Location
+ are available after the Quick Handler has been run). This means that
+ any <Location> containers has to be deleted in existing configs.
+* Fixed configuration merging, so per-vhost settings use defaults set
+ at the server level.
+* By running as a Quick Handler we don't go through the entire lookup
+ phase (resolve path, stat file, etc) before we get the possibility
+ to block a request. This gives a clear performance enhancement.
+* Made the handler exit as soon as possible, doing the "easy" checks
+ first.
+* Don't do subrequest to lookup MIME type if we don't have mime-type
+ specific config.
+* Count connections in closing and logging state too, we don't want to
+ be DOS'd by clients behind buggy firewalls and so on.
+* Added debug messages for easy debugging.
+* Reduced loglevel from ERR to INFO for reject-logging.
+
+In any case, I hope that this can be of use for others than us.
+
+
+/Nikke
+--
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ Niklas Edmundsson, Admin @ {acc,hpc2n}.umu.se | [EMAIL PROTECTED]
+---------------------------------------------------------------------------
+ We are AT&T of Borg, MCI will be assimilated
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+(FWIW: copied without explicit permission)
+
+--
+
+The module can be loaded with the following in /etc/httpd/httpd.conf
+
+LoadModule limitipconn_module lib/httpd/modules/mod_limitipconn.so
+ExtendedStatus On
+MaxConnPerIP 5
+
+To test the 'test.pl' utility from mod_evasive is included in the doc dir.
+
diff --git a/network/mod_limitipconn/mod_limitipconn.SlackBuild b/network/mod_limitipconn/mod_limitipconn.SlackBuild
new file mode 100644
index 0000000000000..916d0475da142
--- /dev/null
+++ b/network/mod_limitipconn/mod_limitipconn.SlackBuild
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+# Slackware build script for mod_limitipconn (an Apache limit module)
+
+# Written by Menno E. Duursma
+
+# This program is free software. It comes without any warranty.
+# Granted WTFPL, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/wtfpl/COPYING for more details.
+
+# Modified by SlackBuilds.org
+
+PRGNAM=mod_limitipconn
+VERSION=${VERSION:-0.22}
+ARCH=${ARCH:-i486}
+BUILD=${BUILD:-1}
+TAG=${TAG:-_SBo}
+
+CWD=`pwd`
+TMP=${TMP:-/tmp/SBo}
+PKG=$TMP/package-$PRGNAM
+OUTPUT=${OUTPUT:-/tmp}
+
+if [ "$ARCH" = "i486" ]; then
+ SLKCFLAGS="-O2 -march=i486 -mtune=i686"
+elif [ "$ARCH" = "i686" ]; then
+ SLKCFLAGS="-O2 -march=i686 -mtune=i686"
+elif [ "$ARCH" = "x86_64" ]; then
+ SLKCFLAGS="-O2 -fPIC"
+fi
+
+set -e # exit on most errors
+
+rm -rf $PKG
+mkdir -p $TMP $PKG $OUTPUT
+cd $TMP
+rm -rf $PRGNAM-$VERSION
+tar xvf $CWD/$PRGNAM-$VERSION.tar.gz
+cd $PRGNAM-$VERSION
+chown -R root:root .
+chmod -R a-s,u+w,go+r-w .
+
+# This is patched for Apache v2.2 support so much we might as well replace it
+cat $CWD/mod_limitipconn.c > mod_limitipconn.c
+
+# Create target directory
+mkdir -p $PKG/usr/lib/httpd/modules
+
+# Compile module as DSO (dynmically shared object)
+CFLAGS="$SLACKFLAGS" \
+apxs -ca mod_limitipconn.c
+
+# copy into place
+cp -v .libs/mod_limitipconn.so $PKG/usr/lib/httpd/modules
+
+( cd $PKG || exit 1
+ find . -type f | xargs file | grep -e "executable" -e "shared object" | grep ELF | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null
+)
+
+mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION
+cp -a ChangeLog INSTALL README $PKG/usr/doc/$PRGNAM-$VERSION
+cat $CWD/test.pl > $PKG/usr/doc/$PRGNAM-$VERSION/test.pl
+cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild
+
+mkdir -p $PKG/install
+cat $CWD/slack-desc > $PKG/install/slack-desc
+
+cd $PKG
+/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.tgz
diff --git a/network/mod_limitipconn/mod_limitipconn.c b/network/mod_limitipconn/mod_limitipconn.c
new file mode 100644
index 0000000000000..a53683c7db56c
--- /dev/null
+++ b/network/mod_limitipconn/mod_limitipconn.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2000-2002 David Jao <[EMAIL PROTECTED]>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice, this permission notice, and the
+ * following disclaimer shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_core.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "ap_mpm.h"
+#include "apr_strings.h"
+#include "scoreboard.h"
+
+#define MODULE_NAME "mod_limitipconn"
+#define MODULE_VERSION "0.22"
+
+module AP_MODULE_DECLARE_DATA limitipconn_module;
+
+static int server_limit, thread_limit;
+
+typedef struct {
+ signed int limit; /* max number of connections per IP */
+
+ /* array of MIME types exempt from limit checking */
+ apr_array_header_t *no_limit;
+ int no_limit_set;
+
+ /* array of MIME types to limit check; all other types are exempt */
+ apr_array_header_t *excl_limit;
+ int excl_limit_set;
+} limitipconn_config;
+
+static void *limitipconn_create_config(apr_pool_t *p, server_rec *s)
+{
+ limitipconn_config *cfg = (limitipconn_config *)
+ apr_pcalloc(p, sizeof (*cfg));
+
+ /* default configuration: no limit (unset), and both arrays are empty */
+ cfg->limit = -1;
+ cfg->no_limit = apr_array_make(p, 0, sizeof(char *));
+ cfg->excl_limit = apr_array_make(p, 0, sizeof(char *));
+
+ return cfg;
+}
+
+/* Simple merge: Per vhost entries overrides main server entries */
+static void *limitipconn_merge_config(apr_pool_t *p, void *BASE, void *ADD)
+{
+ limitipconn_config *base = BASE;
+ limitipconn_config *add = ADD;
+
+ limitipconn_config *cfg = (limitipconn_config *)
+ apr_pcalloc(p, sizeof (*cfg));
+
+ cfg->limit = (add->limit == -1) ? base->limit : add->limit;
+ cfg->no_limit = add->no_limit_set ? add->no_limit : base->no_limit;
+ cfg->excl_limit = add->excl_limit_set ? add->excl_limit : base->excl_limit;
+
+ return cfg;
+}
+
+/* The handler runs as a quick handler so we can arrange for it to be called
+ before mod_cache. Being a quick handler means that we have a lot of
+ limitations, the basic ones are that the only thing we know is the URL and
+ that if we return OK it means that we handle the entire reply of the
+ request including populating the brigades with data. */
+static int limitipconn_handler(request_rec *r, int lookup)
+{
+ /* get configuration information */
+ limitipconn_config *cfg = (limitipconn_config *)
+ ap_get_module_config(r->server->module_config, &limitipconn_module);
+
+ /* convert Apache arrays to normal C arrays */
+ char **nolim = (char **) cfg->no_limit->elts;
+ char **exlim = (char **) cfg->excl_limit->elts;
+
+ const char *address;
+
+ /* loop index variables */
+ int i;
+ int j;
+
+ /* running count of number of connections from this address */
+ int ip_count = 0;
+
+ /* Content-type of the current request */
+ const char *content_type;
+
+ /* scoreboard data structure */
+ worker_score *ws_record;
+
+ /* We decline to handle subrequests: otherwise, in the next step we
+ * could get into an infinite loop. */
+ if (!ap_is_initial_req(r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "mod_limitipconn: SKIPPED: Not initial request");
+ return DECLINED;
+ }
+
+ /* A limit value of 0 by convention means no limit, negative means
+ unset (no limit). */
+ if (cfg->limit <= 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "mod_limitipconn: OK: No limit");
+ return DECLINED;
+ }
+
+#ifdef RECORD_FORWARD
+ if ((address = apr_table_get(r->headers_in, "X-Forwarded-For")) == NULL)
+#endif
+ address = r->connection->remote_ip;
+
+ /* Only check the MIME-type if we have MIME-type stuff in our config.
+ That extra subreq can be quite expensive. */
+ if(cfg->no_limit->nelts > 0 || cfg->excl_limit->nelts > 0) {
+ /* Look up the Content-type of this request. We need a subrequest
+ * here since this module might be called before the URI has been
+ * translated into a MIME type. */
+ content_type = ap_sub_req_lookup_uri(r->uri, r, NULL)->content_type;
+
+ /* If there's no Content-type, use the default. */
+ if (!content_type) {
+ content_type = ap_default_type(r);
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "mod_limitipconn: uri: %s Content-Type: %s",
+ r->uri, content_type);
+
+ /* Cycle through the exempt list; if our content_type is exempt,
+ * return OK */
+ for (i = 0; i < cfg->no_limit->nelts; i++) {
+ if ((ap_strcasecmp_match(content_type, nolim[i]) == 0)
+ || (strncmp(nolim[i], content_type, strlen(nolim[i])) == 0))
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "mod_limitipconn: OK: %s exempt", content_type);
+ return DECLINED;
+ }
+ }
+
+ /* Cycle through the exclusive list, if it exists; if our MIME type
+ * is not present, bail out */
+ if (cfg->excl_limit->nelts) {
+ int excused = 1;
+ for (i = 0; i < cfg->excl_limit->nelts; i++) {
+ if ((ap_strcasecmp_match(content_type, exlim[i]) == 0)
+ ||
+ (strncmp(exlim[i], content_type, strlen(exlim[i])) == 0))
+ {
+ excused = 0;
+ }
+ }
+ if (excused) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "mod_limitipconn: OK: %s not excluded",
+ content_type);
+ return DECLINED;
+ }
+ }
+ }
+
+ /* Count up the number of connections we are handling right now from
+ * this IP address */
+ for (i = 0; i < server_limit; ++i) {
+ for (j = 0; j < thread_limit; ++j) {
+ ws_record = ap_get_scoreboard_worker(i, j);
+ switch (ws_record->status) {
+ case SERVER_BUSY_READ:
+ case SERVER_BUSY_WRITE:
+ case SERVER_BUSY_KEEPALIVE:
+ case SERVER_BUSY_LOG:
+ case SERVER_BUSY_DNS:
+ case SERVER_CLOSING:
+ case SERVER_GRACEFUL:
+ if ((strcmp(address, ws_record->client) == 0)
+#ifdef RECORD_FORWARD
+ || (strcmp(address, ws_record->fwdclient) == 0)
+#endif
+ ) {
+ ip_count++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "mod_limitipconn: vhost: %s uri: %s current: %d limit: %d",
+ r->server->server_hostname, r->uri, ip_count, cfg->limit);
+
+ if (ip_count > cfg->limit) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ "Rejected, too many connections from this host.");
+ /* set an environment variable */
+ apr_table_setn(r->subprocess_env, "LIMITIP", "1");
+ /* return 503 */
+ return HTTP_SERVICE_UNAVAILABLE;
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "mod_limitipconn: OK: Passed all checks");
+ return DECLINED;
+ }
+}
+
+/* Parse the MaxConnPerIP directive */
+static const char *limit_config_cmd(cmd_parms *parms, void *dummy,
+ const char *arg)
+{
+ limitipconn_config *cfg = (limitipconn_config *)
+ ap_get_module_config(parms->server->module_config,
+ &limitipconn_module);
+
+ signed long int limit = strtol(arg, (char **) NULL, 10);
+
+ /* No reasonable person would want more than 2^16. Better would be
+ to use LONG_MAX but that causes portability problems on win32 */
+ if ((limit > 65535) || (limit < 0)) {
+ return "Integer overflow or invalid number";
+ }
+
+ cfg->limit = limit;
+ return NULL;
+}
+
+/* Parse the NoIPLimit directive */
+static const char *no_limit_config_cmd(cmd_parms *parms, void *dummy,
+ const char *arg)
+{
+ limitipconn_config *cfg = (limitipconn_config *)
+ ap_get_module_config(parms->server->module_config,
+ &limitipconn_module);
+
+ *(char **) apr_array_push(cfg->no_limit) = apr_pstrdup(parms->pool, arg);
+ cfg->no_limit_set = 1;
+ return NULL;
+}
+
+/* Parse the OnlyIPLimit directive */
+static const char *excl_limit_config_cmd(cmd_parms *parms, void *dummy,
+ const char *arg)
+{
+ limitipconn_config *cfg = (limitipconn_config *)
+ ap_get_module_config(parms->server->module_config,
+ &limitipconn_module);
+
+ *(char **) apr_array_push(cfg->excl_limit) = apr_pstrdup(parms->pool, arg);
+ cfg->excl_limit_set = 1;
+ return NULL;
+}
+
+/* Array describing structure of configuration directives */
+static command_rec limitipconn_cmds[] = {
+ AP_INIT_TAKE1("MaxConnPerIP", limit_config_cmd, NULL, RSRC_CONF,
+ "maximum simultaneous connections per IP address"),
+ AP_INIT_ITERATE("NoIPLimit", no_limit_config_cmd, NULL, RSRC_CONF,
+ "MIME types for which limit checking is disabled"),
+ AP_INIT_ITERATE("OnlyIPLimit", excl_limit_config_cmd, NULL, RSRC_CONF,
+ "restrict limit checking to these MIME types only"),
+ {NULL},
+};
+
+/* Set up startup-time initialization */
+static int limitipconn_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
+server_rec *s)
+{
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
+ MODULE_NAME " " MODULE_VERSION " started.");
+ ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
+ ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
+ return OK;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+ static const char * const after_me[] = { "mod_cache.c", NULL };
+
+ /* We must run as a quick handle so we can deny connections before
+ mod_cache gets to serve them */
+ ap_hook_quick_handler(limitipconn_handler, NULL, after_me, APR_HOOK_FIRST);
+
+ ap_hook_post_config(limitipconn_init, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA limitipconn_module = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-dir config structures */
+ NULL, /* merge per-dir config structures */
+ limitipconn_create_config, /* create per-server config structures */
+ limitipconn_merge_config, /* merge per-server config structures */
+ limitipconn_cmds, /* table of config file commands */
+ register_hooks
+};
+
diff --git a/network/mod_limitipconn/mod_limitipconn.info b/network/mod_limitipconn/mod_limitipconn.info
new file mode 100644
index 0000000000000..1351784413ed8
--- /dev/null
+++ b/network/mod_limitipconn/mod_limitipconn.info
@@ -0,0 +1,8 @@
+PRGNAM="mod_limitipconn"
+VERSION="0.22"
+HOMEPAGE="http://dominia.org/djao/limitipconn2.html"
+DOWNLOAD="http://dominia.org/djao/limit/mod_limitipconn-0.22.tar.gz"
+MD5SUM="0f4beb9eb4e7b815ca472ccfe11451b3"
+MAINTAINER="Menno E. Duursma"
+EMAIL="druiloor@zonnet.nl"
+APPROVED="Erik Hanson"
diff --git a/network/mod_limitipconn/slack-desc b/network/mod_limitipconn/slack-desc
new file mode 100644
index 0000000000000..a2de81112c719
--- /dev/null
+++ b/network/mod_limitipconn/slack-desc
@@ -0,0 +1,19 @@
+# HOW TO EDIT THIS FILE:
+# The "handy ruler" below makes it easier to edit a package description. Line
+# up the first '|' above the ':' following the base package name, and the '|'
+# on the right side marks the last column you can put a character in. You must
+# make exactly 11 lines for the formatting to be correct. It's also
+# customary to leave one space after the ':'.
+
+ |-----handy-ruler------------------------------------------------------|
+mod_limitipconn: mod_limitipconn (an Apache connection limit module)
+mod_limitipconn:
+mod_limitipconn: This module allows web server administrators to limit the
+mod_limitipconn: number of simultaneous downloads permitted from a single
+mod_limitipconn: IP address.
+mod_limitipconn:
+mod_limitipconn: mod_limitipconn was written by David Jao
+mod_limitipconn: The Apache 2.2 port by Niklas Edmundsson
+mod_limitipconn:
+mod_limitipconn:
+mod_limitipconn:
diff --git a/network/mod_limitipconn/test.pl b/network/mod_limitipconn/test.pl
new file mode 100644
index 0000000000000..dc8ca7677bd64
--- /dev/null
+++ b/network/mod_limitipconn/test.pl
@@ -0,0 +1,18 @@
+#!/usr/bin/perl
+
+# test.pl: small script to test mod_dosevasive's effectiveness
+
+use IO::Socket;
+use strict;
+
+for(0..100) {
+ my($response);
+ my($SOCKET) = new IO::Socket::INET( Proto => "tcp",
+ PeerAddr=> "127.0.0.1:80");
+ if (! defined $SOCKET) { die $!; }
+ print $SOCKET "GET /?$_ HTTP/1.0\n\n";
+ $response = <$SOCKET>;
+ print $response;
+ close($SOCKET);
+}
+