From 3b4c071bc72e00ece7d848be706753da735a438a Mon Sep 17 00:00:00 2001 From: "Menno E. Duursma" Date: Tue, 11 May 2010 22:54:55 +0200 Subject: [PATCH] network/mod_limitipconn: Added to 12.1 repository --- network/mod_limitipconn/README | 69 ++++ .../mod_limitipconn.SlackBuild | 69 ++++ network/mod_limitipconn/mod_limitipconn.c | 320 ++++++++++++++++++ network/mod_limitipconn/mod_limitipconn.info | 8 + network/mod_limitipconn/slack-desc | 19 ++ network/mod_limitipconn/test.pl | 18 + 6 files changed, 503 insertions(+) create mode 100644 network/mod_limitipconn/README create mode 100644 network/mod_limitipconn/mod_limitipconn.SlackBuild create mode 100644 network/mod_limitipconn/mod_limitipconn.c create mode 100644 network/mod_limitipconn/mod_limitipconn.info create mode 100644 network/mod_limitipconn/slack-desc create mode 100644 network/mod_limitipconn/test.pl diff --git a/network/mod_limitipconn/README b/network/mod_limitipconn/README new file mode 100644 index 0000000000..34bf2a7aab --- /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 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 0000000000..916d0475da --- /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 0000000000..a53683c7db --- /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 0000000000..1351784413 --- /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 0000000000..a2de81112c --- /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 0000000000..dc8ca7677b --- /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); +} +