1 /************************************************************************
2  *   Unreal Internet Relay Chat, src/list.c
3  *   Copyright (C) 1990 Jarkko Oikarinen and
4  *                      University of Oulu, Finland
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 1, or (at your option)
9  *   any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 /* -- Jto -- 20 Jun 1990
22  * extern void free() fixed as suggested by
23  * gruner@informatik.tu-muenchen.de
24  */
25 
26 /* -- Jto -- 03 Jun 1990
27  * Added chname initialization...
28  */
29 
30 /* -- Jto -- 24 May 1990
31  * Moved is_full() to channel.c
32  */
33 
34 /* -- Jto -- 10 May 1990
35  * Added #include <sys.h>
36  * Changed memset(xx,0,yy) into bzero(xx,yy)
37  */
38 
39 #include "struct.h"
40 #include "common.h"
41 #include "sys.h"
42 #include "h.h"
43 #include "proto.h"
44 #include "numeric.h"
45 #ifdef	DBMALLOC
46 #include "malloc.h"
47 #endif
48 #include <string.h>
49 void free_link(Link *);
50 Link *make_link();
51 extern ircstats IRCstats;
52 
53 ID_Copyright
54     ("(C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen");
55 ID_Notes("2.24 4/20/94");
56 
57 #ifdef	DEBUGMODE
58 static struct liststats {
59 	int  inuse;
60 } cloc, crem, users, servs, links, classs, aconfs;
61 
62 #endif
63 
64 void outofmemory();
65 
66 MODVAR int  flinks = 0;
67 MODVAR int  freelinks = 0;
68 MODVAR Link *freelink = NULL;
69 MODVAR Member *freemember = NULL;
70 MODVAR Membership *freemembership = NULL;
71 MODVAR MembershipL *freemembershipL = NULL;
72 MODVAR int  numclients = 0;
73 
initlists(void)74 void initlists(void)
75 {
76 #ifdef	DEBUGMODE
77 	bzero((char *)&cloc, sizeof(cloc));
78 	bzero((char *)&crem, sizeof(crem));
79 	bzero((char *)&users, sizeof(users));
80 	bzero((char *)&servs, sizeof(servs));
81 	bzero((char *)&links, sizeof(links));
82 	bzero((char *)&classs, sizeof(classs));
83 #endif
84 }
85 
outofmemory(void)86 void outofmemory(void)
87 {
88 	Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
89 	restart("Out of Memory");
90 }
91 
92 
93 /*
94 ** Create a new aClient structure and set it to initial state.
95 **
96 **	from == NULL,	create local client (a client connected
97 **			to a socket).
98 **
99 **	from,	create remote client (behind a socket
100 **			associated with the client defined by
101 **			'from'). ('from' is a local client!!).
102 */
make_client(aClient * from,aClient * servr)103 aClient *make_client(aClient *from, aClient *servr)
104 {
105 	aClient *cptr = NULL;
106 	unsigned size = CLIENT_REMOTE_SIZE;
107 
108 	/*
109 	 * Check freelists first to see if we can grab a client without
110 	 * having to call malloc.
111 	 */
112 	if (!from)
113 		size = CLIENT_LOCAL_SIZE;
114 
115 	if (!(cptr = (aClient *)MyMalloc(size)))
116 		outofmemory();
117 	bzero((char *)cptr, (int)size);
118 
119 #ifdef	DEBUGMODE
120 	if (size == CLIENT_LOCAL_SIZE)
121 		cloc.inuse++;
122 	else
123 		crem.inuse++;
124 #endif
125 
126 	/* Note:  structure is zero (calloc) */
127 	cptr->from = from ? from : cptr;	/* 'from' of local client is self! */
128 	cptr->next = NULL;	/* For machines with NON-ZERO NULL pointers >;) */
129 	cptr->prev = NULL;
130 	cptr->hnext = NULL;
131 	cptr->user = NULL;
132 	cptr->serv = NULL;
133 	cptr->srvptr = servr;
134 	cptr->status = STAT_UNKNOWN;
135 
136 	(void)strcpy(cptr->username, "unknown");
137 	if (size == CLIENT_LOCAL_SIZE)
138 	{
139 		cptr->since = cptr->lasttime =
140 		    cptr->lastnick = cptr->firsttime = TStime();
141 		cptr->class = NULL;
142 		cptr->passwd = NULL;
143 		cptr->sockhost[0] = '\0';
144 		cptr->buffer[0] = '\0';
145 		cptr->authfd = -1;
146 		cptr->fd = -1;
147 	} else {
148 		cptr->fd = -256;
149 	}
150 	return (cptr);
151 }
152 
free_client(aClient * cptr)153 void free_client(aClient *cptr)
154 {
155 	if (MyConnect(cptr))
156 	{
157 		if (cptr->passwd)
158 			MyFree((char *)cptr->passwd);
159 		if (cptr->error_str)
160 			MyFree(cptr->error_str);
161 #ifdef ZIP_LINKS
162 		if (cptr->zip)
163 			zip_free(cptr);
164 #endif
165 		if (cptr->hostp)
166 			unreal_free_hostent(cptr->hostp);
167 	}
168 	MyFree((char *)cptr);
169 }
170 
171 /*
172 ** 'make_user' add's an User information block to a client
173 ** if it was not previously allocated.
174 */
make_user(aClient * cptr)175 anUser *make_user(aClient *cptr)
176 {
177 	anUser *user;
178 
179 	user = cptr->user;
180 	if (!user)
181 	{
182 		user = (anUser *)MyMallocEx(sizeof(anUser));
183 #ifdef	DEBUGMODE
184 		users.inuse++;
185 #endif
186 		user->swhois = NULL;
187 		user->away = NULL;
188 #ifdef NO_FLOOD_AWAY
189 		user->flood.away_t = 0;
190 		user->flood.away_c = 0;
191 #endif
192 		user->refcnt = 1;
193 		user->joined = 0;
194 		user->channel = NULL;
195 		user->invited = NULL;
196 		user->silence = NULL;
197 		user->server = NULL;
198 		strlcpy(user->svid, "0", sizeof(user->svid));
199 		user->lopt = NULL;
200 		user->whowas = NULL;
201 		user->snomask = 0;
202 		*user->realhost = '\0';
203 		user->virthost = NULL;
204 		user->ip_str = NULL;
205 		cptr->user = user;
206 	}
207 	return user;
208 }
209 
make_server(aClient * cptr)210 aServer *make_server(aClient *cptr)
211 {
212 
213 	aServer *serv = cptr->serv;
214 
215 	if (!serv)
216 	{
217 		serv = (aServer *)MyMallocEx(sizeof(aServer));
218 #ifdef	DEBUGMODE
219 		servs.inuse++;
220 #endif
221 		serv->user = NULL;
222 		*serv->by = '\0';
223 		serv->numeric = 0;
224 		serv->users = 0;
225 		serv->up = NULL;
226 		cptr->serv = serv;
227 	}
228 	return cptr->serv;
229 }
230 
231 /*
232 ** free_user
233 **	Decrease user reference count by one and realease block,
234 **	if count reaches 0
235 */
free_user(anUser * user,aClient * cptr)236 void free_user(anUser *user, aClient *cptr)
237 {
238 	if (--user->refcnt <= 0)
239 	{
240 		if (user->away)
241 			MyFree(user->away);
242 		if (user->swhois)
243 			MyFree(user->swhois);
244 		if (user->virthost)
245 			MyFree(user->virthost);
246 		if (user->ip_str)
247 			MyFree(user->ip_str);
248 		if (user->operlogin)
249 			MyFree(user->operlogin);
250 		/*
251 		 * sanity check
252 		 */
253 		if (user->joined || user->refcnt < 0 ||
254 		    user->invited || user->channel)
255 			sendto_realops("* %p user (%s!%s@%s) %p %p %p %d %d *",
256 			    cptr, cptr ? cptr->name : "<noname>",
257 			    user->username, user->realhost, user,
258 			    user->invited, user->channel, user->joined,
259 			    user->refcnt);
260 		MyFree(user);
261 #ifdef	DEBUGMODE
262 		users.inuse--;
263 #endif
264 	}
265 }
266 
267 /*
268  * taken the code from ExitOneClient() for this and placed it here.
269  * - avalon
270  */
remove_client_from_list(aClient * cptr)271 void remove_client_from_list(aClient *cptr)
272 {
273 	if (IsServer(cptr))
274 	{
275 		remove_server_from_table(cptr);
276 		IRCstats.servers--;
277 	}
278 	if (IsClient(cptr))
279 	{
280 		if (IsInvisible(cptr))
281 		{
282 			IRCstats.invisible--;
283 		}
284 		if (IsOper(cptr) && !IsHideOper(cptr))
285 		{
286 			IRCstats.operators--;
287 			VERIFY_OPERCOUNT(cptr, "rmvlist");
288 		}
289 		IRCstats.clients--;
290 		if (cptr->srvptr && cptr->srvptr->serv)
291 			cptr->srvptr->serv->users--;
292 	}
293 	if (IsUnknown(cptr) || IsConnecting(cptr) || IsHandshake(cptr)
294 #ifdef USE_SSL
295 		|| IsSSLHandshake(cptr)
296 #endif
297 	)
298 		IRCstats.unknown--;
299 	checklist();
300 	if (cptr->prev)
301 		cptr->prev->next = cptr->next;
302 	else
303 	{
304 		client = cptr->next;
305 		if (client)
306 			client->prev = NULL;
307 	}
308 	if (cptr->next)
309 		cptr->next->prev = cptr->prev;
310 	if (IsPerson(cptr))	/* Only persons can have been added before */
311 	{
312 		add_history(cptr, 0);
313 		off_history(cptr);	/* Remove all pointers to cptr */
314 	}
315 
316 	if (cptr->user)
317 		(void)free_user(cptr->user, cptr);
318 	if (cptr->serv)
319 	{
320 		if (cptr->serv->user)
321 			free_user(cptr->serv->user, cptr);
322 		MyFree((char *)cptr->serv);
323 #ifdef	DEBUGMODE
324 		servs.inuse--;
325 #endif
326 	}
327 #ifdef	DEBUGMODE
328 	if (cptr->fd == -2)
329 		cloc.inuse--;
330 	else
331 		crem.inuse--;
332 #endif
333 	(void)free_client(cptr);
334 	numclients--;
335 	return;
336 }
337 
338 /*
339  * although only a small routine, it appears in a number of places
340  * as a collection of a few lines...functions like this *should* be
341  * in this file, shouldnt they ?  after all, this is list.c, isnt it ?
342  * -avalon
343  */
add_client_to_list(aClient * cptr)344 void add_client_to_list(aClient *cptr)
345 {
346 	/*
347 	 * since we always insert new clients to the top of the list,
348 	 * this should mean the "me" is the bottom most item in the list.
349 	 */
350 	cptr->next = client;
351 	client = cptr;
352 	if (cptr->next)
353 		cptr->next->prev = cptr;
354 	return;
355 }
356 
357 /*
358  * Look for ptr in the linked listed pointed to by link.
359  */
find_user_link(Link * lp,aClient * ptr)360 Link *find_user_link(Link *lp, aClient *ptr)
361 {
362 	if (ptr)
363 		while (lp)
364 		{
365 			if (lp->value.cptr == ptr)
366 				return (lp);
367 			lp = lp->next;
368 		}
369 	return NULL;
370 }
371 
372 /* Based on find_str_link() from bahamut -- codemastr */
find_str_match_link(Link * lp,char * charptr)373 int find_str_match_link(Link *lp, char *charptr)
374 {
375 	if (!charptr)
376 		return 0;
377 	for (; lp; lp = lp->next) {
378 		if(!match(lp->value.cp, charptr))
379 			return 1;
380 	}
381 	return 0;
382 }
383 
free_str_list(Link * lp)384 void free_str_list(Link *lp)
385 {
386 	Link *next;
387 
388 
389 	while (lp)
390 	{
391 		next = lp->next;
392 		MyFree((char *)lp->value.cp);
393 		free_link(lp);
394 		lp = next;
395 	}
396 
397 	return;
398 }
399 
400 
401 #define	LINKSIZE	(4072/sizeof(Link))
402 
make_link(void)403 Link *make_link(void)
404 {
405 	Link *lp;
406 	int  i;
407 
408 	/* "caching" slab-allocator... ie. we're allocating one pages
409 	   (hopefully - upped to the Linux default, not dbuf.c) worth of
410 	   link-structures at time to avoid all the malloc overhead.
411 	   All links left free from this process or separately freed
412 	   by a call to free_link() are moved over to freelink-list.
413 	   Impact? Let's see... -Donwulff */
414 	/* Impact is a huge memory leak -Stskeeps
415 	   hope this implementation works a little bit better */
416 	if (freelink == NULL)
417 	{
418 		for (i = 1; i <= LINKSIZE; i++)
419 		{
420 			lp = (Link *)MyMalloc(sizeof(Link));
421 			lp->next = freelink;
422 			freelink = lp;
423 		}
424 		freelinks = freelinks + LINKSIZE;
425 		lp = freelink;
426 		freelink = lp->next;
427 		freelinks--;
428 	}
429 	else
430 	{
431 		lp = freelink;
432 		freelink = freelink->next;
433 		freelinks--;
434 	}
435 #ifdef	DEBUGMODE
436 	links.inuse++;
437 #endif
438 	return lp;
439 }
440 
free_link(Link * lp)441 void free_link(Link *lp)
442 {
443 	lp->next = freelink;
444 	freelink = lp;
445 	freelinks++;
446 
447 #ifdef	DEBUGMODE
448 	links.inuse--;
449 #endif
450 }
451 
make_ban(void)452 Ban *make_ban(void)
453 {
454 	Ban *lp;
455 
456 	lp = (Ban *) MyMalloc(sizeof(Ban));
457 #ifdef	DEBUGMODE
458 	links.inuse++;
459 #endif
460 	return lp;
461 }
462 
free_ban(Ban * lp)463 void free_ban(Ban *lp)
464 {
465 	MyFree((char *)lp);
466 #ifdef	DEBUGMODE
467 	links.inuse--;
468 #endif
469 }
470 
make_class(void)471 aClass *make_class(void)
472 {
473 	aClass *tmp;
474 
475 	tmp = (aClass *)MyMalloc(sizeof(aClass));
476 #ifdef	DEBUGMODE
477 	classs.inuse++;
478 #endif
479 	return tmp;
480 }
481 
free_class(aClass * tmp)482 void free_class(aClass *tmp)
483 {
484 	MyFree((char *)tmp);
485 #ifdef	DEBUGMODE
486 	classs.inuse--;
487 #endif
488 }
489 
490 #ifdef	DEBUGMODE
send_listinfo(aClient * cptr,char * name)491 void send_listinfo(aClient *cptr, char *name)
492 {
493 	int  inuse = 0, mem = 0, tmp = 0;
494 
495 	sendto_one(cptr, ":%s %d %s :Local: inuse: %d(%d)",
496 	    me.name, RPL_STATSDEBUG, name, inuse += cloc.inuse,
497 	    tmp = cloc.inuse * CLIENT_LOCAL_SIZE);
498 	mem += tmp;
499 	sendto_one(cptr, ":%s %d %s :Remote: inuse: %d(%d)",
500 	    me.name, RPL_STATSDEBUG, name,
501 	    crem.inuse, tmp = crem.inuse * CLIENT_REMOTE_SIZE);
502 	mem += tmp;
503 	inuse += crem.inuse;
504 	sendto_one(cptr, ":%s %d %s :Users: inuse: %d(%d)",
505 	    me.name, RPL_STATSDEBUG, name, users.inuse,
506 	    tmp = users.inuse * sizeof(anUser));
507 	mem += tmp;
508 	inuse += users.inuse,
509 	    sendto_one(cptr, ":%s %d %s :Servs: inuse: %d(%d)",
510 	    me.name, RPL_STATSDEBUG, name, servs.inuse,
511 	    tmp = servs.inuse * sizeof(aServer));
512 	mem += tmp;
513 	inuse += servs.inuse,
514 	    sendto_one(cptr, ":%s %d %s :Links: inuse: %d(%d)",
515 	    me.name, RPL_STATSDEBUG, name, links.inuse,
516 	    tmp = links.inuse * sizeof(Link));
517 	mem += tmp;
518 	inuse += links.inuse,
519 	    sendto_one(cptr, ":%s %d %s :Classes: inuse: %d(%d)",
520 	    me.name, RPL_STATSDEBUG, name, classs.inuse,
521 	    tmp = classs.inuse * sizeof(aClass));
522 	mem += tmp;
523 	inuse += aconfs.inuse,
524 	    sendto_one(cptr, ":%s %d %s :Totals: inuse %d %d",
525 	    me.name, RPL_STATSDEBUG, name, inuse, mem);
526 }
527 
528 #endif
529 
add_ListItem(ListStruct * item,ListStruct ** list)530 void add_ListItem(ListStruct *item, ListStruct **list) {
531 	item->next = *list;
532 	item->prev = NULL;
533 	if (*list)
534 		(*list)->prev = item;
535 	*list = item;
536 }
537 
del_ListItem(ListStruct * item,ListStruct ** list)538 ListStruct *del_ListItem(ListStruct *item, ListStruct **list) {
539 	ListStruct *l, *ret;
540 
541 	for (l = *list; l; l = l->next) {
542 		if (l == item) {
543 			ret = item->next;
544 			if (l->prev)
545 				l->prev->next = l->next;
546 			else
547 				*list = l->next;
548 			if (l->next)
549 				l->next->prev = l->prev;
550 			return ret;
551 		}
552 	}
553 	return NULL;
554 }
555 
556 #ifdef JOINTHROTTLE
557 /** Adds a aJFlood entry to user & channel and returns entry.
558  * NOTE: Does not check for already-existing-entry
559  */
cmodej_addentry(aClient * cptr,aChannel * chptr)560 aJFlood *cmodej_addentry(aClient *cptr, aChannel *chptr)
561 {
562 aJFlood *e;
563 
564 #ifdef DEBUGMODE
565 	if (!IsPerson(cptr))
566 		abort();
567 
568 	for (e=cptr->user->jflood; e; e=e->next_u)
569 		if (e->chptr == chptr)
570 			abort();
571 
572 	for (e=chptr->jflood; e; e=e->next_c)
573 		if (e->cptr == cptr)
574 			abort();
575 #endif
576 
577 	e = MyMallocEx(sizeof(aJFlood));
578 	e->cptr = cptr;
579 	e->chptr = chptr;
580 	e->prev_u = e->prev_c = NULL;
581 	e->next_u = cptr->user->jflood;
582 	e->next_c = chptr->jflood;
583 	if (cptr->user->jflood)
584 		cptr->user->jflood->prev_u = e;
585 	if (chptr->jflood)
586 		chptr->jflood->prev_c = e;
587 	cptr->user->jflood = chptr->jflood = e;
588 
589 	return e;
590 }
591 
592 /** Removes an individual entry from list and frees it.
593  */
cmodej_delentry(aJFlood * e)594 void cmodej_delentry(aJFlood *e)
595 {
596 	/* remove from user.. */
597 	if (e->prev_u)
598 		e->prev_u->next_u = e->next_u;
599 	else
600 		e->cptr->user->jflood = e->next_u; /* new head */
601 	if (e->next_u)
602 		e->next_u->prev_u = e->prev_u;
603 
604 	/* remove from channel.. */
605 	if (e->prev_c)
606 		e->prev_c->next_c = e->next_c;
607 	else
608 		e->chptr->jflood = e->next_c; /* new head */
609 	if (e->next_c)
610 		e->next_c->prev_c = e->prev_c;
611 
612 	/* actually free it */
613 	MyFree(e);
614 }
615 
616 /** Removes all entries belonging to user from all lists and free them. */
cmodej_deluserentries(aClient * cptr)617 void cmodej_deluserentries(aClient *cptr)
618 {
619 aJFlood *e, *e_next;
620 
621 	for (e=cptr->user->jflood; e; e=e_next)
622 	{
623 		e_next = e->next_u;
624 
625 		/* remove from channel.. */
626 		if (e->prev_c)
627 			e->prev_c->next_c = e->next_c;
628 		else
629 			e->chptr->jflood = e->next_c; /* new head */
630 		if (e->next_c)
631 			e->next_c->prev_c = e->prev_c;
632 
633 		/* actually free it */
634 		MyFree(e);
635 	}
636 	cptr->user->jflood = NULL;
637 }
638 
639 /** Removes all entries belonging to channel from all lists and free them. */
cmodej_delchannelentries(aChannel * chptr)640 void cmodej_delchannelentries(aChannel *chptr)
641 {
642 aJFlood *e, *e_next;
643 
644 	for (e=chptr->jflood; e; e=e_next)
645 	{
646 		e_next = e->next_c;
647 
648 		/* remove from user.. */
649 		if (e->prev_u)
650 			e->prev_u->next_u = e->next_u;
651 		else
652 			e->cptr->user->jflood = e->next_u; /* new head */
653 		if (e->next_u)
654 			e->next_u->prev_u = e->prev_u;
655 
656 		/* actually free it */
657 		MyFree(e);
658 	}
659 	chptr->jflood = NULL;
660 }
661 
662 /** Regulary cleans up cmode-j user/chan structs */
EVENT(cmodej_cleanup_structs)663 EVENT(cmodej_cleanup_structs)
664 {
665 aJFlood *e, *e_next;
666 int i;
667 aClient *cptr;
668 aChannel *chptr;
669 int t;
670 CmodeParam *cmp;
671 #ifdef DEBUGMODE
672 int freed=0;
673 #endif
674 
675 	for (chptr = channel; chptr; chptr=chptr->nextch)
676 	{
677 		if (!chptr->jflood)
678 			continue;
679 		t=0;
680 		/* t will be kept at 0 if not found or if mode not set,
681 		 * but DO still check since there are entries left as indicated by ->jflood!
682 		 */
683 		if (chptr->mode.extmode & EXTMODE_JOINTHROTTLE)
684 		{
685 			for (cmp = chptr->mode.extmodeparam; cmp; cmp=cmp->next)
686 				if (cmp->flag == 'j')
687 					t = ((aModejEntry *)cmp)->t;
688 		}
689 
690 		for (e = chptr->jflood; e; e = e_next)
691 		{
692 			e_next = e->next_c;
693 
694 			if (e->firstjoin + t < TStime())
695 			{
696 				cmodej_delentry(e);
697 #ifdef DEBUGMODE
698 				freed++;
699 #endif
700 			}
701 		}
702 	}
703 
704 #ifdef DEBUGMODE
705 	if (freed)
706 		ircd_log(LOG_ERROR, "cmodej_cleanup_structs: %d entries freed [%d bytes]", freed, freed * sizeof(aJFlood));
707 #endif
708 }
709 
710 #endif
711 
712