/************************************************************************
* IRC - Internet Relay Chat, src/maskitem.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* 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 2, 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: maskitem.c,v 1.8 2004/02/28 22:23:35 tr-ircd Exp $
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "confitem.h"
#include "h.h"
#include "hostmask.h"
#include "hook.h"
#include "msg.h"
#include "numeric.h"
#include "s_conf.h"
BlockHeap *maskitem_entries;
dlink_list maskitem_list[MASKITEM_MASK];
static aHashEntry maskhash[MASKITEM_MASK][MASK_MAX];
/*
* This is the new maskitem interface for all of the functions
* that depend on certain mask types.
* klines, glines, autokills, quarantines, gecos bans, zaplines ...
*
*/
void init_maskitem(void)
{
int i;
maskitem_entries = BlockHeapCreate(sizeof(aMaskItem), MASKITEM_PREALLOCATE);
memset(maskhash, 0, sizeof(maskhash));
memset(maskitem_list, 0, sizeof(maskitem_list));
for (i = 0; i < MASKITEM_MASK; i++)
maskitem_list[i].head = maskitem_list[i].tail = NULL;
}
/* unsigned long hash_ipv4(struct irc_inaddr*)
* Input: An IP address.
* Output: A hash value of the IP address.
* Side effects: None
*/
static unsigned long hash_ipv4(struct irc_inaddr *addr, int bits)
{
unsigned long av = ntohl(addr->sins.sin.s_addr) & ~((1 << (32 - bits)) - 1);
return (av ^ (av >> 12) ^ (av >> 24)) & (MASK_MAX - 1);
}
/* unsigned long hash_ipv6(struct irc_inaddr*)
* Input: An IP address.
* Output: A hash value of the IP address.
* Side effects: None
*/
#ifdef IPV6
static unsigned long hash_ipv6(struct irc_inaddr *addr, int bits)
{
unsigned long v = 0, n;
for (n = 0; n < 16; n++) {
if (bits >= 8) {
v ^= addr->sins.sin6.s6_addr[n];
bits -= 8;
} else if (bits) {
v ^= addr->sins.sin6.s6_addr[n] & ~((1 << (8 - bits)) - 1);
return v & (MASK_MAX - 1);
} else
return v & (MASK_MAX - 1);
}
return v & (MASK_MAX - 1);
}
#endif
/* unsigned long hash_text(const char *start)
* Input: The start of the text to hash.
* Output: The hash of the string between 1 and (TH_MAX-1)
* Side-effects: None.
*/
static unsigned long hash_string(char *start)
{
const char *p = start;
unsigned long h = 0;
while (*p) {
h = (h << 4) - (h + (unsigned char) ToLower(*p++));
}
return (h & (MASK_MAX - 1));
}
static struct MaskItem *make_maskitem(int type, unsigned long len)
{
aMaskItem *ami;
ami = (aMaskItem *) BlockHeapAlloc(maskitem_entries);
if (ami == NULL)
outofmemory("Create maskitem");
memset(ami, 0, sizeof(aMaskItem));
ami->type = type;
ami->creation = time(NULL);
ami->expiration = (len ? time(NULL) + len : 0);
ami->username = NULL;
return ami;
}
static void free_maskitem(struct MaskItem *ami)
{
if (ami->string)
MyFree(ami->string);
if (ami->username)
MyFree(ami->username);
if (ami->ownername)
MyFree(ami->ownername);
if (ami->reason)
MyFree(ami->reason);
ireg_free_ami(ami);
BlockHeapFree(maskitem_entries, ami);
}
static void remove_from_maskitem_hashtable(aMaskItem * ami)
{
aMaskItem *tmp, *prev = NULL;
int hashval = ami->hashval;
int htype = ami->type - 1;
for (tmp = (aMaskItem *) maskhash[htype][hashval].list; tmp; tmp = tmp->hnext) {
if (tmp == ami) {
if (prev)
prev->hnext = tmp->hnext;
else
maskhash[htype][hashval].list = (void *) tmp->hnext;
tmp->hnext = NULL;
if (maskhash[htype][hashval].links > 0)
--maskhash[htype][hashval].links;
break;
}
prev = tmp;
}
}
struct MaskItem *create_maskitem(char *owner, char *string, char *user, int type,
unsigned long length)
{
/* The value of string may happily differ from host, ip, nick, gecos entries */
struct MaskItem *ami = NULL;
unsigned long hashval = 0;
int htype = type - 1;
dlink_node *ptr;
ami = find_maskitem(string, user, type, 0);
if (ami)
return ami;
switch (type) {
case MASKITEM_KLINE:
case MASKITEM_KLINE_CONFIG:
case MASKITEM_AUTOKILL:
case MASKITEM_EXCLUDE:
if (user && string) {
int bits, t;
ami = make_maskitem(type, length);
t = parse_netmask(string, &(ami->ipnum), &bits);
if (t == HM_HOST)
hashval = hash_string(string);
#ifdef IPV6
if (t == HM_IPV6)
hashval = hash_ipv6(&(ami->ipnum), bits);
else if (t == HM_IPV4)
hashval = hash_ipv4(&(ami->ipnum), bits);
#else
if (t == HM_IPV4)
hashval = hash_ipv4(&(ami->ipnum), bits);
#endif
DupString(ami->username, user);
}
break;
case MASKITEM_ZAPLINE:
case MASKITEM_ZAPLINE_CONFIG:
if (string) {
int bits, t;
ami = make_maskitem(type, length);
t = parse_netmask(string, &(ami->ipnum), &bits);
if (t == HM_HOST) {
free_maskitem(ami);
ami = NULL;
break;
}
#ifdef IPV6
if (t == HM_IPV6)
hashval = hash_ipv6(&(ami->ipnum), bits);
else
hashval = hash_ipv4(&(ami->ipnum), bits);
#else
hashval = hash_ipv4(&(ami->ipnum), bits);
#endif
}
break;
case MASKITEM_KLINE_REGEX:
if (string) {
ami = make_maskitem(type, length);
hashval = hash_string(string);
}
if (user)
DupString(ami->username, user);
break;
default:
if (string) {
ami = make_maskitem(type, length);
hashval = hash_string(string);
}
break;
}
if (!ami)
return NULL;
DupString(ami->ownername, owner);
DupString(ami->string, string);
ami->hashval = hashval;
/* In order to use the table at an effective level, we do -1 */
ami->hnext = (aMaskItem *) maskhash[htype][hashval].list;
maskhash[htype][hashval].list = (void *) ami;
maskhash[htype][hashval].links++;
maskhash[htype][hashval].hits++;
ptr = make_dlink_node();
dlinkAdd(ami, ptr, &maskitem_list[htype]);
return ami;
}
struct MaskItem *find_maskitem(char *string, char *user, int type, int m)
{
dlink_node *ptr;
aMaskItem *ami;
int htype = type - 1;
int (*compfunc)(char *, char *);
if (!string || string[0] == '\0')
return NULL;
expire_maskitems();
compfunc = m ? match : irc_strcmp;
if (type < 13) {
for (ptr = maskitem_list[htype].head; ptr; ptr = ptr->next) {
ami = ptr->data;
if (!ami->string)
continue;
if (compfunc(ami->string, string) == 0) {
if (user && ami->username) {
if (compfunc(ami->username, user) == 0)
return ami;
} else {
return ami;
}
}
}
} else {
for (ptr = maskitem_list[htype].head; ptr; ptr = ptr->next) {
ami = ptr->data;
if (ireg_match(ami, string) > 0)
return ami;
}
}
return NULL;
}
void terminate_maskitem(char *string, char *user, int type)
{
struct MaskItem *ami;
int htype = type - 1;
dlink_node *ptr;
/* first: we locate the maskitem */
ami = find_maskitem(string, user, type, 0);
if (!ami)
return;
/* second: we remove this from the maskitem hashtable */
remove_from_maskitem_hashtable(ami);
/* third: we remove this maskitem from the linked list */
if ((ptr = dlinkFind(&maskitem_list[htype], ami)))
dlinkDeleteNode(ptr, &maskitem_list[htype]);
/* fourth: we remove this maskitem from the memory */
free_maskitem(ami);
}
void expire_maskitems(void)
{
dlink_node *ptr, *next_ptr;
aMaskItem *ami;
int i = 0;
for (i = 0; i < MASKITEM_MASK; i++) {
for (ptr = maskitem_list[i].head; ptr; ptr = next_ptr) {
next_ptr = ptr->next;
ami = ptr->data;
if (!ami)
continue;
if (ami->expiration > 0 && ami->expiration <= time(NULL)) {
remove_from_maskitem_hashtable(ami);
dlinkDeleteNode(ptr, &maskitem_list[i]);
free_maskitem(ami);
}
}
}
}
void report_maskitem_list_secondary(aClient *cptr, char *matchto, int type, int reply, char letter)
{
int htype = type - 1;
dlink_node *ptr;
aMaskItem *ami;
expire_maskitems();
for (ptr = maskitem_list[htype].head; ptr; ptr = ptr->next) {
ami = ptr->data;
if (!ami)
continue;
if (matchto) {
if (match(ami->string, matchto) == 0)
send_me_numeric(cptr, reply, letter, ami->reason, ami->string, 0, 0);
} else {
send_me_numeric(cptr, reply, letter, ami->reason, ami->string, 0, 0);
}
}
return;
}
void report_maskitem_list_primary(aClient *cptr, char *matchto, int type, int reply, char letter)
{
int htype = type - 1;
dlink_node *ptr;
aMaskItem *ami;
expire_maskitems();
for (ptr = maskitem_list[htype].head; ptr; ptr = ptr->next) {
ami = ptr->data;
if (!ami)
continue;
if (matchto) {
if (match(ami->string, matchto) == 0)
send_me_numeric(cptr, reply, letter, ami->string, ami->username,
(ami->expiration ? ami->expiration - ami->creation : 0),
ami->reason);
} else {
send_me_numeric(cptr, reply, letter, ami->string, ami->username,
(ami->expiration ? ami->expiration - ami->creation : 0), ami->reason);
}
}
return;
}
void push_all_maskitems(aClient *cptr, int type)
{
int htype = type - 1;
dlink_node *ptr;
aMaskItem *ami;
expire_maskitems();
for (ptr = maskitem_list[htype].head; ptr; ptr = ptr->next) {
ami = ptr->data;
if (!ami)
continue;
switch (type) {
case MASKITEM_QUARANTINE:
sendto_one_server(cptr, &me, TOK1_SQLINE, "%M :%s", ami, ami->reason);
break;
case MASKITEM_GECOS:
sendto_one_server(cptr, &me, TOK1_SGLINE, "%d :%M:%s", strlen(ami->string),
ami, ami->reason);
break;
case MASKITEM_JUPITER:
sendto_one_server(cptr, &me, TOK1_JUPITER, "%M :%s", ami, ami->reason);
break;
case MASKITEM_ZAPLINE:
sendto_one_server(cptr, &me, TOK1_SZLINE, "%M :%s", ami, ami->reason);
break;
default:
break;
}
}
}
void rehash_maskitems(int type)
{
int htype = type - 1;
dlink_node *ptr, *next_ptr;
aMaskItem *ami;
for (ptr = maskitem_list[htype].head; ptr; ptr = next_ptr) {
next_ptr = ptr->next;
ami = ptr->data;
if (!ami)
continue;
remove_from_maskitem_hashtable(ami);
dlinkDeleteNode(ptr, &maskitem_list[htype]);
free_maskitem(ami);
}
}
syntax highlighted by Code2HTML, v. 0.9.1