/*
* clones.c - Clone detection and limiting
* Copyright (C) 2004 Trevor Talbot and
* the DALnet coding team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: clones.c,v 1.3 2005/07/05 03:17:53 sheik Exp $ */
/*
* WARNING: code is chummy with throttle.c
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "blalloc.h"
#include "numeric.h"
#include "channel.h"
#include "memcount.h"
#include "throttle.h"
#include "clones.h"
#ifndef THROTTLE_ENABLE
extern BlockHeap *hashent_freelist;
#endif
static void *clones_hashtable;
BlockHeap *free_cloneents;
CloneEnt *clones_list;
CloneStat clones_stat;
static CloneEnt *
get_clone(char *key, int create)
{
CloneEnt *ce;
if (!(ce = hash_find(clones_hashtable, key)) && create)
{
ce = BlockHeapALLOC(free_cloneents, CloneEnt);
memset(ce, 0, sizeof(*ce));
strcpy(ce->ent, key);
hash_insert(clones_hashtable, ce);
ce->next = clones_list;
if (clones_list)
clones_list->prev = ce;
clones_list = ce;
}
return ce;
}
static void
expire_clone(CloneEnt *ce)
{
if (ce->gcount || ce->limit || ce->sllimit || ce->sglimit)
return;
if (ce->next)
ce->next->prev = ce->prev;
if (ce->prev)
ce->prev->next = ce->next;
else
clones_list = ce->next;
hash_delete(clones_hashtable, ce);
BlockHeapFree(free_cloneents, ce);
}
#ifdef THROTTLE_ENABLE
static void
get_clones(aClient *cptr, CloneEnt **ceip, CloneEnt **ce24, int create)
{
char ip24[HOSTIPLEN+1];
char *s;
strcpy(ip24, cptr->hostip);
/* deliberate core if strrchr fails -- we need a valid IP string */
s = strrchr(ip24, '.');
*++s = '*';
*++s = 0;
*ceip = get_clone(cptr->hostip, create);
*ce24 = get_clone(ip24, create);
}
static int
report_lclone(aClient *cptr, CloneEnt *ce, int l, int is24, char *t, char *n)
{
if (n)
sendto_realops_lev(REJ_LEV, "clone %s!%s@%s (%s %d/%d local %s %s)",
cptr->name, cptr->user->username, cptr->user->host,
ce->ent, ce->lcount, l, t, n);
else
sendto_realops_lev(REJ_LEV, "clone %s!%s@%s (%s %d/%d local %s)",
cptr->name, cptr->user->username, cptr->user->host,
ce->ent, ce->lcount, l, t);
if (is24)
clones_stat.rls++;
else
clones_stat.rlh++;
throttle_force(cptr->hostip);
return (is24 ? 2 : 1);
}
static int
report_gclone(aClient *cptr, CloneEnt *ce, int l, int is24, char *t)
{
sendto_realops_lev(REJ_LEV, "clone %s!%s@%s (%s %d/%d global %s)",
cptr->name, cptr->user->username, cptr->user->host,
ce->ent, ce->gcount, l, t);
if (is24)
clones_stat.rgs++;
else
clones_stat.rgh++;
throttle_force(cptr->hostip);
return (is24 ? 2 : 1);
}
/*
* Checks a local client against the clone limits.
* Returns 1 if IP/32 limit hit, 2 if IP/24 limit hit, 0 otherwise.
*/
int
clones_check(aClient *cptr)
{
CloneEnt *ceip;
CloneEnt *ce24;
int limit;
int lpri = 0;
int gpri = 0;
get_clones(cptr, &ceip, &ce24, 0);
if (ceip)
{
/* local limit priority stack: soft set, class, default */
if ((limit = ceip->sllimit))
{
lpri = 3;
if (ceip->lcount >= limit)
return report_lclone(cptr, ceip, limit, 0, "soft", NULL);
}
else if ((limit = cptr->user->allow->class->connfreq))
{
lpri = 2;
if (ceip->lcount >= limit)
return report_lclone(cptr, ceip, limit, 0, "class",
cptr->user->allow->class->name);
}
else
{
lpri = 1;
limit = local_ip_limit;
if (ceip->lcount >= limit)
return report_lclone(cptr, ceip, limit, 0, "default", NULL);
}
/* global limit priority stack: soft set, services, default */
if ((limit = ceip->sglimit))
{
gpri = 3;
if (ceip->gcount >= limit)
return report_gclone(cptr, ceip, limit, 0, "soft");
}
else if ((limit = ceip->limit))
{
gpri = 2;
if (ceip->gcount >= limit)
return report_gclone(cptr, ceip, limit, 0, "hard");
}
else
{
gpri = 1;
limit = global_ip_limit;
if (ceip->gcount >= limit)
return report_gclone(cptr, ceip, limit, 0, "default");
}
}
if (ce24)
{
/* For local limits, a specific host limit provides an implicit
* exemption from site limits of a lower priority. */
if ((limit = ce24->sllimit))
{
if (ce24->lcount >= limit)
return report_lclone(cptr, ce24, limit, 1, "soft", NULL);
}
else if (lpri <= 2 && (limit = cptr->user->allow->class->ip24clones))
{
if (ce24->lcount >= limit)
return report_lclone(cptr, ce24, limit, 1, "class",
cptr->user->allow->class->name);
}
else if (lpri <= 1)
{
limit = local_ip24_limit;
if (ce24->lcount >= limit)
return report_lclone(cptr, ce24, limit, 1, "default", NULL);
}
/* For global limits, the implicit exemption is for the default only;
* the soft limit can only be lower, not higher, so the service-set
* hard limit wins if it's not present. */
if ((limit = ce24->sglimit))
{
if (ce24->gcount >= limit)
return report_gclone(cptr, ce24, limit, 1, "soft");
}
else if ((limit = ce24->limit))
{
if (ce24->gcount >= limit)
return report_gclone(cptr, ce24, limit, 1, "hard");
}
else if (gpri <= 1)
{
limit = global_ip24_limit;
if (ce24->gcount >= limit)
return report_gclone(cptr, ce24, limit, 1, "default");
}
}
return 0;
}
/*
* Adds a client to the clone list.
*/
void
clones_add(aClient *cptr)
{
CloneEnt *ceip;
CloneEnt *ce24;
get_clones(cptr, &ceip, &ce24, 1);
cptr->clone.prev = NULL;
cptr->clone.next = ceip->clients;
if (ceip->clients)
ceip->clients->clone.prev = cptr;
ceip->clients = cptr;
ceip->gcount++;
ce24->gcount++;
if (MyConnect(cptr))
{
ceip->lcount++;
ce24->lcount++;
}
}
/*
* Removes a client from the clone list.
*/
void
clones_remove(aClient *cptr)
{
CloneEnt *ceip;
CloneEnt *ce24;
get_clones(cptr, &ceip, &ce24, 0);
if (cptr->clone.next)
cptr->clone.next->clone.prev = cptr->clone.prev;
if (cptr->clone.prev)
cptr->clone.prev->clone.next = cptr->clone.next;
else
ceip->clients = cptr->clone.next;
ceip->gcount--;
ce24->gcount--;
/* !$%#&*%@ user state handling! */
if (cptr->uplink == &me)
{
ceip->lcount--;
ce24->lcount--;
}
expire_clone(ceip);
expire_clone(ce24);
}
#endif /* THROTTLE_ENABLE */
/*
* Sets a global clone limit. A limit of 0 reverts to default settings.
* Returns -1 on invalid parameters, old value otherwise.
*/
int
clones_set(char *ent, int type, int limit)
{
CloneEnt *ce;
int rval = 0;
if (strlen(ent) > HOSTIPLEN)
return -1;
if (limit < 0)
return -1;
ce = get_clone(ent, 1);
switch (type)
{
case CLIM_HARD_GLOBAL:
rval = ce->limit;
ce->limit = limit;
if (limit && ce->sglimit > limit)
ce->sglimit = 0;
break;
case CLIM_SOFT_GLOBAL:
rval = ce->sglimit;
ce->sglimit = limit;
break;
case CLIM_SOFT_LOCAL:
rval = ce->sllimit;
ce->sllimit = limit;
break;
}
expire_clone(ce);
return rval;
}
/*
* Gets the current clone limits. 0 means using default.
*/
void clones_get(char *ent, int *hglimit, int *sglimit, int *sllimit)
{
CloneEnt *ce;
ce = get_clone(ent, 0);
if (ce)
{
*hglimit = ce->limit;
*sglimit = ce->sglimit;
*sllimit = ce->sllimit;
}
else
{
*hglimit = 0;
*sglimit = 0;
*sllimit = 0;
}
}
/*
* Propagate global clone limits.
*/
void
clones_send(aClient *cptr)
{
CloneEnt *ce;
for (ce = clones_list; ce; ce = ce->next)
{
if (!ce->limit)
continue;
sendto_one(cptr, ":%s SVSCLONE %s %d", me.name, ce->ent, ce->limit);
}
}
/*
* Must be called AFTER throttle_init()
*/
void
clones_init(void)
{
#ifndef THROTTLE_ENABLE
hashent_freelist = BlockHeapCreate(sizeof(hashent), 1024);
#endif
free_cloneents = BlockHeapCreate(sizeof(CloneEnt), 1024);
clones_hashtable = create_hash_table(THROTTLE_HASHSIZE,
offsetof(CloneEnt, ent), HOSTIPLEN,
2, (void *)strcmp);
}
u_long
memcount_clones(MCclones *mc)
{
CloneEnt *ce;
mc->file = __FILE__;
for (ce = clones_list; ce; ce = ce->next)
mc->e_cloneents++;
mc->e_heap = free_cloneents;
mc->e_hash = clones_hashtable;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1