1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2021 ircd-hybrid development team
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 2 of the License, or
9 * (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file hash.c
23 * \brief Hash table management.
24 * \version $Id: hash.c 9858 2021-01-01 04:43:42Z michael $
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "conf.h"
30 #include "channel.h"
31 #include "channel_mode.h"
32 #include "client.h"
33 #include "hash.h"
34 #include "id.h"
35 #include "rng_mt.h"
36 #include "irc_string.h"
37 #include "ircd.h"
38 #include "numeric.h"
39 #include "send.h"
40 #include "memory.h"
41 #include "dbuf.h"
42
43
44 /* The actual hash tables, They MUST be of the same HASHSIZE, variable
45 * size tables could be supported but the rehash routine should also
46 * rebuild the transformation maps, I kept the tables of equal size
47 * so that I can use one hash function.
48 */
49 static struct Client *idTable[HASHSIZE];
50 static struct Client *clientTable[HASHSIZE];
51 static struct Channel *channelTable[HASHSIZE];
52
53
54 /*
55 * New hash function based on the Fowler/Noll/Vo (FNV) algorithm from
56 * http://www.isthe.com/chongo/tech/comp/fnv/
57 *
58 * Here, we use the FNV-1 method, which gives slightly better results
59 * than FNV-1a. -Michael
60 */
61 unsigned int
strhash(const char * name)62 strhash(const char *name)
63 {
64 static unsigned int hashf_xor_key = 0;
65 const unsigned char *p = (const unsigned char *)name;
66 unsigned int hval = FNV1_32_INIT;
67
68 if (EmptyString(p))
69 return 0;
70
71 if (hashf_xor_key == 0)
72 do
73 hashf_xor_key = genrand_int32() % 256; /* better than nothing --adx */
74 while (hashf_xor_key == 0);
75
76 for (; *p; ++p)
77 {
78 hval += (hval << 1) + (hval << 4) +
79 (hval << 7) + (hval << 8) + (hval << 24);
80 hval ^= (ToLower(*p) ^ hashf_xor_key);
81 }
82
83 return (hval >> FNV1_32_BITS) ^ (hval & ((1 << FNV1_32_BITS) - 1));
84 }
85
86 /************************** Externally visible functions ********************/
87
88 /* Optimization note: in these functions I supposed that the CSE optimization
89 * (Common Subexpression Elimination) does its work decently, this means that
90 * I avoided introducing new variables to do the work myself and I did let
91 * the optimizer play with more free registers, actual tests proved this
92 * solution to be faster than doing things like tmp2=tmp->hnext... and then
93 * use tmp2 myself which would have given less freedom to the optimizer.
94 */
95
96 /* hash_add_client()
97 *
98 * inputs - pointer to client
99 * output - NONE
100 * side effects - Adds a client's name in the proper hash linked
101 * list, can't fail, client must have a non-null
102 * name or expect a coredump, the name is infact
103 * taken from client->name
104 */
105 void
hash_add_client(struct Client * client)106 hash_add_client(struct Client *client)
107 {
108 const unsigned int hashv = strhash(client->name);
109
110 client->hnext = clientTable[hashv];
111 clientTable[hashv] = client;
112 }
113
114 /* hash_add_channel()
115 *
116 * inputs - pointer to channel
117 * output - NONE
118 * side effects - Adds a channel's name in the proper hash linked
119 * list, can't fail. channel must have a non-null name
120 * or expect a coredump. As before the name is taken
121 * from channel->name, we do hash its entire lenght
122 * since this proved to be statistically faster
123 */
124 void
hash_add_channel(struct Channel * channel)125 hash_add_channel(struct Channel *channel)
126 {
127 const unsigned int hashv = strhash(channel->name);
128
129 channel->hnextch = channelTable[hashv];
130 channelTable[hashv] = channel;
131 }
132
133 void
hash_add_id(struct Client * client)134 hash_add_id(struct Client *client)
135 {
136 const unsigned int hashv = strhash(client->id);
137
138 client->idhnext = idTable[hashv];
139 idTable[hashv] = client;
140 }
141
142 /* hash_del_id()
143 *
144 * inputs - pointer to client
145 * output - NONE
146 * side effects - Removes an ID from the hash linked list
147 */
148 void
hash_del_id(struct Client * client)149 hash_del_id(struct Client *client)
150 {
151 const unsigned int hashv = strhash(client->id);
152 struct Client *tmp = idTable[hashv];
153
154 if (tmp)
155 {
156 if (tmp == client)
157 {
158 idTable[hashv] = client->idhnext;
159 client->idhnext = client;
160 }
161 else
162 {
163 while (tmp->idhnext != client)
164 if ((tmp = tmp->idhnext) == NULL)
165 return;
166
167 tmp->idhnext = tmp->idhnext->idhnext;
168 client->idhnext = client;
169 }
170 }
171 }
172
173 /* hash_del_client()
174 *
175 * inputs - pointer to client
176 * output - NONE
177 * side effects - Removes a Client's name from the hash linked list
178 */
179 void
hash_del_client(struct Client * client)180 hash_del_client(struct Client *client)
181 {
182 const unsigned int hashv = strhash(client->name);
183 struct Client *tmp = clientTable[hashv];
184
185 if (tmp)
186 {
187 if (tmp == client)
188 {
189 clientTable[hashv] = client->hnext;
190 client->hnext = client;
191 }
192 else
193 {
194 while (tmp->hnext != client)
195 if ((tmp = tmp->hnext) == NULL)
196 return;
197
198 tmp->hnext = tmp->hnext->hnext;
199 client->hnext = client;
200 }
201 }
202 }
203
204 /* hash_del_channel()
205 *
206 * inputs - pointer to client
207 * output - NONE
208 * side effects - Removes the channel's name from the corresponding
209 * hash linked list
210 */
211 void
hash_del_channel(struct Channel * channel)212 hash_del_channel(struct Channel *channel)
213 {
214 const unsigned int hashv = strhash(channel->name);
215 struct Channel *tmp = channelTable[hashv];
216
217 if (tmp)
218 {
219 if (tmp == channel)
220 {
221 channelTable[hashv] = channel->hnextch;
222 channel->hnextch = channel;
223 }
224 else
225 {
226 while (tmp->hnextch != channel)
227 if ((tmp = tmp->hnextch) == NULL)
228 return;
229
230 tmp->hnextch = tmp->hnextch->hnextch;
231 channel->hnextch = channel;
232 }
233 }
234 }
235
236 /* hash_find_client()
237 *
238 * inputs - pointer to name
239 * output - NONE
240 * side effects - New semantics: finds a client whose name is 'name'
241 * if can't find one returns NULL. If it finds one moves
242 * it to the top of the list and returns it.
243 */
244 struct Client *
hash_find_client(const char * name)245 hash_find_client(const char *name)
246 {
247 const unsigned int hashv = strhash(name);
248 struct Client *client;
249
250 if ((client = clientTable[hashv]))
251 {
252 if (irccmp(name, client->name))
253 {
254 struct Client *prev;
255
256 while (prev = client, (client = client->hnext))
257 {
258 if (irccmp(name, client->name) == 0)
259 {
260 prev->hnext = client->hnext;
261 client->hnext = clientTable[hashv];
262 clientTable[hashv] = client;
263 break;
264 }
265 }
266 }
267 }
268
269 return client;
270 }
271
272 struct Client *
hash_find_id(const char * name)273 hash_find_id(const char *name)
274 {
275 const unsigned int hashv = strhash(name);
276 struct Client *client;
277
278 if ((client = idTable[hashv]))
279 {
280 if (strcmp(name, client->id))
281 {
282 struct Client *prev;
283
284 while (prev = client, (client = client->idhnext))
285 {
286 if (strcmp(name, client->id) == 0)
287 {
288 prev->idhnext = client->idhnext;
289 client->idhnext = idTable[hashv];
290 idTable[hashv] = client;
291 break;
292 }
293 }
294 }
295 }
296
297 return client;
298 }
299
300 struct Client *
hash_find_server(const char * name)301 hash_find_server(const char *name)
302 {
303 const unsigned int hashv = strhash(name);
304 struct Client *client;
305
306 if (IsDigit(*name) && strlen(name) == IRC_MAXSID)
307 return hash_find_id(name);
308
309 if ((client = clientTable[hashv]))
310 {
311 if ((!IsServer(client) && !IsMe(client)) ||
312 irccmp(name, client->name))
313 {
314 struct Client *prev;
315
316 while (prev = client, (client = client->hnext))
317 {
318 if ((IsServer(client) || IsMe(client)) &&
319 irccmp(name, client->name) == 0)
320 {
321 prev->hnext = client->hnext;
322 client->hnext = clientTable[hashv];
323 clientTable[hashv] = client;
324 break;
325 }
326 }
327 }
328 }
329
330 return client;
331 }
332
333 /* hash_find_channel()
334 *
335 * inputs - pointer to name
336 * output - NONE
337 * side effects - New semantics: finds a channel whose name is 'name',
338 * if can't find one returns NULL, if can find it moves
339 * it to the top of the list and returns it.
340 */
341 struct Channel *
hash_find_channel(const char * name)342 hash_find_channel(const char *name)
343 {
344 const unsigned int hashv = strhash(name);
345 struct Channel *channel;
346
347 if ((channel = channelTable[hashv]))
348 {
349 if (irccmp(name, channel->name))
350 {
351 struct Channel *prev;
352
353 while (prev = channel, (channel = channel->hnextch))
354 {
355 if (irccmp(name, channel->name) == 0)
356 {
357 prev->hnextch = channel->hnextch;
358 channel->hnextch = channelTable[hashv];
359 channelTable[hashv] = channel;
360 break;
361 }
362 }
363 }
364 }
365
366 return channel;
367 }
368
369 /* hash_get_bucket(int type, unsigned int hashv)
370 *
371 * inputs - hash value (must be between 0 and HASHSIZE - 1)
372 * output - NONE
373 * returns - pointer to first channel in channelTable[hashv]
374 * if that exists;
375 * NULL if there is no channel in that place;
376 * NULL if hashv is an invalid number.
377 * side effects - NONE
378 */
379 void *
hash_get_bucket(int type,unsigned int hashv)380 hash_get_bucket(int type, unsigned int hashv)
381 {
382 assert(hashv < HASHSIZE);
383
384 if (hashv >= HASHSIZE)
385 return NULL;
386
387 switch (type)
388 {
389 case HASH_TYPE_ID:
390 return idTable[hashv];
391 break;
392 case HASH_TYPE_CHANNEL:
393 return channelTable[hashv];
394 break;
395 case HASH_TYPE_CLIENT:
396 return clientTable[hashv];
397 break;
398 default:
399 assert(0);
400 }
401
402 return NULL;
403 }
404
405 /*
406 * Safe list code.
407 *
408 * The idea is really quite simple. As the link lists pointed to in
409 * each "bucket" of the channel hash table are traversed atomically
410 * there is no locking needed. Overall, yes, inconsistent reported
411 * state can still happen, but normally this isn't a big deal.
412 * I don't like sticking the code into hash.c but oh well. Moreover,
413 * if a hash isn't used in future, oops.
414 *
415 * - Dianora
416 */
417
418 /* exceeding_sendq()
419 *
420 * inputs - pointer to client to check
421 * output - 1 if client is in danger of blowing its sendq
422 * 0 if it is not.
423 * side effects -
424 *
425 * Sendq limit is fairly conservative at 1/2 (In original anyway)
426 */
427 static bool
exceeding_sendq(const struct Client * to)428 exceeding_sendq(const struct Client *to)
429 {
430 if (dbuf_length(&to->connection->buf_sendq) > (get_sendq(&to->connection->confs) / 2))
431 return true;
432 else
433 return false;
434 }
435
436 void
free_list_task(struct Client * client)437 free_list_task(struct Client *client)
438 {
439 struct ListTask *const lt = client->connection->list_task;
440 dlink_node *node, *node_next;
441
442 dlinkDelete(<->node, &listing_client_list);
443
444 DLINK_FOREACH_SAFE(node, node_next, lt->show_mask.head)
445 {
446 xfree(node->data);
447 dlinkDelete(node, <->show_mask);
448 free_dlink_node(node);
449 }
450
451 DLINK_FOREACH_SAFE(node, node_next, lt->hide_mask.head)
452 {
453 xfree(node->data);
454 dlinkDelete(node, <->hide_mask);
455 free_dlink_node(node);
456 }
457
458 xfree(lt);
459 client->connection->list_task = NULL;
460 }
461
462 /* list_allow_channel()
463 *
464 * inputs - channel name
465 * - pointer to a list task
466 * output - 1 if the channel is to be displayed
467 * 0 otherwise
468 * side effects -
469 */
470 static bool
list_allow_channel(const char * name,const struct ListTask * lt)471 list_allow_channel(const char *name, const struct ListTask *lt)
472 {
473 dlink_node *node;
474
475 DLINK_FOREACH(node, lt->show_mask.head)
476 if (match(node->data, name) != 0)
477 return false;
478
479 DLINK_FOREACH(node, lt->hide_mask.head)
480 if (match(node->data, name) == 0)
481 return false;
482
483 return true;
484 }
485
486 /* list_one_channel()
487 *
488 * inputs - client pointer to return result to
489 * - pointer to channel to list
490 * - pointer to ListTask structure
491 * output - none
492 * side effects -
493 */
494 static void
list_one_channel(struct Client * client,struct Channel * channel)495 list_one_channel(struct Client *client, struct Channel *channel)
496 {
497 const struct ListTask *const lt = client->connection->list_task;
498 const struct ChannelMember *member = NULL;
499 char listbuf[MODEBUFLEN] = "";
500 char modebuf[MODEBUFLEN] = "";
501 char parabuf[MODEBUFLEN] = "";
502
503 if (SecretChannel(channel) &&
504 !(HasUMode(client, UMODE_ADMIN) || (member = member_find_link(client, channel))))
505 return;
506
507 if (dlink_list_length(&channel->members) < lt->users_min ||
508 dlink_list_length(&channel->members) > lt->users_max ||
509 (channel->creation_time != 0 &&
510 ((unsigned int)channel->creation_time < lt->created_min ||
511 (unsigned int)channel->creation_time > lt->created_max)) ||
512 (unsigned int)channel->topic_time < lt->topicts_min ||
513 (channel->topic_time ? (unsigned int)channel->topic_time : UINT_MAX) >
514 lt->topicts_max)
515 return;
516
517 if (lt->topic[0] && match(lt->topic, channel->topic))
518 return;
519
520 if (list_allow_channel(channel->name, lt) == false)
521 return;
522
523 channel_modes(channel, client, member, modebuf, parabuf);
524
525 if (channel->topic[0])
526 snprintf(listbuf, sizeof(listbuf), "[%s] ", modebuf);
527 else
528 snprintf(listbuf, sizeof(listbuf), "[%s]", modebuf);
529
530 sendto_one_numeric(client, &me, RPL_LIST, channel->name,
531 dlink_list_length(&channel->members),
532 listbuf, channel->topic);
533 }
534
535 /* safe_list_channels()
536 *
537 * inputs - pointer to client requesting list
538 * output - 0/1
539 * side effects - safely list all channels to client
540 *
541 * Walk the channel buckets, ensure all pointers in a bucket are
542 * traversed before blocking on a sendq. This means, no locking is needed.
543 *
544 * - Dianora
545 */
546 void
safe_list_channels(struct Client * client,bool only_unmasked_channels)547 safe_list_channels(struct Client *client, bool only_unmasked_channels)
548 {
549 struct ListTask *const lt = client->connection->list_task;
550 struct Channel *channel;
551
552 if (only_unmasked_channels == false)
553 {
554 for (unsigned int i = lt->hash_index; i < HASHSIZE; ++i)
555 {
556 if (exceeding_sendq(client) == true)
557 {
558 lt->hash_index = i;
559 return; /* Still more to do */
560 }
561
562 for (channel = channelTable[i]; channel; channel = channel->hnextch)
563 list_one_channel(client, channel);
564 }
565 }
566 else
567 {
568 dlink_node *node;
569
570 DLINK_FOREACH(node, lt->show_mask.head)
571 if ((channel = hash_find_channel(node->data)))
572 list_one_channel(client, channel);
573 }
574
575 free_list_task(client);
576 sendto_one_numeric(client, &me, RPL_LISTEND);
577 }
578