mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-04 23:33:14 +02:00
422b339be1
easily deal with "parameter eating" of unknown channel modes. Now, 12 years later, finally added the code to do this. This prevents some (serious) desynching if you have a parameter-eating channel mode on one server and not on the other. Obviously, you should always try to have the same featureset on all servers, but sometimes this is not possible, like when upgrading..
570 lines
12 KiB
C
570 lines
12 KiB
C
/************************************************************************
|
|
* Unreal Internet Relay Chat, src/list.c
|
|
* Copyright (C) 1990 Jarkko Oikarinen and
|
|
* University of Oulu, Finland
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/* -- Jto -- 20 Jun 1990
|
|
* extern void free() fixed as suggested by
|
|
* gruner@informatik.tu-muenchen.de
|
|
*/
|
|
|
|
/* -- Jto -- 03 Jun 1990
|
|
* Added chname initialization...
|
|
*/
|
|
|
|
/* -- Jto -- 24 May 1990
|
|
* Moved is_full() to channel.c
|
|
*/
|
|
|
|
/* -- Jto -- 10 May 1990
|
|
* Added #include <sys.h>
|
|
* Changed memset(xx,0,yy) into bzero(xx,yy)
|
|
*/
|
|
|
|
#include "struct.h"
|
|
#include "common.h"
|
|
#include "sys.h"
|
|
#include "h.h"
|
|
#include "proto.h"
|
|
#include "numeric.h"
|
|
#ifdef DBMALLOC
|
|
#include "malloc.h"
|
|
#endif
|
|
#include "mempool.h"
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
void free_link(Link *);
|
|
Link *make_link();
|
|
extern ircstats IRCstats;
|
|
|
|
ID_Copyright
|
|
("(C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen");
|
|
ID_Notes("2.24 4/20/94");
|
|
|
|
#ifdef DEBUGMODE
|
|
static struct liststats {
|
|
int inuse;
|
|
} cloc, crem, users, servs, links, classs, aconfs;
|
|
|
|
#endif
|
|
|
|
void outofmemory();
|
|
|
|
MODVAR int flinks = 0;
|
|
MODVAR int freelinks = 0;
|
|
MODVAR Link *freelink = NULL;
|
|
MODVAR Member *freemember = NULL;
|
|
MODVAR Membership *freemembership = NULL;
|
|
MODVAR MembershipL *freemembershipL = NULL;
|
|
MODVAR int numclients = 0;
|
|
|
|
/* unless documented otherwise, these are all local-only, except client_list. */
|
|
MODVAR struct list_head client_list, lclient_list, server_list, oper_list, unknown_list, global_server_list;
|
|
|
|
static mp_pool_t *user_pool = NULL;
|
|
|
|
void initlists(void)
|
|
{
|
|
#ifdef DEBUGMODE
|
|
bzero((char *)&cloc, sizeof(cloc));
|
|
bzero((char *)&crem, sizeof(crem));
|
|
bzero((char *)&users, sizeof(users));
|
|
bzero((char *)&servs, sizeof(servs));
|
|
bzero((char *)&links, sizeof(links));
|
|
bzero((char *)&classs, sizeof(classs));
|
|
#endif
|
|
|
|
INIT_LIST_HEAD(&client_list);
|
|
INIT_LIST_HEAD(&lclient_list);
|
|
INIT_LIST_HEAD(&server_list);
|
|
INIT_LIST_HEAD(&oper_list);
|
|
INIT_LIST_HEAD(&unknown_list);
|
|
INIT_LIST_HEAD(&global_server_list);
|
|
|
|
user_pool = mp_pool_new(sizeof(anUser), 512 * 1024);
|
|
}
|
|
|
|
void outofmemory(void)
|
|
{
|
|
Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
|
|
restart("Out of Memory");
|
|
}
|
|
|
|
|
|
/*
|
|
** Create a new aClient structure and set it to initial state.
|
|
**
|
|
** from == NULL, create local client (a client connected
|
|
** to a socket).
|
|
**
|
|
** from, create remote client (behind a socket
|
|
** associated with the client defined by
|
|
** 'from'). ('from' is a local client!!).
|
|
*/
|
|
aClient *make_client(aClient *from, aClient *servr)
|
|
{
|
|
aClient *cptr = NULL;
|
|
unsigned size = CLIENT_REMOTE_SIZE;
|
|
|
|
/*
|
|
* Check freelists first to see if we can grab a client without
|
|
* having to call malloc.
|
|
*/
|
|
if (!from)
|
|
size = CLIENT_LOCAL_SIZE;
|
|
|
|
if (!(cptr = (aClient *)MyMalloc(size)))
|
|
outofmemory();
|
|
bzero((char *)cptr, (int)size);
|
|
|
|
#ifdef DEBUGMODE
|
|
if (size == CLIENT_LOCAL_SIZE)
|
|
cloc.inuse++;
|
|
else
|
|
crem.inuse++;
|
|
#endif
|
|
|
|
/* Note: structure is zero (calloc) */
|
|
cptr->from = from ? from : cptr; /* 'from' of local client is self! */
|
|
cptr->user = NULL;
|
|
cptr->serv = NULL;
|
|
cptr->srvptr = servr;
|
|
cptr->status = STAT_UNKNOWN;
|
|
|
|
INIT_LIST_HEAD(&cptr->client_node);
|
|
INIT_LIST_HEAD(&cptr->client_hash);
|
|
INIT_LIST_HEAD(&cptr->id_hash);
|
|
|
|
(void)strcpy(cptr->username, "unknown");
|
|
if (size == CLIENT_LOCAL_SIZE)
|
|
{
|
|
INIT_LIST_HEAD(&cptr->lclient_node);
|
|
INIT_LIST_HEAD(&cptr->special_node);
|
|
|
|
cptr->since = cptr->lasttime =
|
|
cptr->lastnick = cptr->firsttime = TStime();
|
|
cptr->class = NULL;
|
|
cptr->passwd = NULL;
|
|
cptr->sockhost[0] = '\0';
|
|
cptr->buffer[0] = '\0';
|
|
cptr->authfd = -1;
|
|
cptr->fd = -1;
|
|
|
|
dbuf_queue_init(&cptr->recvQ);
|
|
dbuf_queue_init(&cptr->sendQ);
|
|
} else {
|
|
cptr->fd = -256;
|
|
}
|
|
return (cptr);
|
|
}
|
|
|
|
void free_client(aClient *cptr)
|
|
{
|
|
if (!list_empty(&cptr->client_node))
|
|
list_del(&cptr->client_node);
|
|
if (MyConnect(cptr))
|
|
{
|
|
if (!list_empty(&cptr->lclient_node))
|
|
list_del(&cptr->lclient_node);
|
|
if (!list_empty(&cptr->special_node))
|
|
list_del(&cptr->special_node);
|
|
|
|
RunHook(HOOKTYPE_FREE_CLIENT, cptr);
|
|
if (cptr->passwd)
|
|
MyFree((char *)cptr->passwd);
|
|
if (cptr->error_str)
|
|
MyFree(cptr->error_str);
|
|
if (cptr->hostp)
|
|
unreal_free_hostent(cptr->hostp);
|
|
|
|
assert(list_empty(&cptr->lclient_node));
|
|
assert(list_empty(&cptr->special_node));
|
|
}
|
|
|
|
MyFree((char *)cptr);
|
|
}
|
|
|
|
/*
|
|
** 'make_user' add's an User information block to a client
|
|
** if it was not previously allocated.
|
|
*/
|
|
anUser *make_user(aClient *cptr)
|
|
{
|
|
anUser *user;
|
|
|
|
user = cptr->user;
|
|
if (!user)
|
|
{
|
|
user = mp_pool_get(user_pool);
|
|
memset(user, 0, sizeof(anUser));
|
|
|
|
#ifdef DEBUGMODE
|
|
users.inuse++;
|
|
#endif
|
|
user->swhois = NULL;
|
|
user->away = NULL;
|
|
#ifdef NO_FLOOD_AWAY
|
|
user->flood.away_t = 0;
|
|
user->flood.away_c = 0;
|
|
#endif
|
|
user->refcnt = 1;
|
|
user->joined = 0;
|
|
user->channel = NULL;
|
|
user->invited = NULL;
|
|
user->silence = NULL;
|
|
user->server = NULL;
|
|
strlcpy(user->svid, "0", sizeof(user->svid));
|
|
user->lopt = NULL;
|
|
user->whowas = NULL;
|
|
user->snomask = 0;
|
|
*user->realhost = '\0';
|
|
user->virthost = NULL;
|
|
user->ip_str = NULL;
|
|
cptr->user = user;
|
|
}
|
|
return user;
|
|
}
|
|
|
|
aServer *make_server(aClient *cptr)
|
|
{
|
|
|
|
aServer *serv = cptr->serv;
|
|
|
|
if (!serv)
|
|
{
|
|
serv = (aServer *)MyMallocEx(sizeof(aServer));
|
|
#ifdef DEBUGMODE
|
|
servs.inuse++;
|
|
#endif
|
|
serv->user = NULL;
|
|
*serv->by = '\0';
|
|
serv->users = 0;
|
|
serv->up = NULL;
|
|
cptr->serv = serv;
|
|
}
|
|
return cptr->serv;
|
|
}
|
|
|
|
/*
|
|
** free_user
|
|
** Decrease user reference count by one and realease block,
|
|
** if count reaches 0
|
|
*/
|
|
void free_user(anUser *user, aClient *cptr)
|
|
{
|
|
if (user->refcnt == 0)
|
|
sendto_realops("[BUG] free_user: ref count for '%s' was already 0!?", user->username);
|
|
else
|
|
--user->refcnt;
|
|
if (user->refcnt == 0)
|
|
{
|
|
RunHook2(HOOKTYPE_FREE_USER, user, cptr);
|
|
if (user->away)
|
|
MyFree(user->away);
|
|
if (user->swhois)
|
|
MyFree(user->swhois);
|
|
if (user->virthost)
|
|
MyFree(user->virthost);
|
|
if (user->ip_str)
|
|
MyFree(user->ip_str);
|
|
if (user->operlogin)
|
|
MyFree(user->operlogin);
|
|
mp_pool_release(user);
|
|
#ifdef DEBUGMODE
|
|
users.inuse--;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* taken the code from ExitOneClient() for this and placed it here.
|
|
* - avalon
|
|
*/
|
|
void remove_client_from_list(aClient *cptr)
|
|
{
|
|
list_del(&cptr->client_node);
|
|
if (IsServer(cptr))
|
|
{
|
|
IRCstats.servers--;
|
|
}
|
|
if (IsClient(cptr))
|
|
{
|
|
if (IsInvisible(cptr))
|
|
{
|
|
IRCstats.invisible--;
|
|
}
|
|
if (IsOper(cptr) && !IsHideOper(cptr))
|
|
{
|
|
IRCstats.operators--;
|
|
VERIFY_OPERCOUNT(cptr, "rmvlist");
|
|
}
|
|
IRCstats.clients--;
|
|
if (cptr->srvptr && cptr->srvptr->serv)
|
|
cptr->srvptr->serv->users--;
|
|
}
|
|
if (IsUnknown(cptr) || IsConnecting(cptr) || IsHandshake(cptr)
|
|
|| IsSSLHandshake(cptr)
|
|
)
|
|
IRCstats.unknown--;
|
|
|
|
if (IsPerson(cptr)) /* Only persons can have been added before */
|
|
{
|
|
add_history(cptr, 0);
|
|
off_history(cptr); /* Remove all pointers to cptr */
|
|
}
|
|
|
|
if (cptr->user)
|
|
(void)free_user(cptr->user, cptr);
|
|
if (cptr->serv)
|
|
{
|
|
if (cptr->serv->user)
|
|
free_user(cptr->serv->user, cptr);
|
|
safefree(cptr->serv->features.chanmodes[0]);
|
|
safefree(cptr->serv->features.chanmodes[1]);
|
|
safefree(cptr->serv->features.chanmodes[2]);
|
|
safefree(cptr->serv->features.chanmodes[3]);
|
|
MyFree((char *)cptr->serv);
|
|
#ifdef DEBUGMODE
|
|
servs.inuse--;
|
|
#endif
|
|
}
|
|
#ifdef DEBUGMODE
|
|
if (cptr->fd == -2)
|
|
cloc.inuse--;
|
|
else
|
|
crem.inuse--;
|
|
#endif
|
|
assert(list_empty(&cptr->client_node));
|
|
assert(list_empty(&cptr->client_hash));
|
|
assert(list_empty(&cptr->id_hash));
|
|
(void)free_client(cptr);
|
|
checklist();
|
|
numclients--;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* although only a small routine, it appears in a number of places
|
|
* as a collection of a few lines...functions like this *should* be
|
|
* in this file, shouldnt they ? after all, this is list.c, isnt it ?
|
|
* -avalon
|
|
*/
|
|
void add_client_to_list(aClient *cptr)
|
|
{
|
|
list_add(&cptr->client_node, &client_list);
|
|
}
|
|
|
|
/*
|
|
* Look for ptr in the linked listed pointed to by link.
|
|
*/
|
|
Link *find_user_link(Link *lp, aClient *ptr)
|
|
{
|
|
if (ptr)
|
|
while (lp)
|
|
{
|
|
if (lp->value.cptr == ptr)
|
|
return (lp);
|
|
lp = lp->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Based on find_str_link() from bahamut -- codemastr */
|
|
int find_str_match_link(Link *lp, char *charptr)
|
|
{
|
|
if (!charptr)
|
|
return 0;
|
|
for (; lp; lp = lp->next) {
|
|
if(!match(lp->value.cp, charptr))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void free_str_list(Link *lp)
|
|
{
|
|
Link *next;
|
|
|
|
|
|
while (lp)
|
|
{
|
|
next = lp->next;
|
|
MyFree((char *)lp->value.cp);
|
|
free_link(lp);
|
|
lp = next;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#define LINKSIZE (4072/sizeof(Link))
|
|
|
|
Link *make_link(void)
|
|
{
|
|
Link *lp;
|
|
int i;
|
|
|
|
/* "caching" slab-allocator... ie. we're allocating one pages
|
|
(hopefully - upped to the Linux default, not dbuf.c) worth of
|
|
link-structures at time to avoid all the malloc overhead.
|
|
All links left free from this process or separately freed
|
|
by a call to free_link() are moved over to freelink-list.
|
|
Impact? Let's see... -Donwulff */
|
|
/* Impact is a huge memory leak -Stskeeps
|
|
hope this implementation works a little bit better */
|
|
if (freelink == NULL)
|
|
{
|
|
for (i = 1; i <= LINKSIZE; i++)
|
|
{
|
|
lp = (Link *)MyMalloc(sizeof(Link));
|
|
lp->next = freelink;
|
|
freelink = lp;
|
|
}
|
|
freelinks = freelinks + LINKSIZE;
|
|
lp = freelink;
|
|
freelink = lp->next;
|
|
freelinks--;
|
|
}
|
|
else
|
|
{
|
|
lp = freelink;
|
|
freelink = freelink->next;
|
|
freelinks--;
|
|
}
|
|
#ifdef DEBUGMODE
|
|
links.inuse++;
|
|
#endif
|
|
return lp;
|
|
}
|
|
|
|
void free_link(Link *lp)
|
|
{
|
|
lp->next = freelink;
|
|
freelink = lp;
|
|
freelinks++;
|
|
|
|
#ifdef DEBUGMODE
|
|
links.inuse--;
|
|
#endif
|
|
}
|
|
|
|
Ban *make_ban(void)
|
|
{
|
|
Ban *lp;
|
|
|
|
lp = (Ban *) MyMalloc(sizeof(Ban));
|
|
#ifdef DEBUGMODE
|
|
links.inuse++;
|
|
#endif
|
|
return lp;
|
|
}
|
|
|
|
void free_ban(Ban *lp)
|
|
{
|
|
MyFree((char *)lp);
|
|
#ifdef DEBUGMODE
|
|
links.inuse--;
|
|
#endif
|
|
}
|
|
|
|
aClass *make_class(void)
|
|
{
|
|
aClass *tmp;
|
|
|
|
tmp = (aClass *)MyMalloc(sizeof(aClass));
|
|
#ifdef DEBUGMODE
|
|
classs.inuse++;
|
|
#endif
|
|
return tmp;
|
|
}
|
|
|
|
void free_class(aClass *tmp)
|
|
{
|
|
MyFree((char *)tmp);
|
|
#ifdef DEBUGMODE
|
|
classs.inuse--;
|
|
#endif
|
|
}
|
|
|
|
#ifdef DEBUGMODE
|
|
void send_listinfo(aClient *cptr, char *name)
|
|
{
|
|
int inuse = 0, mem = 0, tmp = 0;
|
|
|
|
sendto_one(cptr, ":%s %d %s :Local: inuse: %d(%d)",
|
|
me.name, RPL_STATSDEBUG, name, inuse += cloc.inuse,
|
|
tmp = cloc.inuse * CLIENT_LOCAL_SIZE);
|
|
mem += tmp;
|
|
sendto_one(cptr, ":%s %d %s :Remote: inuse: %d(%d)",
|
|
me.name, RPL_STATSDEBUG, name,
|
|
crem.inuse, tmp = crem.inuse * CLIENT_REMOTE_SIZE);
|
|
mem += tmp;
|
|
inuse += crem.inuse;
|
|
sendto_one(cptr, ":%s %d %s :Users: inuse: %d(%d)",
|
|
me.name, RPL_STATSDEBUG, name, users.inuse,
|
|
tmp = users.inuse * sizeof(anUser));
|
|
mem += tmp;
|
|
inuse += users.inuse,
|
|
sendto_one(cptr, ":%s %d %s :Servs: inuse: %d(%d)",
|
|
me.name, RPL_STATSDEBUG, name, servs.inuse,
|
|
tmp = servs.inuse * sizeof(aServer));
|
|
mem += tmp;
|
|
inuse += servs.inuse,
|
|
sendto_one(cptr, ":%s %d %s :Links: inuse: %d(%d)",
|
|
me.name, RPL_STATSDEBUG, name, links.inuse,
|
|
tmp = links.inuse * sizeof(Link));
|
|
mem += tmp;
|
|
inuse += links.inuse,
|
|
sendto_one(cptr, ":%s %d %s :Classes: inuse: %d(%d)",
|
|
me.name, RPL_STATSDEBUG, name, classs.inuse,
|
|
tmp = classs.inuse * sizeof(aClass));
|
|
mem += tmp;
|
|
inuse += aconfs.inuse,
|
|
sendto_one(cptr, ":%s %d %s :Totals: inuse %d %d",
|
|
me.name, RPL_STATSDEBUG, name, inuse, mem);
|
|
}
|
|
|
|
#endif
|
|
|
|
void add_ListItem(ListStruct *item, ListStruct **list) {
|
|
item->next = *list;
|
|
item->prev = NULL;
|
|
if (*list)
|
|
(*list)->prev = item;
|
|
*list = item;
|
|
}
|
|
|
|
ListStruct *del_ListItem(ListStruct *item, ListStruct **list) {
|
|
ListStruct *l, *ret;
|
|
|
|
for (l = *list; l; l = l->next) {
|
|
if (l == item) {
|
|
ret = item->next;
|
|
if (l->prev)
|
|
l->prev->next = l->next;
|
|
else
|
|
*list = l->next;
|
|
if (l->next)
|
|
l->next->prev = l->prev;
|
|
return ret;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|