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