Logo Search packages:      
Sourcecode: rageircd version File versions  Download package

throttle.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: throttle.c,v 1.35.2.1 2004/12/07 03:05:41 pneumatus Exp $
 */

/*
 * Copyright 2000, 2001 Chip Norkus
 * Modified for RageIRCd v2 by Alasdair McWilliam. RageIRCd, Inc.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names of the maintainer, developers and contributors may not be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE MAINTAINER, DEVELOPERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE DEVELOPERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "setup.h"

#ifdef USE_THROTTLE

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "numeric.h"
#include "blalloc.h"
#include "queue.h"
#include "memory.h"
#include "hook.h"
#include <sys/types.h>
#include <sys/socket.h>

int numthrottles = 0;
int numhashents = 0;
hash_table *throttle_hash;

SLIST_HEAD(hashent_list_t, hashent_t);
LIST_HEAD(throttle_list_t, throttle_t) throttles;

BlockHeap *throttle_heap = NULL;
BlockHeap *hashent_heap = NULL;

static char throttlebuf[512];
static int throttlelen = 0;

static hash_table *create_hash_table(int, size_t, size_t, int (*)(void *, void *));
static unsigned int hash_get_key_hash(hash_table *, void *, size_t);
static int hash_insert(hash_table *, void *);
static int hash_delete(hash_table *, void *);
static void *hash_find(hash_table *, void *);

static int throttle_get_zline_time(int stage)
{
      switch (stage) {
            case -1:
                  return 0;
            case 0:
                  return 120;
            case 1:
                  return 300;
            case 2:
                  return 900;
            case 3:
                  return 1800;
            default:
                  return 3600;
      }
      return 0;
}

void throttle_timer(time_t now)
{
      throttle *tp, *tp2;
      time_t zlength;

      if (!FloodConfig.min_connect_time || !FloodConfig.max_connect_count) {
            return;
      }
      for (tp = LIST_FIRST(&throttles); tp != NULL; tp = tp2) {
            tp2 = LIST_NEXT(tp, lp);
            zlength = throttle_get_zline_time(tp->stage);
            if (!now || (tp->zline_start && (now - tp->zline_start) >= (zlength + THROTTLE_RECORDTIME)) ||
              (!tp->zline_start && (now - tp->last) >= THROTTLE_RECORDTIME)) {
                  LIST_REMOVE(tp, lp);
                  hash_delete(throttle_hash, tp);
                  free_throttle(tp);
                  numthrottles--;
            }
      }
}

void throttle_rehash()
{
      throttle_timer(0);
}

static hash_table *create_hash_table(int elems, size_t offset, size_t len, int (*cmpfunc)(void *, void *))
{
      hash_table *htp = (hash_table *)MyMalloc(sizeof(hash_table));
      htp->size = elems;
      htp->keyoffset = offset;
      htp->flag.string = 1;
      htp->flag.nocase = 0;
      htp->cmpfunc = cmpfunc;
      htp->table = MyMalloc(sizeof(hashent_list) * htp->size);
      return htp;
}

static unsigned int hash_get_key_hash(hash_table *table, void *key, size_t offset)
{
      char *rkey = (char *)key + offset;
      int len = table->keylen;
      unsigned int hash = 0;

      if (!len) {
            len = strlen(rkey);
      }
      else if (table->flag.string) {
            if ((len = strlen(rkey)) > table->keylen) {
                  len = table->keylen;
            }
      }
      if (table->flag.nocase) {
            while (len--) {
                  hash = hash * 33 + ToLower(*rkey++);
            }
      }
      else {
            while (len--) {
                  hash = hash * 33 + *rkey++;
            }
      }
      return (hash % table->size);
}

static int hash_insert(hash_table *table, void *ent)
{
      int hash = hash_get_key_hash(table, ent, table->keyoffset);
      hashent *hep = make_hashent();
      hep->ent = ent;
      SLIST_INSERT_HEAD(&table->table[hash], hep, lp);
      numhashents++;
      return 1;
}

static int hash_delete(hash_table *table, void *ent)
{
      int hash = hash_get_key_hash(table, ent, table->keyoffset);
      hashent *hep;

      SLIST_FOREACH(hep, &table->table[hash], lp) {
            if (hep->ent == ent) {
                  break;
            }
      }
      if (hep == NULL) {
            return 0;
      }
      SLIST_REMOVE(&table->table[hash], hep, hashent_t, lp);
      free_hashent(hep);
      numhashents--;
      return 1;
}

static void *hash_find(hash_table *table, void *key)
{
      int hash = hash_get_key_hash(table, key, 0);
      hashent *hep;

      SLIST_FOREACH(hep, &table->table[hash], lp) {
            if (!table->cmpfunc(&((char *)hep->ent)[table->keyoffset], key)) {
                  return hep->ent;
            }
      }
      return NULL;
}

void init_throttle(void)
{
      throttle_heap = BlockHeapCreate(sizeof(throttle), THROTTLE_HEAP_SIZE);
      hashent_heap = BlockHeapCreate(sizeof(hashent), HASHENT_HEAP_SIZE);

      throttle_hash = create_hash_table(THROTTLE_HASHSIZE, offsetof(throttle, addr),
            HOSTIPLEN + 1, (int (*)(void *, void *))irccmp);

      throttlelen = ircsnprintf(throttlebuf, 512, "ERROR :Your host is trying to "
            "(re)connect too fast -- throttled.\r\n");
}

void throttle_remove(char *host)
{
      throttle *tp;
      if ((tp = hash_find(throttle_hash, host)) != NULL) {
            LIST_REMOVE(tp, lp);
            hash_delete(throttle_hash, tp);
            free_throttle(tp);
            numthrottles--;
      }
}

int throttle_force(char *host)
{
      throttle *tp;

      if (!FloodConfig.min_connect_time || !FloodConfig.max_connect_count) {
            return 0;
      }
      if ((tp = hash_find(throttle_hash, host)) == NULL) {
            tp = make_throttle();
            strcpy(tp->addr, host);
            tp->stage = -1;
            tp->zline_start = 0;
            tp->conns = 0;
            tp->first = timeofday;
            tp->re_zlines = 0;

            hash_insert(throttle_hash, tp);
            LIST_INSERT_HEAD(&throttles, tp, lp);
            numthrottles++;
      }
      tp->conns = FloodConfig.max_connect_count;
      tp->last = tp->first = timeofday;
      return 1;
}

int throttle_check(char *host, int fd, time_t sotime)
{
      throttle *tp;

      if (!FloodConfig.min_connect_time || !FloodConfig.max_connect_count) {
            return 1;
      }
      if (fd == -1 && (timeofday - sotime > FloodConfig.min_connect_time)) {
            return 1;
      }
      if (sotime > timeofday) {
            sotime = timeofday;
      }
      if ((tp = hash_find(throttle_hash, host)) == NULL) {
            tp = make_throttle();
            strcpy(tp->addr, host);
            tp->stage = -1;
            tp->zline_start = 0;
            tp->conns = 0;
            tp->first = sotime;
            tp->re_zlines = 0;

            hash_insert(throttle_hash, tp);
            LIST_INSERT_HEAD(&throttles, tp, lp);
            numthrottles++;
      }
      else if (tp->zline_start) {
            time_t zlength = throttle_get_zline_time(tp->stage);

            if (sotime - tp->zline_start < zlength) {
                  if (fd == -1) {
                        return 0;
                  }
                  send(fd, throttlebuf, throttlelen, 0);
                  tp->re_zlines++;
                  tp->zline_start = sotime;
                  return 0;
            }
            tp->conns = 0;
            tp->first = sotime;
            tp->zline_start = 0;
      }

      if (tp->conns >= 0) {
            tp->conns++;
      }
      tp->last = sotime;

      if (sotime - tp->first > FloodConfig.min_connect_time) {
            tp->conns = 1;
            tp->first = sotime;
            return 1;
      }
      if (tp->conns == -1) {
            return 0;
      }
      if (tp->conns >= FloodConfig.max_connect_count) {
            if (fd != -1) {
                  char errbuf[512];
                  time_t zlength = throttle_get_zline_time(++tp->stage);
                  int elength, tmp;

                  sendto_realops_lev(CCONN_LEV, "throttled connections from %s (%d in %d seconds) for %d "
                        "minutes (offense %d)", tp->addr, tp->conns, sotime - tp->first, zlength / 60,
                        tp->stage + 1);

                  elength = ircsnprintf(errbuf, 512, ":%s NOTICE :You have been throttled for %d "
                        "minutes for too many connections in a short period of time. Further "
                        "connections during this period will reset your throttle and you will "
                        "have to wait longer.\r\n", me.name, zlength / 60);
                  tmp = send(fd, errbuf, elength, 0);

                  if (throttle_get_zline_time(tp->stage + 1) != zlength) {
                        elength = ircsnprintf(errbuf, 512, ":%s NOTICE :When you reconnect, if "
                              "you are throttled again, it will last longer.\r\n", me.name);
                        send(fd, errbuf, elength, 0);
                  }

                  send(fd, throttlebuf, throttlelen, 0);
                  tp->zline_start = sotime;
            }
            return 0;
      }
      return 1;
}

void throttle_stats(aClient *cptr)
{
      int pending = 0, bans = 0;
      throttle *tp;

      send_me_debug(cptr, "T :throttles: %d", numthrottles);
      send_me_debug(cptr, "T :memory allocation: %d hashents (%d bytes) %d throttles (%d bytes)",
            numhashents, numhashents * sizeof(hashent), numthrottles, numthrottles * sizeof(throttle));
      send_me_debug(cptr, "T :throttle hash table size: %d", throttle_hash->size);

      LIST_FOREACH(tp, &throttles, lp) {
            if (tp->zline_start > 0) {
                  bans++;
            }
            else {
                  pending++;
            }
      }

      send_me_debug(cptr, "T :throttles pending=%d bans=%d", pending, bans);

      LIST_FOREACH(tp, &throttles, lp) {
            time_t zlength = throttle_get_zline_time(tp->stage);
            if (tp->zline_start && tp->zline_start + zlength > timeofday) {
                  send_me_debug(cptr, "T :throttled: %s [stage %d, %d secs remain, %d retries]",
                        tp->addr, tp->stage, (tp->zline_start + zlength) - timeofday, tp->re_zlines);
            }
      }
}

#endif

Generated by  Doxygen 1.6.0   Back to index