/************************************************************************
* IRC - Internet Relay Chat, src/userban.c
* Copyright (C) 2002 Lucas Madar and
* the DALnet coding team
*
* 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: userban.c,v 1.12 2006/06/25 21:03:35 sheik Exp $ */
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "inet.h"
#include "h.h"
#include "userban.h"
#include "queue.h"
#include "memcount.h"
#define HASH_SIZE (32749) /* largest prime < 32768 */
LIST_HEAD(banlist_t, userBanEntry);
typedef struct banlist_t ban_list;
typedef struct userBanEntry {
struct userBan *ban;
LIST_ENTRY(userBanEntry) lp;
} uBanEnt;
typedef struct _abanlist {
ban_list wild_list;
int numbuckets;
ban_list *hash_list;
} aBanList;
typedef struct userBan auserBan;
ban_list CIDR4BIG_bans = LIST_HEAD_INITIALIZER(CIDR4BIG_bans);
ban_list **CIDR4_bans;
aBanList host_bans;
aBanList ip_bans;
aBanList gcos_bans;
aBanList nick_bans;
aBanList chan_bans;
struct userBan *userban_alloc();
struct simBan *simban_alloc();
uBanEnt *ubanent_alloc();
void ubanent_free(uBanEnt *);
void userban_free(struct userBan *);
void simban_free(struct simBan *);
unsigned int host_hash(char *n);
unsigned int ip_hash(char *n);
unsigned int cidr_to_netmask(unsigned int cidr)
{
if (cidr == 0)
return 0;
return (0xFFFFFFFF - (1 << (32 - cidr)) + 1);
}
unsigned int netmask_to_cidr(unsigned int mask)
{
int tmp = 0;
while (!(mask & (1 << tmp)) && tmp < 32)
tmp++;
return (32 - tmp);
}
/* userban (akill/kline) functions */
void add_hostbased_userban(struct userBan *b)
{
uBanEnt *bl;
bl = ubanent_alloc();
bl->ban = b;
b->internal_ent = (void *) bl;
if(b->flags & UBAN_CIDR4BIG)
{
LIST_INSERT_HEAD(&CIDR4BIG_bans, bl, lp);
return;
}
if(b->flags & UBAN_CIDR4)
{
unsigned char *s = (unsigned char *) &bl->ban->cidr4ip;
int a, b;
a = (int) *s++;
b = (int) *s;
LIST_INSERT_HEAD(&CIDR4_bans[a][b], bl, lp);
return;
}
if(b->flags & UBAN_IP)
{
if(b->flags & UBAN_WILD)
{
LIST_INSERT_HEAD(&ip_bans.wild_list, bl, lp);
}
else
{
unsigned int hv = ip_hash(b->h) % HASH_SIZE;
LIST_INSERT_HEAD(&ip_bans.hash_list[hv], bl, lp);
}
return;
}
if(b->flags & UBAN_HOST)
{
if(b->flags & UBAN_WILD)
{
LIST_INSERT_HEAD(&host_bans.wild_list, bl, lp);
}
else
{
unsigned int hv = host_hash(b->h) % HASH_SIZE;
LIST_INSERT_HEAD(&host_bans.hash_list[hv], bl, lp);
}
return;
}
/* unreachable code */
abort();
}
void remove_userban(struct userBan *b)
{
uBanEnt *bl = (uBanEnt *) b->internal_ent;
LIST_REMOVE(bl, lp);
ubanent_free(bl);
return;
}
/*
* user_match_ban -- be sure to call only for fully-initialized users
* returns 0 on no match, 1 otherwise
*/
int user_match_ban(aClient *cptr, struct userBan *ban)
{
/* first match the 'user' portion */
if((!(ban->flags & UBAN_WILDUSER)) && match(ban->u, cptr->user->username))
return 0;
if(ban->flags & UBAN_IP)
{
char iptmp[HOSTIPLEN + 1];
strncpyzt(iptmp, inetntoa((char *)&cptr->ip), HOSTIPLEN + 1);
if(ban->flags & UBAN_WILD)
{
if(match(ban->h, iptmp) == 0)
return 1;
}
else
{
if(mycmp(ban->h, iptmp) == 0)
return 1;
}
return 0;
}
if(ban->flags & UBAN_HOST)
{
if(ban->flags & UBAN_WILD)
{
if((ban->flags & UBAN_WILDHOST) || match(ban->h, cptr->user->realhost) == 0)
return 1;
}
else
{
if(mycmp(ban->h, cptr->user->realhost) == 0)
return 1;
}
return 0;
}
if(ban->flags & (UBAN_CIDR4|UBAN_CIDR4BIG))
{
if((cptr->ip.s_addr & ban->cidr4mask) == ban->cidr4ip)
return 1;
return 0;
}
return 0;
}
struct userBan *check_userbanned(aClient *cptr, unsigned int yflags, unsigned int nflags)
{
char iptmp[HOSTIPLEN + 1];
uBanEnt *bl;
strncpyzt(iptmp, inetntoa((char *)&cptr->ip), HOSTIPLEN + 1);
if(yflags & UBAN_IP)
{
unsigned int hv = ip_hash(iptmp) % HASH_SIZE;
LIST_FOREACH(bl, &ip_bans.hash_list[hv], lp)
{
if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
continue;
if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
continue;
if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username))
continue;
if(mycmp(bl->ban->h, iptmp) == 0)
return bl->ban;
}
LIST_FOREACH(bl, &ip_bans.wild_list, lp)
{
if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
continue;
if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
continue;
if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username))
continue;
if(match(bl->ban->h, iptmp) == 0)
return bl->ban;
}
}
if(yflags & UBAN_CIDR4)
{
unsigned char *s = (unsigned char *) &cptr->ip.s_addr;
int a, b;
a = (int) *s++;
b = (int) *s;
LIST_FOREACH(bl, &CIDR4_bans[a][b], lp)
{
if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
continue;
if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
continue;
if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username))
continue;
if((cptr->ip.s_addr & bl->ban->cidr4mask) == bl->ban->cidr4ip)
return bl->ban;
}
LIST_FOREACH(bl, &CIDR4BIG_bans, lp)
{
if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
continue;
if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
continue;
if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username))
continue;
if((cptr->ip.s_addr & bl->ban->cidr4mask) == bl->ban->cidr4ip)
return bl->ban;
}
}
if(yflags & UBAN_HOST)
{
unsigned int hv = host_hash(cptr->user->realhost) % HASH_SIZE;
LIST_FOREACH(bl, &host_bans.hash_list[hv], lp)
{
if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
continue;
if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
continue;
if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username))
continue;
if(mycmp(bl->ban->h, cptr->user->realhost) == 0)
return bl->ban;
}
LIST_FOREACH(bl, &host_bans.wild_list, lp)
{
if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
continue;
if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
continue;
if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username))
continue;
if((bl->ban->flags & UBAN_WILDHOST) || match(bl->ban->h, cptr->user->realhost) == 0)
return bl->ban;
}
}
return NULL;
}
struct userBan *find_userban_exact(struct userBan *borig, unsigned int careflags)
{
uBanEnt *bl;
if(borig->flags & UBAN_CIDR4BIG)
{
LIST_FOREACH(bl, &CIDR4BIG_bans, lp) {
/* must have same wilduser, etc setting */
if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
continue;
/* user fields do not match? */
if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
continue;
if(!((borig->cidr4ip == bl->ban->cidr4ip) && (borig->cidr4mask == bl->ban->cidr4mask)))
continue;
return bl->ban;
}
return NULL;
}
if(borig->flags & UBAN_CIDR4)
{
unsigned char *s = (unsigned char *) &borig->cidr4ip;
int a, b;
a = (int) *s++;
b = (int) *s;
LIST_FOREACH(bl, &CIDR4_bans[a][b], lp) {
if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
continue;
if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
continue;
if(!((borig->cidr4ip == bl->ban->cidr4ip) && (borig->cidr4mask == bl->ban->cidr4mask)))
continue;
return bl->ban;
}
return NULL;
}
if(borig->flags & UBAN_IP)
{
if(borig->flags & UBAN_WILD)
{
LIST_FOREACH(bl, &ip_bans.wild_list, lp) {
if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
continue;
if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
continue;
if(mycmp(borig->h, bl->ban->h))
continue;
return bl->ban;
}
}
else
{
unsigned int hv = ip_hash(borig->h) % HASH_SIZE;
LIST_FOREACH(bl, &ip_bans.hash_list[hv], lp) {
if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
continue;
if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
continue;
if(mycmp(borig->h, bl->ban->h))
continue;
return bl->ban;
}
}
return NULL;
}
if(borig->flags & UBAN_HOST)
{
if(borig->flags & UBAN_WILD)
{
LIST_FOREACH(bl, &host_bans.wild_list, lp) {
if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
continue;
if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
continue;
if(mycmp(borig->h, bl->ban->h))
continue;
return bl->ban;
}
}
else
{
unsigned int hv = host_hash(borig->h) % HASH_SIZE;
LIST_FOREACH(bl, &host_bans.hash_list[hv], lp) {
if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
continue;
if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
continue;
if(mycmp(borig->h, bl->ban->h))
continue;
return bl->ban;
}
}
return NULL;
}
/* unreachable code */
abort();
}
static inline void expire_list(uBanEnt *bl)
{
uBanEnt *bln;
aClient *acptr;
struct userBan *ban;
int i;
while(bl)
{
bln = LIST_NEXT(bl, lp);
ban = bl->ban;
if((ban->flags & UBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
{
if (ban->flags & UBAN_SHUN)
{
for (i = 0; i <= highest_fd; i++)
{
if (!(acptr = local[i]) || IsMe(acptr) || IsLog(acptr))
continue;
if (IsPerson(acptr) && IsShunned(acptr) && user_match_ban(acptr, ban))
{
ClearShun(acptr);
i--;
}
}
}
remove_userban(ban);
userban_free(ban);
}
bl = bln;
}
}
static inline void remove_list_match_flags(uBanEnt *bl, unsigned int flags, unsigned int nflags)
{
uBanEnt *bln;
struct userBan *ban;
while(bl)
{
bln = LIST_NEXT(bl, lp);
ban = bl->ban;
if((flags == 0 && nflags == 0) || (((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0)))
{
remove_userban(ban);
userban_free(ban);
}
bl = bln;
}
}
static inline void report_list_match_flags(aClient *cptr, uBanEnt *bl, unsigned int flags, unsigned int nflags, char rchar)
{
struct userBan *ban;
char kset[8];
char host[128];
while(bl)
{
ban = bl->ban;
if((flags == 0 && nflags == 0) || (((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0)))
{
if(ban->flags & UBAN_LOCAL)
{
if(ban->flags & UBAN_TEMPORARY)
kset[0] = 'k';
else
kset[0] = 'K';
}
else if (ban->flags & UBAN_GLINE)
{
kset[0] = 'g';
}
else if (ban->flags & UBAN_SHUN)
{
kset[0] = 's';
}
else
{
kset[0] = 'a';
}
kset[1] = rchar;
kset[2] = '\0';
if(ban->flags & (UBAN_CIDR4|UBAN_CIDR4BIG))
snprintf(host, 128, "%s/%d", inetntoa((char *)&ban->cidr4ip), netmask_to_cidr(ntohl(ban->cidr4mask)));
else
strcpy(host, ban->h);
sendto_one(cptr, rpl_str(RPL_STATSKLINE), me.name,
cptr->name, kset, host,
(ban->flags & UBAN_WILDUSER) ? "*" : ban->u,
(ban->flags & UBAN_TEMPORARY) ? (((ban->timeset + ban->duration) - NOW) / 60) : -1,
(ban->reason) ? ban->reason : "No reason");
}
bl = LIST_NEXT(bl, lp);
}
}
void expire_userbans()
{
uBanEnt *bl;
int a, b;
bl = LIST_FIRST(&CIDR4BIG_bans);
expire_list(bl);
for(a = 0; a < 256; a++)
{
for(b = 0; b < 256; b++)
{
bl = LIST_FIRST(&CIDR4_bans[a][b]);
expire_list(bl);
}
}
bl = LIST_FIRST(&host_bans.wild_list);
expire_list(bl);
bl = LIST_FIRST(&ip_bans.wild_list);
expire_list(bl);
for(a = 0; a < HASH_SIZE; a++)
{
bl = LIST_FIRST(&host_bans.hash_list[a]);
expire_list(bl);
bl = LIST_FIRST(&ip_bans.hash_list[a]);
expire_list(bl);
}
}
void remove_userbans_match_flags(unsigned int flags, unsigned int nflags)
{
uBanEnt *bl;
int a, b;
bl = LIST_FIRST(&CIDR4BIG_bans);
remove_list_match_flags(bl, flags, nflags);
for(a = 0; a < 256; a++)
{
for(b = 0; b < 256; b++)
{
bl = LIST_FIRST(&CIDR4_bans[a][b]);
remove_list_match_flags(bl, flags, nflags);
}
}
bl = LIST_FIRST(&host_bans.wild_list);
remove_list_match_flags(bl, flags, nflags);
bl = LIST_FIRST(&ip_bans.wild_list);
remove_list_match_flags(bl, flags, nflags);
for(a = 0; a < HASH_SIZE; a++)
{
bl = LIST_FIRST(&host_bans.hash_list[a]);
remove_list_match_flags(bl, flags, nflags);
bl = LIST_FIRST(&ip_bans.hash_list[a]);
remove_list_match_flags(bl, flags, nflags);
}
}
void report_userbans_match_flags(aClient *cptr, unsigned int flags, unsigned int nflags)
{
uBanEnt *bl;
int a, b;
bl = LIST_FIRST(&CIDR4BIG_bans);
report_list_match_flags(cptr, bl, flags, nflags, 'C');
for(a = 0; a < 256; a++)
{
for(b = 0; b < 256; b++)
{
bl = LIST_FIRST(&CIDR4_bans[a][b]);
report_list_match_flags(cptr, bl, flags, nflags, 'c');
}
}
bl = LIST_FIRST(&host_bans.wild_list);
report_list_match_flags(cptr, bl, flags, nflags, 'h');
bl = LIST_FIRST(&ip_bans.wild_list);
report_list_match_flags(cptr, bl, flags, nflags, 'i');
for(a = 0; a < HASH_SIZE; a++)
{
bl = LIST_FIRST(&host_bans.hash_list[a]);
report_list_match_flags(cptr, bl, flags, nflags, 'H');
bl = LIST_FIRST(&ip_bans.hash_list[a]);
report_list_match_flags(cptr, bl, flags, nflags, 'I');
}
}
char *get_userban_host(struct userBan *ban, char *buf, int buflen)
{
*buf = '\0';
if(ban->flags & (UBAN_CIDR4|UBAN_CIDR4BIG))
snprintf(buf, buflen, "%s/%d", inetntoa((char *)&ban->cidr4ip), netmask_to_cidr(ntohl(ban->cidr4mask)));
else
snprintf(buf, buflen, "%s", ban->h);
return buf;
}
/*
* Fills in the following fields
* of a userban structure, or returns NULL if invalid stuff is passed.
* - flags, u, h, cidr4ip, cidr4mask
*/
struct userBan *make_hostbased_ban(char *user, char *phost)
{
char host[512];
unsigned int flags = 0, c4h = 0, c4m = 0;
int numcount, othercount, wildcount, dotcount, slashcount;
char *tmp;
struct userBan *b;
strncpy(host, phost, 512);
numcount = othercount = wildcount = dotcount = slashcount = 0;
for(tmp = host; *tmp; tmp++)
{
switch(*tmp)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
numcount++;
break;
case '*':
case '?':
wildcount++;
break;
case '.':
dotcount++;
break;
case '/':
slashcount++;
break;
default:
othercount++;
break;
}
}
if(wildcount && !numcount && !othercount)
{
if(!user || !*user || mycmp(user, "*") == 0)
return NULL; /* all wildcards? aagh! */
flags = (UBAN_HOST|UBAN_WILD);
if(mycmp(host, "*.*") == 0 || mycmp(host, "*") == 0)
flags |= UBAN_WILDHOST;
goto success;
}
/* everything must have a dot. never more than one slash. */
if(dotcount == 0 || slashcount > 1)
return NULL;
/* wildcarded IP address? -- can we convert it to a CIDR? */
if(wildcount && numcount && !othercount)
{
char octet[4][8];
int i1, i2;
int gotwild;
if(slashcount)
return NULL; /* slashes and wildcards? */
/* I see... more than 3 dots? */
if(dotcount > 3)
return NULL;
i1 = i2 = 0;
/* separate this thing into dotcount octets. */
for(tmp = host; *tmp; tmp++)
{
if(*tmp == '.')
{
octet[i1][i2] = '\0';
i2 = 0;
i1++;
continue;
}
if(i2 < 6)
{
octet[i1][i2++] = *tmp;
}
}
octet[i1][i2] = '\0';
/* verify that each octet is all numbers or just a '*' */
/* bans that match 123.123.123.1?? are still valid, just not convertable to a CIDR */
for(gotwild = i1 = 0; i1 <= dotcount; i1++)
{
if(strcmp(octet[i1], "*") == 0)
{
gotwild++;
continue;
}
/* ban in the format of 1.2.*.4 */
if(gotwild)
{
flags = (UBAN_IP|UBAN_WILD);
goto success;
}
for(i2 = 0; octet[i1][i2]; i2++)
{
switch(octet[i1][i2])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
default:
flags = (UBAN_IP|UBAN_WILD);
goto success;
}
}
}
if(octet[0][0] == '*')
return NULL; /* the first octet is a wildcard? what the hell? */
if(octet[1][0] == '*')
{
sprintf(host, "%s.0.0.0/8", octet[0]);
goto cidrforce;
}
else if(dotcount >= 2 && octet[2][0] == '*')
{
sprintf(host, "%s.%s.0.0/16", octet[0], octet[1]);
goto cidrforce;
}
else if(dotcount >= 3 && octet[3][0] == '*')
{
sprintf(host, "%s.%s.%s.0/24", octet[0], octet[1], octet[2]);
goto cidrforce;
}
return NULL; /* we should never get here. If we do, something is wrong. */
}
/* CIDR IP4 address? */
if(!wildcount && numcount && !othercount && slashcount)
{
int sval;
char *sep, *err;
struct in_addr ia, na;
cidrforce:
sep = strchr(host, '/'); /* guaranteed to be here because slashcount */
*sep = '\0';
sep++;
if((ia.s_addr = inet_addr(host)) == 0xFFFFFFFF) /* invalid ip4 address! */
return NULL;
/* is there a problem with the / mask? */
sval = strtol(sep, &err, 10);
if(*err != '\0')
return NULL;
if(sval < 0 || sval > 32)
return NULL;
na.s_addr = htonl(cidr_to_netmask(sval));
ia.s_addr &= na.s_addr;
c4h = ia.s_addr;
c4m = na.s_addr;
flags = (sval < 16) ? UBAN_CIDR4BIG : UBAN_CIDR4;
goto success;
}
if(slashcount)
return NULL;
if(!othercount)
{
flags = (UBAN_IP | (wildcount ? UBAN_WILD : 0));
goto success;
}
flags = (UBAN_HOST | (wildcount ? UBAN_WILD : 0));
success:
b = userban_alloc();
if(!b)
return NULL;
b->reason = NULL;
if(flags & (UBAN_CIDR4BIG|UBAN_CIDR4))
{
b->cidr4ip = c4h;
b->cidr4mask = c4m;
b->h = NULL;
}
else
{
b->cidr4ip = b->cidr4mask = 0;
b->h = (char *)MyMalloc(strlen(host) + 1);
strcpy(b->h, host);
}
if(!user || !*user || mycmp(user, "*") == 0)
{
flags |= UBAN_WILDUSER;
b->u = NULL;
}
else
{
b->u = (char *)MyMalloc(strlen(user) + 1);
strcpy(b->u, user);
}
b->flags = flags;
return b;
}
/* simban (simple ban) functions */
/*
* make_simpleban does only simple sanity checking.
* You must pass it one of each of the following flags in 'flags', or'd together:
* SBAN_GCOS (gline) or SBAN_NICK (qline) or SBAN_CHAN (channel qline)
* SBAN_LOCAL or SBAN_NETWORK (self-explanatory)
*/
struct simBan *make_simpleban(unsigned int flags, char *mask)
{
char *tmp;
struct simBan *b;
int wildcount = 0, othercount = 0;
for(tmp = mask; *tmp; tmp++)
{
switch(*tmp)
{
case '*':
case '?':
wildcount++;
break;
default:
othercount++;
break;
}
}
if((flags & (SBAN_NETWORK|SBAN_LOCAL)) == 0)
return NULL;
if((flags & (SBAN_NICK|SBAN_GCOS|SBAN_CHAN)) == 0)
return NULL;
if(othercount == 0)
return NULL; /* No bans consisting only of wildcards */
if(wildcount)
flags |= SBAN_WILD;
b = simban_alloc();
if(!b)
return NULL;
b->reason = NULL;
b->mask = (char *) MyMalloc(strlen(mask) + 1);
strcpy(b->mask, mask);
b->flags = flags;
return b;
}
void add_simban(struct simBan *b)
{
uBanEnt *bl;
aBanList *banlist;
ban_list *thelist;
bl = ubanent_alloc();
bl->ban = (struct userBan *) b;
b->internal_ent = (void *) bl;
if(b->flags & SBAN_NICK)
banlist = &nick_bans;
else if(b->flags & SBAN_CHAN)
banlist = &chan_bans;
else if(b->flags & SBAN_GCOS)
banlist = &gcos_bans;
else
abort(); /* ack! */
if(b->flags & SBAN_WILD)
{
thelist = &banlist->wild_list;
}
else
{
unsigned int hv = host_hash(b->mask) % HASH_SIZE;
thelist = &banlist->hash_list[hv];
}
LIST_INSERT_HEAD(thelist, bl, lp);
}
void remove_simban(struct simBan *b)
{
uBanEnt *bl = (uBanEnt *) b->internal_ent;
LIST_REMOVE(bl, lp);
ubanent_free(bl);
return;
}
struct simBan *find_simban_exact(struct simBan *borig)
{
uBanEnt *bl;
struct simBan *ban;
aBanList *banlist;
ban_list *thelist;
if(borig->flags & SBAN_NICK)
banlist = &nick_bans;
else if(borig->flags & SBAN_CHAN)
banlist = &chan_bans;
else if(borig->flags & SBAN_GCOS)
banlist = &gcos_bans;
else
return NULL;
if(borig->flags & SBAN_WILD)
{
thelist = &banlist->wild_list;
}
else
{
unsigned int hv = host_hash(borig->mask) % HASH_SIZE;
thelist = &banlist->hash_list[hv];
}
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if(ban->flags != borig->flags)
continue;
if(mycmp(ban->mask, borig->mask))
continue;
return ban;
}
return NULL;
}
/* does cptr match the ban specified in b?
* return: 0 = no
*/
int user_match_simban(aClient *cptr, struct simBan *b)
{
char *userinfo;
int (*chkfnc)(char *, char *);
if(b->flags & SBAN_NICK)
userinfo = cptr->name;
else if(b->flags & SBAN_CHAN)
return 0; /* not applicable */
else if(b->flags & SBAN_GCOS)
userinfo = cptr->info;
else
abort(); /* aagh! */
if(b->flags & SBAN_WILD)
chkfnc = match;
else
chkfnc = mycmp;
if(chkfnc(b->mask, userinfo) == 0)
return 1;
return 0;
}
struct simBan *check_mask_simbanned(char *mask, unsigned int flags)
{
uBanEnt *bl;
struct simBan *ban;
aBanList *banlist;
ban_list *thelist;
int (*chkfnc)(char *, char *);
unsigned int hv;
if(flags & SBAN_NICK)
banlist = &nick_bans;
else if(flags & SBAN_CHAN)
banlist = &chan_bans;
else if(flags & SBAN_GCOS)
banlist = &gcos_bans;
else
abort(); /* aagh! */
hv = host_hash(mask) % HASH_SIZE;
thelist = &banlist->hash_list[hv];
chkfnc = mycmp;
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
continue;
if(chkfnc(ban->mask, mask))
continue;
return ban;
}
thelist = &banlist->wild_list;
chkfnc = match;
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
continue;
if(chkfnc(ban->mask, mask))
continue;
return ban;
}
return NULL;
}
void report_simbans_match_flags(aClient *cptr, unsigned int flags, unsigned int nflags)
{
uBanEnt *bl;
struct simBan *ban;
aBanList *banlist;
ban_list *thelist;
unsigned int hv;
char sbuf[16];
int slen;
if(flags & SBAN_NICK)
banlist = &nick_bans;
else if(flags & SBAN_CHAN)
banlist = &chan_bans;
else if(flags & SBAN_GCOS)
banlist = &gcos_bans;
else
abort(); /* aagh! */
for(hv = 0; hv < HASH_SIZE; hv++)
{
thelist = &banlist->hash_list[hv];
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
continue;
if(((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0))
{
int rpl = RPL_STATSQLINE;
slen = 0;
if(flags & SBAN_NICK)
{
sbuf[slen++] = (flags & SBAN_LOCAL) ? 'Q' : 'q';
sbuf[slen++] = 'n';
}
else if(flags & SBAN_CHAN)
{
sbuf[slen++] = (flags & SBAN_LOCAL) ? 'Q' : 'q';
sbuf[slen++] = 'c';
}
else if(flags & SBAN_GCOS)
{
rpl = RPL_STATSGLINE;
sbuf[slen++] = (flags & SBAN_LOCAL) ? 'G' : 'g';
}
sbuf[slen] = '\0';
sendto_one(cptr, rpl_str(rpl), me.name, cptr->name,
sbuf,
ban->mask,
(ban->flags & SBAN_TEMPORARY) ? (((ban->timeset + ban->duration) - NOW) / 60) : -1,
ban->reason ? ban->reason : "No Reason");
}
}
}
thelist = &banlist->wild_list;
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
continue;
if(((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0))
{
int rpl = RPL_STATSQLINE;
slen = 0;
if(flags & SBAN_NICK)
{
sbuf[slen++] = (flags & SBAN_LOCAL) ? 'Q' : 'q';
sbuf[slen++] = 'n';
}
else if(flags & SBAN_CHAN)
{
sbuf[slen++] = (flags & SBAN_LOCAL) ? 'Q' : 'q';
sbuf[slen++] = 'c';
}
else if(flags & SBAN_GCOS)
{
rpl = RPL_STATSGLINE;
sbuf[slen++] = (flags & SBAN_LOCAL) ? 'G' : 'g';
}
sbuf[slen++] = 'w';
sbuf[slen] = '\0';
sendto_one(cptr, rpl_str(rpl), me.name, cptr->name,
sbuf,
ban->mask,
(ban->flags & SBAN_TEMPORARY) ? (((ban->timeset + ban->duration) - NOW) / 60) : -1,
ban->reason ? ban->reason : "No Reason");
}
}
}
void remove_simbans_match_flags(unsigned int flags, unsigned int nflags)
{
uBanEnt *bl;
struct simBan *ban;
aBanList *banlist;
ban_list *thelist;
unsigned int hv;
if(flags & SBAN_NICK)
banlist = &nick_bans;
else if(flags & SBAN_CHAN)
banlist = &chan_bans;
else if(flags & SBAN_GCOS)
banlist = &gcos_bans;
else
abort(); /* aagh! */
for(hv = 0; hv < HASH_SIZE; hv++)
{
thelist = &banlist->hash_list[hv];
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
continue;
if(((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0))
{
/* Kludge it out! */
ban->flags |= SBAN_TEMPORARY;
ban->timeset = NOW - 5;
ban->duration = 1;
}
}
}
thelist = &banlist->wild_list;
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
continue;
if(((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0))
{
/* Kludge it out! */
ban->flags |= SBAN_TEMPORARY;
ban->timeset = NOW - 5;
ban->duration = 1;
}
}
}
void send_simbans(aClient *cptr, unsigned int flags)
{
uBanEnt *bl;
struct simBan *ban;
aBanList *banlist;
ban_list *thelist;
unsigned int hv;
if(flags & SBAN_NICK)
banlist = &nick_bans;
else if(flags & SBAN_CHAN)
banlist = &chan_bans;
else if(flags & SBAN_GCOS)
banlist = &gcos_bans;
else
abort(); /* aagh! */
for(hv = 0; hv < HASH_SIZE; hv++)
{
thelist = &banlist->hash_list[hv];
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if(ban->flags & SBAN_TEMPORARY)
continue;
if((ban->flags & flags) == flags)
{
if(ban->flags & SBAN_GCOS)
sendto_one(cptr, ":%s SGLINE %d :%s:%s", me.name, strlen(ban->mask),
ban->mask, ban->reason);
else
sendto_one(cptr, ":%s SQLINE %s :%s", me.name,
ban->mask, ban->reason);
}
}
}
thelist = &banlist->wild_list;
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if(ban->flags & SBAN_TEMPORARY)
continue;
if((ban->flags & flags) == flags)
{
if(ban->flags & SBAN_GCOS)
sendto_one(cptr, ":%s SGLINE %d :%s:%s", me.name, strlen(ban->mask),
ban->mask, ban->reason);
else
sendto_one(cptr, ":%s SQLINE %s :%s", me.name,
ban->mask, ban->reason);
}
}
}
void remove_simbans_match_mask(unsigned int flags, char *mask, int wild)
{
uBanEnt *bl;
struct simBan *ban;
aBanList *banlist;
ban_list *thelist;
unsigned int hv;
int (*chkfnc)(char *, char *);
chkfnc = wild ? match : mycmp;
if(flags & SBAN_NICK)
banlist = &nick_bans;
else if(flags & SBAN_CHAN)
banlist = &chan_bans;
else if(flags & SBAN_GCOS)
banlist = &gcos_bans;
else
abort(); /* aagh! */
for(hv = 0; hv < HASH_SIZE; hv++)
{
thelist = &banlist->hash_list[hv];
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
continue;
if((ban->flags & flags) != flags)
continue;
if(chkfnc(mask, ban->mask) == 0)
{
/* Kludge it out! */
ban->flags |= SBAN_TEMPORARY;
ban->timeset = NOW - 5;
ban->duration = 1;
}
}
}
thelist = &banlist->wild_list;
LIST_FOREACH(bl, thelist, lp)
{
ban = (struct simBan *) bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
continue;
if((ban->flags & flags) != flags)
continue;
if(chkfnc(mask, ban->mask) == 0)
{
/* Kludge it out! */
ban->flags |= SBAN_TEMPORARY;
ban->timeset = NOW - 5;
ban->duration = 1;
}
}
}
static inline void expire_simlist(uBanEnt *bl)
{
uBanEnt *bln;
struct simBan *ban;
while(bl)
{
bln = LIST_NEXT(bl, lp);
ban = (struct simBan *)bl->ban;
if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
{
remove_simban(ban);
simban_free(ban);
}
bl = bln;
}
}
void expire_simbans()
{
uBanEnt *bl;
int a;
bl = LIST_FIRST(&nick_bans.wild_list);
expire_simlist(bl);
bl = LIST_FIRST(&chan_bans.wild_list);
expire_simlist(bl);
bl = LIST_FIRST(&gcos_bans.wild_list);
expire_simlist(bl);
for(a = 0; a < HASH_SIZE; a++)
{
bl = LIST_FIRST(&nick_bans.hash_list[a]);
expire_simlist(bl);
bl = LIST_FIRST(&chan_bans.hash_list[a]);
expire_simlist(bl);
bl = LIST_FIRST(&gcos_bans.hash_list[a]);
expire_simlist(bl);
}
}
/* Hash and init functions */
unsigned int ip_hash(char *n)
{
unsigned int hv = 0;
while(*n)
{
hv = hv * 33 + tolowertab[(unsigned char) *n++];
}
return hv;
}
unsigned int host_hash(char *n)
{
unsigned int hv = 0;
while(*n)
{
if(*n != '.')
{
hv <<= 5;
hv |= ((touppertab[(unsigned char) *n]) - 65) & 0xFF;
}
n++;
}
return hv;
}
void init_banlist(aBanList *a, int numbuckets)
{
memset(a, 0, sizeof(aBanList));
a->numbuckets = numbuckets;
a->hash_list = (ban_list *) MyMalloc(numbuckets * sizeof(ban_list));
memset(a->hash_list, 0, numbuckets * sizeof(ban_list));
}
void init_userban()
{
int i;
CIDR4_bans = (ban_list **) MyMalloc(256 * sizeof(ban_list *));
for(i = 0; i < 256; i++)
{
CIDR4_bans[i] = (ban_list *) MyMalloc(256 * sizeof(ban_list));
memset(CIDR4_bans[i], 0, 256 * sizeof(ban_list));
}
init_banlist(&host_bans, HASH_SIZE);
init_banlist(&ip_bans, HASH_SIZE);
init_banlist(&gcos_bans, HASH_SIZE);
init_banlist(&nick_bans, HASH_SIZE);
init_banlist(&chan_bans, HASH_SIZE);
}
unsigned int userban_count = 0, ubanent_count = 0, simban_count = 0;
struct userBan *userban_alloc()
{
struct userBan *b;
b = (struct userBan *) MyMalloc(sizeof(struct userBan));
if(b)
{
memset(b, 0, sizeof(struct userBan));
userban_count++;
}
return b;
}
void userban_free(struct userBan *b)
{
if(b->u)
MyFree(b->u);
if(b->h)
MyFree(b->h);
if(b->reason)
MyFree(b->reason);
userban_count--;
MyFree(b);
}
uBanEnt *ubanent_alloc()
{
uBanEnt *b;
b = (uBanEnt *) MyMalloc(sizeof(uBanEnt));
if(b)
{
memset(b, 0, sizeof(uBanEnt));
ubanent_count++;
}
return b;
}
void ubanent_free(uBanEnt *b)
{
ubanent_count--;
MyFree(b);
}
struct simBan *simban_alloc()
{
struct simBan *b;
b = (struct simBan *) MyMalloc(sizeof(struct simBan));
if(b)
{
memset(b, 0, sizeof(struct simBan));
simban_count++;
}
return b;
}
void simban_free(struct simBan *b)
{
if(b->mask)
MyFree(b->mask);
if(b->reason)
MyFree(b->reason);
simban_count--;
MyFree(b);
}
/*
* Dump all local connections that match a userban.
*/
void userban_sweep(struct userBan *ban)
{
char rbuf[512];
aClient *acptr;
char *reason;
char *btext;
char *ntext;
int clientonly = 1;
int i;
if (ban->flags & UBAN_NETWORK) {
btext = NETWORK_BANNED_NAME;
ntext = NETWORK_BAN_NAME;
} else if (ban->flags & UBAN_GLINE) { /* Also, include glines now */
btext = NETWORK_GLINNED_NAME;
ntext = NETWORK_GLINE_NAME;
} else {
btext = LOCAL_BANNED_NAME;
ntext = LOCAL_BAN_NAME;
}
if (!(reason = ban->reason))
reason = "<no reason>";
/* if it's purely IP based, dump unregistered and server connections too */
if (ban->flags & UBAN_WILDUSER)
if (ban->flags & (UBAN_IP|UBAN_CIDR4|UBAN_CIDR4BIG))
clientonly = 0;
ircsnprintf(rbuf, sizeof(rbuf), "%s: %s", btext, reason);
for (i = 0; i <= highest_fd; i++)
{
if (!(acptr = local[i]) || acptr->status < STAT_UNKNOWN)
continue;
if (clientonly && !IsPerson(acptr))
continue;
if (user_match_ban(acptr, ban))
{
sendto_ops("%s active for %s", btext,
get_client_name(acptr, FALSE));
exit_client(acptr, acptr, &me, rbuf);
i--;
}
}
}
/*
* ks_dumpklines() helper
*/
static void
ks_dumplist(int f, uBanEnt *be)
{
struct userBan *ub;
/* klines.c */
extern void ks_write(int, char, struct userBan *);
for (; be; be = LIST_NEXT(be, lp))
{
ub = be->ban;
/* must be local and not from conf */
if ((ub->flags & (UBAN_LOCAL|UBAN_CONF)) != UBAN_LOCAL)
continue;
/* must be over the storage threshold duration */
if ((ub->flags & UBAN_TEMPORARY)
&& ub->duration < (KLINE_MIN_STORE_TIME * 60))
continue;
ks_write(f, '+', ub);
}
}
/*
* Called from klines.c during a storage GC.
*/
void
ks_dumpklines(int f)
{
int i, j;
for (i = 0; i < 256; i++)
for (j = 0; j < 256; j++)
ks_dumplist(f, LIST_FIRST(&CIDR4_bans[i][j]));
ks_dumplist(f, LIST_FIRST(&CIDR4BIG_bans));
ks_dumplist(f, LIST_FIRST(&host_bans.wild_list));
ks_dumplist(f, LIST_FIRST(&ip_bans.wild_list));
for (i = 0; i < HASH_SIZE; i++)
{
ks_dumplist(f, LIST_FIRST(&host_bans.hash_list[i]));
ks_dumplist(f, LIST_FIRST(&ip_bans.hash_list[i]));
}
}
/* gline.c */
/*
* gs_dumpklines() helper
*/
static void
gs_dumplist(int f, uBanEnt *be)
{
struct userBan *ub;
/* glines.c */
extern void gs_write(int, char, struct userBan *);
for (; be; be = LIST_NEXT(be, lp))
{
ub = be->ban;
/* must be local and not from conf */
if ((ub->flags & (UBAN_GLINE|UBAN_CONF)) != UBAN_GLINE)
continue;
/* must be over the storage threshold duration */
if ((ub->flags & UBAN_TEMPORARY)
&& ub->duration < (GLINE_MIN_STORE_TIME * 60))
continue;
gs_write(f, '+', ub);
}
}
/*
* Called from glines.c during a storage GC.
*/
void
gs_dumpglines(int f)
{
int i, j;
for (i = 0; i < 256; i++)
for (j = 0; j < 256; j++)
gs_dumplist(f, LIST_FIRST(&CIDR4_bans[i][j]));
gs_dumplist(f, LIST_FIRST(&CIDR4BIG_bans));
gs_dumplist(f, LIST_FIRST(&host_bans.wild_list));
gs_dumplist(f, LIST_FIRST(&ip_bans.wild_list));
for (i = 0; i < HASH_SIZE; i++)
{
gs_dumplist(f, LIST_FIRST(&host_bans.hash_list[i]));
gs_dumplist(f, LIST_FIRST(&ip_bans.hash_list[i]));
}
}
static void
mc_userlist(MemCount *mc, uBanEnt *be)
{
struct userBan *ub;
while (be)
{
ub = be->ban;
mc->c++;
mc->m += sizeof(*ub);
if (ub->u)
mc->m += strlen(ub->u) + 1;
if (ub->h)
mc->m += strlen(ub->h) + 1;
if (ub->reason)
mc->m += strlen(ub->reason) + 1;
be = LIST_NEXT(be, lp);
}
}
static void
mc_simlist(MemCount *mc, uBanEnt *be)
{
struct simBan *sb;
while (be)
{
sb = (struct simBan *)be->ban;
mc->c++;
mc->m += sizeof(*sb);
if (sb->mask)
mc->m += strlen(sb->mask) + 1;
if (sb->reason)
mc->m += strlen(sb->reason) + 1;
be = LIST_NEXT(be, lp);
}
}
/* Revised for shuns and g-lines */
static inline void sync_me(aClient *cptr, uBanEnt *bl)
{
uBanEnt *bln;
struct userBan *ban;
while(bl)
{
bln = LIST_NEXT(bl, lp);
ban = bl->ban;
if (ban->flags & UBAN_SHUN)
sendto_one(cptr, ":%s SHUN %l %s@%s :%s", me.name, ban->duration,
ban->u ? ban->u : "*", ban->h ? ban->h : "*", ban->reason);
else if (ban->flags & UBAN_GLINE)
sendto_one(cptr, ":%s GLINE %l %s@%s :%s", me.name, ban->duration,
ban->u ? ban->u : "*", ban->h ? ban->h : "*", ban->reason);
bl = bln;
}
}
void sync_lists(aClient *cptr)
{
uBanEnt *bl;
int a, b;
bl = LIST_FIRST(&CIDR4BIG_bans);
sync_me(cptr, bl);
for(a = 0; a < 256; a++)
{
for(b = 0; b < 256; b++)
{
bl = LIST_FIRST(&CIDR4_bans[a][b]);
sync_me(cptr, bl);
}
}
bl = LIST_FIRST(&host_bans.wild_list);
sync_me(cptr, bl);
bl = LIST_FIRST(&ip_bans.wild_list);
sync_me(cptr, bl);
for(a = 0; a < HASH_SIZE; a++)
{
bl = LIST_FIRST(&host_bans.hash_list[a]);
sync_me(cptr, bl);
bl = LIST_FIRST(&ip_bans.hash_list[a]);
sync_me(cptr, bl);
}
}
u_long
memcount_userban(MCuserban *mc)
{
int i;
int j;
mc->file = __FILE__;
/* host, ip, gcos, chan, nick */
mc->lists.c += 5 * HASH_SIZE;
mc->lists.m += 5 * HASH_SIZE * sizeof(ban_list);
/* CIDR4 table */
mc->lists.c += 256;
mc->lists.m += 256 * sizeof(ban_list *);
/* CIDR4 subtables */
mc->lists.c += 256 * 256;
mc->lists.m += 256 * 256 * sizeof(ban_list);
mc_userlist(&mc->cidr4big_userbans, LIST_FIRST(&CIDR4BIG_bans));
for (i = 0; i < 256; i++)
for (j = 0; j < 256; j++)
mc_userlist(&mc->cidr4_userbans, LIST_FIRST(&CIDR4_bans[i][j]));
mc_userlist(&mc->hostwild_userbans, LIST_FIRST(&host_bans.wild_list));
mc_userlist(&mc->ipwild_userbans, LIST_FIRST(&ip_bans.wild_list));
mc_simlist(&mc->nickwild_simbans, LIST_FIRST(&nick_bans.wild_list));
mc_simlist(&mc->chanwild_simbans, LIST_FIRST(&chan_bans.wild_list));
mc_simlist(&mc->gcoswild_simbans, LIST_FIRST(&gcos_bans.wild_list));
for (i = 0; i < HASH_SIZE; i++)
{
mc_userlist(&mc->hosthash_userbans,
LIST_FIRST(&host_bans.hash_list[i]));
mc_userlist(&mc->iphash_userbans, LIST_FIRST(&ip_bans.hash_list[i]));
mc_simlist(&mc->nickhash_simbans, LIST_FIRST(&nick_bans.hash_list[i]));
mc_simlist(&mc->chanhash_simbans, LIST_FIRST(&chan_bans.hash_list[i]));
mc_simlist(&mc->gcoshash_simbans, LIST_FIRST(&gcos_bans.hash_list[i]));
}
mc->entries.c = ubanent_count;
mc->entries.m = ubanent_count * sizeof(uBanEnt);
mc->userbans.c = mc->cidr4big_userbans.c + mc->cidr4_userbans.c;
mc->userbans.m = mc->cidr4big_userbans.m + mc->cidr4_userbans.m;
mc->userbans.c += mc->hosthash_userbans.c + mc->hostwild_userbans.c;
mc->userbans.m += mc->hosthash_userbans.m + mc->hostwild_userbans.m;
mc->userbans.c += mc->iphash_userbans.c + mc->ipwild_userbans.c;
mc->userbans.m += mc->iphash_userbans.m + mc->ipwild_userbans.m;
mc->simbans.c = mc->nickhash_simbans.c + mc->nickwild_simbans.c;
mc->simbans.m = mc->nickhash_simbans.m + mc->nickwild_simbans.m;
mc->simbans.c += mc->chanhash_simbans.c + mc->chanwild_simbans.c;
mc->simbans.m += mc->chanhash_simbans.m + mc->chanwild_simbans.m;
mc->simbans.c += mc->gcoshash_simbans.c + mc->gcoswild_simbans.c;
mc->simbans.m += mc->gcoshash_simbans.m + mc->gcoswild_simbans.m;
mc->total.c = mc->lists.c + mc->entries.c + mc->userbans.c + mc->simbans.c;
mc->total.m = mc->lists.m + mc->entries.m + mc->userbans.m + mc->simbans.m;
return mc->total.m;
}
syntax highlighted by Code2HTML, v. 0.9.1