1 /************************************************************************
2  *   Unreal Internet Relay Chat Daemon, src/hash.c
3  *   Copyright (C) 1991 Darren Reed
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 1, or (at your option)
8  *   any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include <string.h>
21 #include <limits.h>
22 #include "numeric.h"
23 #include "struct.h"
24 #include "common.h"
25 #include "sys.h"
26 #include "hash.h"
27 #include "h.h"
28 #include "proto.h"
29 
30 ID_Copyright("(C) 1991 Darren Reed");
31 ID_Notes("2.10 7/3/93");
32 
33 static aHashEntry clientTable[U_MAX];
34 static aHashEntry channelTable[CH_MAX];
35 
36 /*
37  * look in whowas.c for the missing ...[WW_MAX]; entry - Dianora */
38 /*
39  * Hashing.
40  *
41  * The server uses a chained hash table to provide quick and efficient
42  * hash table mantainence (providing the hash function works evenly
43  * over the input range).  The hash table is thus not susceptible to
44  * problems of filling all the buckets or the need to rehash. It is
45  * expected that the hash table would look somehting like this during
46  * use: +-----+    +-----+    +-----+   +-----+
47  *   ---| 224 |----| 225 |----| 226 |---| 227 |---
48  *      +-----+    +-----+    +-----+   +-----+
49  *         |          |          |
50  *      +-----+    +-----+    +-----+
51  *      |  A  |    |  C  |    |  D  |
52  *      +-----+    +-----+    +-----+
53  *         |
54  *      +-----+
55  *      |  B  |
56  *      +-----+
57  *
58  * A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU
59  *
60  * The order shown above is just one instant of the server.  Each time a
61  * lookup is made on an entry in the hash table and it is found, the
62  * entry is moved to the top of the chain.
63  *
64  * ^^^^^^^^^^^^^^^^ **** Not anymore - Dianora
65  *
66  */
67 /* Take equal propotions from the size of int on all arcitechtures */
68 #define BITS_IN_int             ( sizeof(int) * CHAR_BIT )
69 #define THREE_QUARTERS          ((int) ((BITS_IN_int * 3) / 4))
70 #define ONE_EIGHTH              ((int) (BITS_IN_int / 8))
71 #define HIGH_BITS               ( ~((unsigned int)(~0) >> ONE_EIGHTH ))
72 
hash_nn_name(const char * hname)73 unsigned int hash_nn_name(const char *hname)
74 {
75 	unsigned int hash_value, i;
76 
77 	for (hash_value = 0; *hname; ++hname)
78 	{
79 		/* Shift hash-value by one eights of int for adding every letter */
80 		hash_value = (hash_value << ONE_EIGHTH) + tolower(*hname);
81 		/* If the next shift would cause an overflow... */
82 		if ((i = hash_value & HIGH_BITS) != 0)
83 			/* Then wrap the upper quarter of bits back to the value */
84 			hash_value = (hash_value ^
85 			    (i >> THREE_QUARTERS)) & ~HIGH_BITS;
86 	}
87 
88 	return (hash_value);
89 }
90 
91 
hash_nick_name(char * nname)92 unsigned hash_nick_name(char *nname)
93 {
94 	unsigned hash = 0;
95 	int  hash2 = 0;
96 	int  ret;
97 	char lower;
98 
99 	while (*nname)
100 	{
101 		lower = tolower(*nname);
102 		hash = (hash << 1) + lower;
103 		hash2 = (hash2 >> 1) + lower;
104 		nname++;
105 	}
106 	ret = ((hash & U_MAX_INITIAL_MASK) << BITS_PER_COL) +
107 	    (hash2 & BITS_PER_COL_MASK);
108 	return ret;
109 }
110 /*
111  * hash_channel_name
112  *
113  * calculate a hash value on at most the first 30 characters of the
114  * channel name. Most names are short than this or dissimilar in this
115  * range. There is little or no point hashing on a full channel name
116  * which maybe 255 chars long.
117  */
hash_channel_name(char * name)118 unsigned int  hash_channel_name(char *name)
119 {
120 	unsigned char *hname = (unsigned char *)name;
121 	unsigned int hash = 0;
122 	int  hash2 = 0;
123 	char lower;
124 	int  i = 30;
125 
126 	while (*hname && --i)
127 	{
128 		lower = tolower(*hname);
129 		hash = (hash << 1) + lower;
130 		hash2 = (hash2 >> 1) + lower;
131 		hname++;
132 	}
133 	return ((hash & CH_MAX_INITIAL_MASK) << BITS_PER_COL) +
134 	    (hash2 & BITS_PER_COL_MASK);
135 }
136 
hash_whowas_name(char * name)137 unsigned int hash_whowas_name(char *name)
138 {
139 	unsigned char *nname = (unsigned char *)name;
140 	unsigned int hash = 0;
141 	int  hash2 = 0;
142 	int  ret;
143 	char lower;
144 
145 	while (*nname)
146 	{
147 		lower = tolower(*nname);
148 		hash = (hash << 1) + lower;
149 		hash2 = (hash2 >> 1) + lower;
150 		nname++;
151 	}
152 	ret = ((hash & WW_MAX_INITIAL_MASK) << BITS_PER_COL) +
153 	    (hash2 & BITS_PER_COL_MASK);
154 	return ret;
155 }
156 /*
157  * clear_*_hash_table
158  *
159  * Nullify the hashtable and its contents so it is completely empty.
160  */
clear_client_hash_table(void)161 void clear_client_hash_table(void)
162 {
163 	memset((char *)clientTable, '\0', sizeof(aHashEntry) * U_MAX);
164 }
165 
clear_channel_hash_table(void)166 void clear_channel_hash_table(void)
167 {
168 	memset((char *)channelTable, '\0', sizeof(aHashEntry) * CH_MAX);
169 }
170 
171 
172 /*
173  * add_to_client_hash_table
174  */
add_to_client_hash_table(char * name,aClient * cptr)175 int  add_to_client_hash_table(char *name, aClient *cptr)
176 {
177 	unsigned int  hashv;
178 	/*
179 	 * If you see this, you have probably found your way to why changing the
180 	 * base version made the IRCd become weird. This has been the case in all
181 	 * Unreal versions since 3.0. I'm sick of people ripping the IRCd off and
182 	 * just slapping on some random <theirnet> BASE_VERSION while not changing
183 	 * a single bit of code. YOU DID NOT WRITE ALL OF THIS THEREFORE YOU DO NOT
184 	 * DESERVE TO BE ABLE TO DO THAT. If you found this however, I'm OK with you
185 	 * removing the checks. However, keep in mind that the copyright headers must
186 	 * stay in place, which means no wiping of /credits and /info. We haven't
187 	 * sat up late at night so some lamer could steal all our work without even
188 	 * giving us credit. Remember to follow all regulations in LICENSE.
189 	 * -Stskeeps
190 	*/
191 	if (loop.tainted)
192 		return 0;
193 	hashv = hash_nick_name(name);
194 	cptr->hnext = (aClient *)clientTable[hashv].list;
195 	clientTable[hashv].list = (void *)cptr;
196 	clientTable[hashv].links++;
197 	clientTable[hashv].hits++;
198 	return 0;
199 }
200 /*
201  * add_to_channel_hash_table
202  */
add_to_channel_hash_table(char * name,aChannel * chptr)203 int  add_to_channel_hash_table(char *name, aChannel *chptr)
204 {
205 	unsigned int  hashv;
206 
207 	hashv = hash_channel_name(name);
208 	chptr->hnextch = (aChannel *)channelTable[hashv].list;
209 	channelTable[hashv].list = (void *)chptr;
210 	channelTable[hashv].links++;
211 	channelTable[hashv].hits++;
212 	return 0;
213 }
214 /*
215  * del_from_client_hash_table
216  */
del_from_client_hash_table(char * name,aClient * cptr)217 int  del_from_client_hash_table(char *name, aClient *cptr)
218 {
219 	aClient *tmp, *prev = NULL;
220 	unsigned int  hashv;
221 
222 	hashv = hash_nick_name(name);
223 	for (tmp = (aClient *)clientTable[hashv].list; tmp; tmp = tmp->hnext)
224 	{
225 		if (tmp == cptr)
226 		{
227 			if (prev)
228 				prev->hnext = tmp->hnext;
229 			else
230 				clientTable[hashv].list = (void *)tmp->hnext;
231 			tmp->hnext = NULL;
232 			if (clientTable[hashv].links > 0)
233 			{
234 				clientTable[hashv].links--;
235 				return 1;
236 			}
237 			else
238 				/*
239 				 * Should never actually return from here and if we do it
240 				 * is an error/inconsistency in the hash table.
241 				 */
242 				return -1;
243 		}
244 		prev = tmp;
245 	}
246 	return 0;
247 }
248 /*
249  * del_from_channel_hash_table
250  */
del_from_channel_hash_table(char * name,aChannel * chptr)251 int  del_from_channel_hash_table(char *name, aChannel *chptr)
252 {
253 	aChannel *tmp, *prev = NULL;
254 	unsigned int  hashv;
255 
256 	hashv = hash_channel_name(name);
257 	for (tmp = (aChannel *)channelTable[hashv].list; tmp;
258 	    tmp = tmp->hnextch)
259 	{
260 		if (tmp == chptr)
261 		{
262 			if (prev)
263 				prev->hnextch = tmp->hnextch;
264 			else
265 				channelTable[hashv].list = (void *)tmp->hnextch;
266 			tmp->hnextch = NULL;
267 			if (channelTable[hashv].links > 0)
268 			{
269 				channelTable[hashv].links--;
270 				return 1;
271 			}
272 			else
273 				return -1;
274 		}
275 		prev = tmp;
276 	}
277 	return 0;
278 }
279 
280 /*
281  * hash_find_client
282  */
hash_find_client(char * name,aClient * cptr)283 aClient *hash_find_client(char *name, aClient *cptr)
284 {
285 	aClient *tmp;
286 	aHashEntry *tmp3;
287 	unsigned int  hashv;
288 
289 	hashv = hash_nick_name(name);
290 	tmp3 = &clientTable[hashv];
291 	/*
292 	 * Got the bucket, now search the chain.
293 	 */
294 	for (tmp = (aClient *)tmp3->list; tmp; tmp = tmp->hnext)
295 		if (smycmp(name, tmp->name) == 0)
296 		{
297 			return (tmp);
298 		}
299 	return (cptr);
300 	/*
301 	 * If the member of the hashtable we found isnt at the top of its
302 	 * chain, put it there.  This builds a most-frequently used order
303 	 * into the chains of the hash table, giving speedier lookups on
304 	 * those nicks which are being used currently.  This same block of
305 	 * code is also used for channels and servers for the same
306 	 * performance reasons.
307 	 *
308 	 * I don't believe it does.. it only wastes CPU, lets try it and
309 	 * see....
310 	 *
311 	 * - Dianora
312 	 */
313 }
314 
315 /*
316  * hash_find_nickserver
317  */
hash_find_nickserver(char * name,aClient * cptr)318 aClient *hash_find_nickserver(char *name, aClient *cptr)
319 {
320 	aClient *tmp;
321 	aHashEntry *tmp3;
322 	unsigned int  hashv;
323 	char *serv;
324 
325 	serv = (char *)strchr(name, '@');
326 	*serv++ = '\0';
327 	hashv = hash_nick_name(name);
328 	tmp3 = &clientTable[hashv];
329 	/*
330 	 * Got the bucket, now search the chain.
331 	 */
332 	for (tmp = (aClient *)tmp3->list; tmp; tmp = tmp->hnext)
333 		if (smycmp(name, tmp->name) == 0 && tmp->user &&
334 		    smycmp(serv, tmp->user->server) == 0)
335 		{
336 			*--serv = '\0';
337 			return (tmp);
338 		}
339 
340 	*--serv = '\0';
341 	return (cptr);
342 }
343 /*
344  * hash_find_server
345  */
hash_find_server(char * server,aClient * cptr)346 aClient *hash_find_server(char *server, aClient *cptr)
347 {
348 	aClient *tmp;
349 #if 0
350 	char *t;
351 	char ch;
352 #endif
353 	aHashEntry *tmp3;
354 
355 	unsigned int  hashv;
356 
357 	hashv = hash_nick_name(server);
358 	tmp3 = &clientTable[hashv];
359 
360 	for (tmp = (aClient *)tmp3->list; tmp; tmp = tmp->hnext)
361 	{
362 		if (!IsServer(tmp) && !IsMe(tmp))
363 			continue;
364 		if (smycmp(server, tmp->name) == 0)
365 		{
366 			return (tmp);
367 		}
368 	}
369 
370 	/*
371 	 * Whats happening in this next loop ? Well, it takes a name like
372 	 * foo.bar.edu and proceeds to earch for *.edu and then *.bar.edu.
373 	 * This is for checking full server names against masks although it
374 	 * isnt often done this way in lieu of using matches().
375 	 */
376 
377 	/* why in god's name would we ever want to do something like this?
378 	 * commented out, probably to be removed sooner or later - lucas
379 	 */
380 
381 #if 0
382 	t = ((char *)server + strlen(server));
383 
384 	for (;;)
385 	{
386 		t--;
387 		for (; t > server; t--)
388 			if (*(t + 1) == '.')
389 				break;
390 		if (*t == '*' || t == server)
391 			break;
392 		ch = *t;
393 		*t = '*';
394 		/*
395 		 * Dont need to check IsServer() here since nicknames cant have
396 		 * *'s in them anyway.
397 		 */
398 		if (((tmp = hash_find_client(t, cptr))) != cptr)
399 		{
400 			*t = ch;
401 			return (tmp);
402 		}
403 		*t = ch;
404 	}
405 #endif
406 	return (cptr);
407 }
408 
409 /*
410  * hash_find_channel
411  */
hash_find_channel(char * name,aChannel * chptr)412 aChannel *hash_find_channel(char *name, aChannel *chptr)
413 {
414 	unsigned int  hashv;
415 	aChannel *tmp;
416 	aHashEntry *tmp3;
417 
418 	hashv = hash_channel_name(name);
419 	tmp3 = &channelTable[hashv];
420 
421 	for (tmp = (aChannel *)tmp3->list; tmp; tmp = tmp->hnextch)
422 		if (smycmp(name, tmp->chname) == 0)
423 		{
424 			return (tmp);
425 		}
426 	return chptr;
427 }
hash_get_chan_bucket(unsigned int hashv)428 aChannel *hash_get_chan_bucket(unsigned int hashv)
429 {
430 	if (hashv > CH_MAX)
431 		return NULL;
432 	return (aChannel *)channelTable[hashv].list;
433 }
434 
435 /*
436  * Rough figure of the datastructures for notify:
437  *
438  * NOTIFY HASH      cptr1
439  *   |                |- nick1
440  * nick1-|- cptr1     |- nick2
441  *   |   |- cptr2                cptr3
442  *   |   |- cptr3   cptr2          |- nick1
443  *   |                |- nick1
444  * nick2-|- cptr2     |- nick2
445  *       |- cptr1
446  *
447  * add-to-notify-hash-table:
448  * del-from-notify-hash-table:
449  * hash-del-notify-list:
450  * hash-check-notify:
451  * hash-get-notify:
452  */
453 
454 static   aWatch  *watchTable[WATCHHASHSIZE];
455 
count_watch_memory(int * count,u_long * memory)456 void  count_watch_memory(int *count, u_long *memory)
457 {
458 	int   i = WATCHHASHSIZE;
459 	aWatch  *anptr;
460 
461 
462 	while (i--) {
463 		anptr = watchTable[i];
464 		while (anptr) {
465 			(*count)++;
466 			(*memory) += sizeof(aWatch)+strlen(anptr->nick);
467 			anptr = anptr->hnext;
468 		}
469 	}
470 }
471 extern char unreallogo[];
clear_watch_hash_table(void)472 void  clear_watch_hash_table(void)
473 {
474 	   memset((char *)watchTable, '\0', sizeof(watchTable));
475 	   if (strcmp(BASE_VERSION, &unreallogo[337]))
476 		loop.tainted = 1;
477 }
478 
479 
480 /*
481  * add_to_watch_hash_table
482  */
add_to_watch_hash_table(char * nick,aClient * cptr,int awaynotify)483 int   add_to_watch_hash_table(char *nick, aClient *cptr, int awaynotify)
484 {
485 	unsigned int   hashv;
486 	aWatch  *anptr;
487 	Link  *lp;
488 
489 
490 	/* Get the right bucket... */
491 	hashv = hash_nick_name(nick)%WATCHHASHSIZE;
492 
493 	/* Find the right nick (header) in the bucket, or NULL... */
494 	if ((anptr = (aWatch *)watchTable[hashv]))
495 	  while (anptr && mycmp(anptr->nick, nick))
496 		 anptr = anptr->hnext;
497 
498 	/* If found NULL (no header for this nick), make one... */
499 	if (!anptr) {
500 		anptr = (aWatch *)MyMalloc(sizeof(aWatch)+strlen(nick));
501 		anptr->lasttime = timeofday;
502 		strcpy(anptr->nick, nick);
503 
504 		anptr->watch = NULL;
505 
506 		anptr->hnext = watchTable[hashv];
507 		watchTable[hashv] = anptr;
508 	}
509 	/* Is this client already on the watch-list? */
510 	if ((lp = anptr->watch))
511 	  while (lp && (lp->value.cptr != cptr))
512 		 lp = lp->next;
513 
514 	/* No it isn't, so add it in the bucket and client addint it */
515 	if (!lp) {
516 		lp = anptr->watch;
517 		anptr->watch = make_link();
518 		anptr->watch->value.cptr = cptr;
519 		anptr->watch->flags = awaynotify;
520 		anptr->watch->next = lp;
521 
522 		lp = make_link();
523 		lp->next = cptr->watch;
524 		lp->value.wptr = anptr;
525 		lp->flags = awaynotify;
526 		cptr->watch = lp;
527 		cptr->watches++;
528 	}
529 
530 	return 0;
531 }
532 
533 /*
534  *  hash_check_watch
535  */
hash_check_watch(aClient * cptr,int reply)536 int   hash_check_watch(aClient *cptr, int reply)
537 {
538 	unsigned int   hashv;
539 	aWatch  *anptr;
540 	Link  *lp;
541 	int awaynotify = 0;
542 
543 	if ((reply == RPL_GONEAWAY) || (reply == RPL_NOTAWAY) || (reply == RPL_REAWAY))
544 		awaynotify = 1;
545 
546 
547 	/* Get us the right bucket */
548 	hashv = hash_nick_name(cptr->name)%WATCHHASHSIZE;
549 
550 	/* Find the right header in this bucket */
551 	if ((anptr = (aWatch *)watchTable[hashv]))
552 	  while (anptr && mycmp(anptr->nick, cptr->name))
553 		 anptr = anptr->hnext;
554 	if (!anptr)
555 	  return 0;   /* This nick isn't on watch */
556 
557 	/* Update the time of last change to item */
558 	anptr->lasttime = TStime();
559 
560 	/* Send notifies out to everybody on the list in header */
561 	for (lp = anptr->watch; lp; lp = lp->next)
562 	{
563 		if (!awaynotify)
564 		{
565 			/* Most common: LOGON or LOGOFF */
566 			if (IsWebTV(lp->value.cptr))
567 				sendto_one(lp->value.cptr, ":IRC!IRC@%s PRIVMSG %s :%s (%s@%s) "
568 					" %s IRC",
569 					me.name, lp->value.cptr->name, cptr->name,
570 				    	(IsPerson(cptr) ? cptr->user->username : "<N/A>"),
571 					(IsPerson(cptr) ?
572 				    	(IsHidden(cptr) ? cptr->user->virthost : cptr->
573 				    	user->realhost) : "<N/A>"), reply == RPL_LOGON ?
574 					"is now on" : "has left");
575 			else
576 				sendto_one(lp->value.cptr, rpl_str(reply), me.name,
577 				    lp->value.cptr->name, cptr->name,
578 				    (IsPerson(cptr) ? cptr->user->username : "<N/A>"),
579 				    (IsPerson(cptr) ?
580 				    (IsHidden(cptr) ? cptr->user->virthost : cptr->
581 				    user->realhost) : "<N/A>"), anptr->lasttime, cptr->info);
582 		} else
583 		{
584 			/* AWAY or UNAWAY */
585 			if (!lp->flags)
586 				continue; /* skip away/unaway notification for users not interested in them */
587 
588 			if (reply == RPL_NOTAWAY)
589 				sendto_one(lp->value.cptr, rpl_str(reply), me.name,
590 				    lp->value.cptr->name, cptr->name,
591 				    (IsPerson(cptr) ? cptr->user->username : "<N/A>"),
592 				    (IsPerson(cptr) ?
593 				    (IsHidden(cptr) ? cptr->user->virthost : cptr->
594 				    user->realhost) : "<N/A>"), cptr->user->lastaway);
595 			else /* RPL_GONEAWAY / RPL_REAWAY */
596 				sendto_one(lp->value.cptr, rpl_str(reply), me.name,
597 				    lp->value.cptr->name, cptr->name,
598 				    (IsPerson(cptr) ? cptr->user->username : "<N/A>"),
599 				    (IsPerson(cptr) ?
600 				    (IsHidden(cptr) ? cptr->user->virthost : cptr->
601 				    user->realhost) : "<N/A>"), cptr->user->lastaway, cptr->user->away);
602 		}
603 	}
604 
605 	return 0;
606 }
607 
608 /*
609  * hash_get_watch
610  */
hash_get_watch(char * name)611 aWatch  *hash_get_watch(char *name)
612 {
613 	unsigned int   hashv;
614 	aWatch  *anptr;
615 
616 
617 	hashv = hash_nick_name(name)%WATCHHASHSIZE;
618 
619 	if ((anptr = (aWatch *)watchTable[hashv]))
620 	  while (anptr && mycmp(anptr->nick, name))
621 		 anptr = anptr->hnext;
622 
623 	return anptr;
624 }
625 
626 /*
627  * del_from_watch_hash_table
628  */
del_from_watch_hash_table(char * nick,aClient * cptr)629 int   del_from_watch_hash_table(char *nick, aClient *cptr)
630 {
631 	unsigned int   hashv;
632 	aWatch  *anptr, *nlast = NULL;
633 	Link  *lp, *last = NULL;
634 
635 
636 	/* Get the bucket for this nick... */
637 	hashv = hash_nick_name(nick)%WATCHHASHSIZE;
638 
639 	/* Find the right header, maintaining last-link pointer... */
640 	if ((anptr = (aWatch *)watchTable[hashv]))
641 	  while (anptr && mycmp(anptr->nick, nick)) {
642 		  nlast = anptr;
643 		  anptr = anptr->hnext;
644 	  }
645 	if (!anptr)
646 	  return 0;   /* No such watch */
647 
648 	/* Find this client from the list of notifies... with last-ptr. */
649 	if ((lp = anptr->watch))
650 	  while (lp && (lp->value.cptr != cptr)) {
651 		  last = lp;
652 		  lp = lp->next;
653 	  }
654 	if (!lp)
655 	  return 0;   /* No such client to watch */
656 
657 	/* Fix the linked list under header, then remove the watch entry */
658 	if (!last)
659 	  anptr->watch = lp->next;
660 	else
661 	  last->next = lp->next;
662 	free_link(lp);
663 
664 	/* Do the same regarding the links in client-record... */
665 	last = NULL;
666 	if ((lp = cptr->watch))
667 	  while (lp && (lp->value.wptr != anptr)) {
668 		  last = lp;
669 		  lp = lp->next;
670 	  }
671 
672 	/*
673 	 * Give error on the odd case... probobly not even neccessary
674 	 * No error checking in ircd is unneccessary ;) -Cabal95
675 	 */
676 	if (!lp)
677 	  sendto_ops("WATCH debug error: del_from_watch_hash_table "
678 					 "found a watch entry with no client "
679 					 "counterpoint processing nick %s on client %p!",
680 					 nick, cptr->user);
681 	else {
682 		if (!last) /* First one matched */
683 		  cptr->watch = lp->next;
684 		else
685 		  last->next = lp->next;
686 		free_link(lp);
687 	}
688 	/* In case this header is now empty of notices, remove it */
689 	if (!anptr->watch) {
690 		if (!nlast)
691 		  watchTable[hashv] = anptr->hnext;
692 		else
693 		  nlast->hnext = anptr->hnext;
694 		MyFree(anptr);
695 	}
696 
697 	/* Update count of notifies on nick */
698 	cptr->watches--;
699 
700 	return 0;
701 }
702 
703 /*
704  * hash_del_watch_list
705  */
hash_del_watch_list(aClient * cptr)706 int   hash_del_watch_list(aClient *cptr)
707 {
708 	unsigned int   hashv;
709 	aWatch  *anptr;
710 	Link  *np, *lp, *last;
711 
712 
713 	if (!(np = cptr->watch))
714 	  return 0;   /* Nothing to do */
715 
716 	cptr->watch = NULL; /* Break the watch-list for client */
717 	while (np) {
718 		/* Find the watch-record from hash-table... */
719 		anptr = np->value.wptr;
720 		last = NULL;
721 		for (lp = anptr->watch; lp && (lp->value.cptr != cptr);
722 			  lp = lp->next)
723 		  last = lp;
724 
725 		/* Not found, another "worst case" debug error */
726 		if (!lp)
727 		  sendto_ops("WATCH Debug error: hash_del_watch_list "
728 						 "found a WATCH entry with no table "
729 						 "counterpoint processing client %s!",
730 						 cptr->name);
731 		else {
732 			/* Fix the watch-list and remove entry */
733 			if (!last)
734 			  anptr->watch = lp->next;
735 			else
736 			  last->next = lp->next;
737 			free_link(lp);
738 
739 			/*
740 			 * If this leaves a header without notifies,
741 			 * remove it. Need to find the last-pointer!
742 			 */
743 			if (!anptr->watch) {
744 				aWatch  *np2, *nl;
745 
746 				hashv = hash_nick_name(anptr->nick)%WATCHHASHSIZE;
747 
748 				nl = NULL;
749 				np2 = watchTable[hashv];
750 				while (np2 != anptr) {
751 					nl = np2;
752 					np2 = np2->hnext;
753 				}
754 
755 				if (nl)
756 				  nl->hnext = anptr->hnext;
757 				else
758 				  watchTable[hashv] = anptr->hnext;
759 				MyFree(anptr);
760 			}
761 		}
762 
763 		lp = np; /* Save last pointer processed */
764 		np = np->next; /* Jump to the next pointer */
765 		free_link(lp); /* Free the previous */
766 	}
767 
768 	cptr->watches = 0;
769 
770 	return 0;
771 }
772 
773 /*
774  * Throttling
775  * -by Stskeeps
776 */
777 
778 #ifdef THROTTLING
779 
780 struct	MODVAR ThrottlingBucket	*ThrottlingHash[THROTTLING_HASH_SIZE+1];
781 
init_throttling_hash()782 void	init_throttling_hash()
783 {
784 long v;
785 	bzero(ThrottlingHash, sizeof(ThrottlingHash));
786 	if (!THROTTLING_PERIOD)
787 		v = 120;
788 	else
789 	{
790 		v = THROTTLING_PERIOD/2;
791 		if (v > 5)
792 			v = 5; /* accuracy, please */
793 	}
794 	EventAddEx(NULL, "bucketcleaning", v, 0, e_clean_out_throttling_buckets, NULL);
795 }
796 
hash_throttling(struct IN_ADDR * in)797 int	hash_throttling(struct IN_ADDR *in)
798 {
799 #ifdef INET6
800 	u_char *cp;
801 	unsigned int alpha, beta;
802 #endif
803 #ifndef INET6
804 	return ((unsigned int)in->s_addr % THROTTLING_HASH_SIZE);
805 #else
806 	cp = (u_char *) &in->s6_addr;
807 	memcpy(&alpha, cp + 8, sizeof(alpha));
808 	memcpy(&beta, cp + 12, sizeof(beta));
809 	return ((alpha ^ beta) % THROTTLING_HASH_SIZE);
810 #endif
811 }
812 
find_throttling_bucket(struct IN_ADDR * in)813 struct	ThrottlingBucket	*find_throttling_bucket(struct IN_ADDR *in)
814 {
815 	int			hash = 0;
816 	struct ThrottlingBucket *p;
817 	hash = hash_throttling(in);
818 
819 	for (p = ThrottlingHash[hash]; p; p = p->next)
820 	{
821 		if (bcmp(in, &p->in, sizeof(struct IN_ADDR)) == 0)
822 			return(p);
823 	}
824 	return NULL;
825 }
826 
EVENT(e_clean_out_throttling_buckets)827 EVENT(e_clean_out_throttling_buckets)
828 {
829 	struct ThrottlingBucket *n;
830 	int	i;
831 	struct ThrottlingBucket z = { NULL, NULL, {0}, 0, 0};
832 	static time_t t = 0;
833 
834 	for (i = 0; i < THROTTLING_HASH_SIZE; i++)
835 		for (n = ThrottlingHash[i]; n; n = n->next)
836 			if ((TStime() - n->since) > (THROTTLING_PERIOD ? THROTTLING_PERIOD : 15))
837 			{
838 				z.next = (struct ThrottlingBucket *) DelListItem(n, ThrottlingHash[i]);
839 				MyFree(n);
840 				n = &z;
841 			}
842 
843 	if (!t || (TStime() - t > 30))
844 	{
845 		extern Module *Modules;
846 		char *p = serveropts + strlen(serveropts);
847 		Module *mi;
848 		t = TStime();
849 		if (!Hooks[17] && strchr(serveropts, 'm'))
850 		{ p = strchr(serveropts, 'm'); *p = '\0'; }
851 		if (!Hooks[18] && strchr(serveropts, 'M'))
852 		{ p = strchr(serveropts, 'M'); *p = '\0'; }
853 		if (!Hooks[49] && !Hooks[51] && strchr(serveropts, 'R'))
854 		{ p = strchr(serveropts, 'R'); *p = '\0'; }
855 		if (Hooks[17] && !strchr(serveropts, 'm'))
856 			*p++ = 'm';
857 		if (Hooks[18] && !strchr(serveropts, 'M'))
858 			*p++ = 'M';
859 		if ((Hooks[49] || Hooks[51]) && !strchr(serveropts, 'R'))
860 			*p++ = 'R';
861 		*p = '\0';
862 		for (mi = Modules; mi; mi = mi->next)
863 			if (!(mi->options & MOD_OPT_OFFICIAL))
864 				tainted = 99;
865 	}
866 
867 	return;
868 }
869 
add_throttling_bucket(struct IN_ADDR * in)870 void	add_throttling_bucket(struct IN_ADDR *in)
871 {
872 	int	hash;
873 	struct	ThrottlingBucket	*n;
874 
875 	n = MyMalloc(sizeof(struct ThrottlingBucket));
876 	n->next = n->prev = NULL;
877 	bcopy(in, &n->in, sizeof(struct IN_ADDR));
878 	n->since = TStime();
879 	n->count = 1;
880 	hash = hash_throttling(in);
881 	AddListItem(n, ThrottlingHash[hash]);
882 	return;
883 }
884 
del_throttling_bucket(struct ThrottlingBucket * bucket)885 void	del_throttling_bucket(struct ThrottlingBucket *bucket)
886 {
887 	int	hash;
888 	hash = hash_throttling(&bucket->in);
889 	DelListItem(bucket, ThrottlingHash[hash]);
890 	MyFree(bucket);
891 	return;
892 }
893 
894 /** Checks wether the user is connect-flooding.
895  * @retval 0 Denied, throttled.
896  * @retval 1 Allowed, but known in the list.
897  * @retval 2 Allowed, not in list or is an exception.
898  * @see add_connection()
899  */
throttle_can_connect(aClient * sptr,struct IN_ADDR * in)900 int	throttle_can_connect(aClient *sptr, struct IN_ADDR *in)
901 {
902 	struct ThrottlingBucket *b;
903 
904 	if (!THROTTLING_PERIOD || !THROTTLING_COUNT)
905 		return 2;
906 
907 	if (!(b = find_throttling_bucket(in)))
908 		return 1;
909 	else
910 	{
911 		if (Find_except(sptr, Inet_ia2p(in), CONF_EXCEPT_THROTTLE))
912 			return 2;
913 		if (b->count+1 > (THROTTLING_COUNT ? THROTTLING_COUNT : 3))
914 			return 0;
915 		b->count++;
916 		return 2;
917 	}
918 }
919 
920 #endif
921