]> jfr.im git - solanum.git/blob - ircd/hash.c
extensions/helpops: implement DEHELPER command
[solanum.git] / ircd / hash.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * hash.c: Maintains hashtables.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 *
24 * $Id: hash.c 3177 2007-02-01 00:19:14Z jilles $
25 */
26
27 #include "stdinc.h"
28 #include "ircd_defs.h"
29 #include "s_conf.h"
30 #include "channel.h"
31 #include "client.h"
32 #include "common.h"
33 #include "hash.h"
34 #include "match.h"
35 #include "ircd.h"
36 #include "numeric.h"
37 #include "send.h"
38 #include "msg.h"
39 #include "cache.h"
40 #include "s_newconf.h"
41 #include "s_assert.h"
42 #include "irc_dictionary.h"
43 #include "irc_radixtree.h"
44
45 rb_dlink_list *hostTable;
46
47 struct Dictionary *client_connid_tree = NULL;
48 struct Dictionary *client_zconnid_tree = NULL;
49 struct irc_radixtree *client_id_tree = NULL;
50 struct irc_radixtree *client_name_tree = NULL;
51
52 struct irc_radixtree *channel_tree = NULL;
53 struct irc_radixtree *resv_tree = NULL;
54
55 /*
56 * look in whowas.c for the missing ...[WW_MAX]; entry
57 */
58
59 /* init_hash()
60 *
61 * clears the various hashtables
62 */
63 void
64 init_hash(void)
65 {
66 hostTable = rb_malloc(sizeof(rb_dlink_list) * HOST_MAX);
67
68 client_connid_tree = irc_dictionary_create("client connid", irc_uint32cmp);
69 client_zconnid_tree = irc_dictionary_create("client zconnid", irc_uint32cmp);
70 client_id_tree = irc_radixtree_create("client id", NULL);
71 client_name_tree = irc_radixtree_create("client name", irc_radixtree_irccasecanon);
72
73 channel_tree = irc_radixtree_create("channel", irc_radixtree_irccasecanon);
74 resv_tree = irc_radixtree_create("resv", irc_radixtree_irccasecanon);
75 }
76
77 u_int32_t
78 fnv_hash_upper(const unsigned char *s, int bits)
79 {
80 u_int32_t h = FNV1_32_INIT;
81
82 while (*s)
83 {
84 h ^= ToUpper(*s++);
85 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
86 }
87 if (bits < 32)
88 h = ((h >> bits) ^ h) & ((1<<bits)-1);
89 return h;
90 }
91
92 u_int32_t
93 fnv_hash(const unsigned char *s, int bits)
94 {
95 u_int32_t h = FNV1_32_INIT;
96
97 while (*s)
98 {
99 h ^= *s++;
100 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
101 }
102 if (bits < 32)
103 h = ((h >> bits) ^ h) & ((1<<bits)-1);
104 return h;
105 }
106
107 u_int32_t
108 fnv_hash_len(const unsigned char *s, int bits, int len)
109 {
110 u_int32_t h = FNV1_32_INIT;
111 const unsigned char *x = s + len;
112 while (*s && s < x)
113 {
114 h ^= *s++;
115 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
116 }
117 if (bits < 32)
118 h = ((h >> bits) ^ h) & ((1<<bits)-1);
119 return h;
120 }
121
122 u_int32_t
123 fnv_hash_upper_len(const unsigned char *s, int bits, int len)
124 {
125 u_int32_t h = FNV1_32_INIT;
126 const unsigned char *x = s + len;
127 while (*s && s < x)
128 {
129 h ^= ToUpper(*s++);
130 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
131 }
132 if (bits < 32)
133 h = ((h >> bits) ^ h) & ((1<<bits)-1);
134 return h;
135 }
136
137 /* hash_hostname()
138 *
139 * hashes a hostname, based on first 30 chars only, as thats likely to
140 * be more dynamic than rest.
141 */
142 static u_int32_t
143 hash_hostname(const char *name)
144 {
145 return fnv_hash_upper_len((const unsigned char *) name, HOST_MAX_BITS, 30);
146 }
147
148 /* add_to_id_hash()
149 *
150 * adds an entry to the id hash table
151 */
152 void
153 add_to_id_hash(const char *name, struct Client *client_p)
154 {
155 if(EmptyString(name) || (client_p == NULL))
156 return;
157
158 irc_radixtree_add(client_id_tree, name, client_p);
159 }
160
161 /* add_to_client_hash()
162 *
163 * adds an entry (client/server) to the client hash table
164 */
165 void
166 add_to_client_hash(const char *name, struct Client *client_p)
167 {
168 s_assert(name != NULL);
169 s_assert(client_p != NULL);
170 if(EmptyString(name) || (client_p == NULL))
171 return;
172
173 irc_radixtree_add(client_name_tree, name, client_p);
174 }
175
176 /* add_to_hostname_hash()
177 *
178 * adds a client entry to the hostname hash table
179 */
180 void
181 add_to_hostname_hash(const char *hostname, struct Client *client_p)
182 {
183 unsigned int hashv;
184
185 s_assert(hostname != NULL);
186 s_assert(client_p != NULL);
187 if(EmptyString(hostname) || (client_p == NULL))
188 return;
189
190 hashv = hash_hostname(hostname);
191 rb_dlinkAddAlloc(client_p, &hostTable[hashv]);
192 }
193
194 /* add_to_resv_hash()
195 *
196 * adds a resv channel entry to the resv hash table
197 */
198 void
199 add_to_resv_hash(const char *name, struct ConfItem *aconf)
200 {
201 s_assert(!EmptyString(name));
202 s_assert(aconf != NULL);
203 if(EmptyString(name) || aconf == NULL)
204 return;
205
206 irc_radixtree_add(resv_tree, name, aconf);
207 }
208
209 /* del_from_id_hash()
210 *
211 * removes an id from the id hash table
212 */
213 void
214 del_from_id_hash(const char *id, struct Client *client_p)
215 {
216 s_assert(id != NULL);
217 s_assert(client_p != NULL);
218 if(EmptyString(id) || client_p == NULL)
219 return;
220
221 irc_radixtree_delete(client_id_tree, id);
222 }
223
224 /* del_from_client_hash()
225 *
226 * removes a client/server from the client hash table
227 */
228 void
229 del_from_client_hash(const char *name, struct Client *client_p)
230 {
231 /* no s_asserts, this can happen when removing a client that
232 * is unregistered.
233 */
234 if(EmptyString(name) || client_p == NULL)
235 return;
236
237 irc_radixtree_delete(client_name_tree, name);
238 }
239
240 /* del_from_channel_hash()
241 *
242 * removes a channel from the channel hash table
243 */
244 void
245 del_from_channel_hash(const char *name, struct Channel *chptr)
246 {
247 s_assert(name != NULL);
248 s_assert(chptr != NULL);
249
250 if(EmptyString(name) || chptr == NULL)
251 return;
252
253 irc_radixtree_delete(channel_tree, name);
254 }
255
256 /* del_from_hostname_hash()
257 *
258 * removes a client entry from the hostname hash table
259 */
260 void
261 del_from_hostname_hash(const char *hostname, struct Client *client_p)
262 {
263 unsigned int hashv;
264
265 if(hostname == NULL || client_p == NULL)
266 return;
267
268 hashv = hash_hostname(hostname);
269
270 rb_dlinkFindDestroy(client_p, &hostTable[hashv]);
271 }
272
273 /* del_from_resv_hash()
274 *
275 * removes a resv entry from the resv hash table
276 */
277 void
278 del_from_resv_hash(const char *name, struct ConfItem *aconf)
279 {
280 s_assert(name != NULL);
281 s_assert(aconf != NULL);
282 if(EmptyString(name) || aconf == NULL)
283 return;
284
285 irc_radixtree_delete(resv_tree, name);
286 }
287
288 /* find_id()
289 *
290 * finds a client entry from the id hash table
291 */
292 struct Client *
293 find_id(const char *name)
294 {
295 if(EmptyString(name))
296 return NULL;
297
298 return irc_radixtree_retrieve(client_id_tree, name);
299 }
300
301 /* find_client()
302 *
303 * finds a client/server entry from the client hash table
304 */
305 struct Client *
306 find_client(const char *name)
307 {
308 s_assert(name != NULL);
309 if(EmptyString(name))
310 return NULL;
311
312 /* hunting for an id, not a nick */
313 if(IsDigit(*name))
314 return (find_id(name));
315
316 return irc_radixtree_retrieve(client_name_tree, name);
317 }
318
319 /* find_named_client()
320 *
321 * finds a client/server entry from the client hash table
322 */
323 struct Client *
324 find_named_client(const char *name)
325 {
326 s_assert(name != NULL);
327 if(EmptyString(name))
328 return NULL;
329
330 return irc_radixtree_retrieve(client_name_tree, name);
331 }
332
333 /* find_server()
334 *
335 * finds a server from the client hash table
336 */
337 struct Client *
338 find_server(struct Client *source_p, const char *name)
339 {
340 struct Client *target_p;
341
342 if(EmptyString(name))
343 return NULL;
344
345 if((source_p == NULL || !MyClient(source_p)) &&
346 IsDigit(*name) && strlen(name) == 3)
347 {
348 target_p = find_id(name);
349 return(target_p);
350 }
351
352 target_p = irc_radixtree_retrieve(client_name_tree, name);
353 if (target_p != NULL)
354 {
355 if(IsServer(target_p) || IsMe(target_p))
356 return target_p;
357 }
358
359 return NULL;
360 }
361
362 /* find_hostname()
363 *
364 * finds a hostname rb_dlink list from the hostname hash table.
365 * we return the full rb_dlink list, because you can have multiple
366 * entries with the same hostname
367 */
368 rb_dlink_node *
369 find_hostname(const char *hostname)
370 {
371 unsigned int hashv;
372
373 if(EmptyString(hostname))
374 return NULL;
375
376 hashv = hash_hostname(hostname);
377
378 return hostTable[hashv].head;
379 }
380
381 /* find_channel()
382 *
383 * finds a channel from the channel hash table
384 */
385 struct Channel *
386 find_channel(const char *name)
387 {
388 s_assert(name != NULL);
389 if(EmptyString(name))
390 return NULL;
391
392 return irc_radixtree_retrieve(channel_tree, name);
393 }
394
395 /*
396 * get_or_create_channel
397 * inputs - client pointer
398 * - channel name
399 * - pointer to int flag whether channel was newly created or not
400 * output - returns channel block or NULL if illegal name
401 * - also modifies *isnew
402 *
403 * Get Channel block for chname (and allocate a new channel
404 * block, if it didn't exist before).
405 */
406 struct Channel *
407 get_or_create_channel(struct Client *client_p, const char *chname, int *isnew)
408 {
409 struct Channel *chptr;
410 int len;
411 const char *s = chname;
412
413 if(EmptyString(s))
414 return NULL;
415
416 len = strlen(s);
417 if(len > CHANNELLEN)
418 {
419 char *t;
420 if(IsServer(client_p))
421 {
422 sendto_realops_snomask(SNO_DEBUG, L_ALL,
423 "*** Long channel name from %s (%d > %d): %s",
424 client_p->name, len, CHANNELLEN, s);
425 }
426 len = CHANNELLEN;
427 t = LOCAL_COPY(s);
428 *(t + CHANNELLEN) = '\0';
429 s = t;
430 }
431
432 chptr = irc_radixtree_retrieve(channel_tree, s);
433 if (chptr != NULL)
434 {
435 if (isnew != NULL)
436 *isnew = 0;
437 return chptr;
438 }
439
440 if(isnew != NULL)
441 *isnew = 1;
442
443 chptr = allocate_channel(s);
444 chptr->channelts = rb_current_time(); /* doesn't hurt to set it here */
445
446 rb_dlinkAdd(chptr, &chptr->node, &global_channel_list);
447 irc_radixtree_add(channel_tree, chptr->chname, chptr);
448
449 return chptr;
450 }
451
452 /* hash_find_resv()
453 *
454 * hunts for a resv entry in the resv hash table
455 */
456 struct ConfItem *
457 hash_find_resv(const char *name)
458 {
459 struct ConfItem *aconf;
460
461 s_assert(name != NULL);
462 if(EmptyString(name))
463 return NULL;
464
465 aconf = irc_radixtree_retrieve(resv_tree, name);
466 if (aconf != NULL)
467 {
468 aconf->port++;
469 return aconf;
470 }
471
472 return NULL;
473 }
474
475 void
476 clear_resv_hash(void)
477 {
478 struct ConfItem *aconf;
479 struct irc_radixtree_iteration_state iter;
480
481 IRC_RADIXTREE_FOREACH(aconf, &iter, resv_tree)
482 {
483 /* skip temp resvs */
484 if(aconf->hold)
485 continue;
486
487 irc_radixtree_delete(resv_tree, aconf->host);
488 free_conf(aconf);
489 }
490 }
491
492 void
493 add_to_zconnid_hash(struct Client *client_p)
494 {
495 irc_dictionary_add(client_zconnid_tree, IRC_UINT_TO_POINTER(client_p->localClient->zconnid), client_p);
496 }
497
498 void
499 del_from_zconnid_hash(struct Client *client_p)
500 {
501 irc_dictionary_delete(client_zconnid_tree, IRC_UINT_TO_POINTER(client_p->localClient->zconnid));
502 }
503
504 void
505 add_to_cli_connid_hash(struct Client *client_p)
506 {
507 irc_dictionary_add(client_connid_tree, IRC_UINT_TO_POINTER(client_p->localClient->connid), client_p);
508 }
509
510 void
511 del_from_cli_connid_hash(struct Client *client_p)
512 {
513 irc_dictionary_delete(client_connid_tree, IRC_UINT_TO_POINTER(client_p->localClient->connid));
514 }
515
516 struct Client *
517 find_cli_connid_hash(uint32_t connid)
518 {
519 struct Client *target_p;
520
521 target_p = irc_dictionary_retrieve(client_connid_tree, IRC_UINT_TO_POINTER(connid));
522 if (target_p != NULL)
523 return target_p;
524
525 target_p = irc_dictionary_retrieve(client_zconnid_tree, IRC_UINT_TO_POINTER(connid));
526 if (target_p != NULL)
527 return target_p;
528
529 return NULL;
530 }
531
532 static void
533 output_hash(struct Client *source_p, const char *name, int length, int *counts, unsigned long deepest)
534 {
535 unsigned long total = 0;
536 int i;
537 char buf[128];
538
539 sendto_one_numeric(source_p, RPL_STATSDEBUG,
540 "B :%s Hash Statistics", name);
541
542 snprintf(buf, sizeof buf, "%.3f%%",
543 (float) ((counts[0]*100) / (float) length));
544 sendto_one_numeric(source_p, RPL_STATSDEBUG,
545 "B :Size: %d Empty: %d (%s)",
546 length, counts[0], buf);
547
548 for(i = 1; i < 11; i++)
549 {
550 total += (counts[i] * i);
551 }
552
553 /* dont want to divide by 0! --fl */
554 if(counts[0] != length)
555 {
556 snprintf(buf, sizeof buf, "%.3f/%.3f",
557 (float) (total / (length - counts[0])),
558 (float) (total / length));
559 sendto_one_numeric(source_p, RPL_STATSDEBUG,
560 "B :Average depth: %s Highest depth: %lu",
561 buf, deepest);
562 }
563 }
564
565
566 static void
567 count_hash(struct Client *source_p, rb_dlink_list *table, int length, const char *name)
568 {
569 int counts[11];
570 unsigned long deepest = 0;
571 int i;
572
573 memset(counts, 0, sizeof(counts));
574
575 for(i = 0; i < length; i++)
576 {
577 if(rb_dlink_list_length(&table[i]) >= 10)
578 counts[10]++;
579 else
580 counts[rb_dlink_list_length(&table[i])]++;
581
582 if(rb_dlink_list_length(&table[i]) > deepest)
583 deepest = rb_dlink_list_length(&table[i]);
584 }
585
586 output_hash(source_p, name, length, counts, deepest);
587 }
588
589 void
590 hash_stats(struct Client *source_p)
591 {
592 count_hash(source_p, hostTable, HOST_MAX, "Hostname");
593 }