unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

hash.c (21556B)

      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 "unrealircd.h"
     21 
     22 /* Next #define's, the siphash_raw() and siphash_nocase() functions are based
     23  * on the SipHash reference C implementation to which the following applies:
     24  * Copyright (c) 2012-2016 Jean-Philippe Aumasson
     25  *  <jeanphilippe.aumasson@gmail.com>
     26  * Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
     27  * Further enhancements were made by:
     28  * Copyright (c) 2017 Salvatore Sanfilippo <antirez@gmail.com>
     29  * To the extent possible under law, the author(s) have dedicated all copyright
     30  * and related and neighboring rights to this software to the public domain
     31  * worldwide. This software is distributed without any warranty.
     32  * You should have received a copy of the CC0 Public Domain Dedication along
     33  * with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
     34  *
     35  * In addition to above, Bram Matthys (Syzop), did some minor enhancements,
     36  * such as dropping the uint8_t stuff (in UnrealIRCd char is always unsigned)
     37  * and getting rid of the length argument.
     38  *
     39  * The end result are simple functions for API end-users and we encourage
     40  * everyone to use these two hash functions everywhere in UnrealIRCd.
     41  */
     42 
     43 #define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
     44 
     45 #define U32TO8_LE(p, v)                                                        \
     46     (p)[0] = (char)((v));                                                   \
     47     (p)[1] = (char)((v) >> 8);                                              \
     48     (p)[2] = (char)((v) >> 16);                                             \
     49     (p)[3] = (char)((v) >> 24);
     50 
     51 #define U64TO8_LE(p, v)                                                        \
     52     U32TO8_LE((p), (uint32_t)((v)));                                           \
     53     U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
     54 
     55 #define U8TO64_LE(p)                                                           \
     56     (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |                        \
     57      ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) |                 \
     58      ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) |                 \
     59      ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
     60 
     61 #define U8TO64_LE_NOCASE(p)                                                    \
     62     (((uint64_t)(tolower((p)[0]))) |                                           \
     63      ((uint64_t)(tolower((p)[1])) << 8) |                                      \
     64      ((uint64_t)(tolower((p)[2])) << 16) |                                     \
     65      ((uint64_t)(tolower((p)[3])) << 24) |                                     \
     66      ((uint64_t)(tolower((p)[4])) << 32) |                                              \
     67      ((uint64_t)(tolower((p)[5])) << 40) |                                              \
     68      ((uint64_t)(tolower((p)[6])) << 48) |                                              \
     69      ((uint64_t)(tolower((p)[7])) << 56))
     70 
     71 #define SIPROUND                                                               \
     72     do {                                                                       \
     73         v0 += v1;                                                              \
     74         v1 = ROTL(v1, 13);                                                     \
     75         v1 ^= v0;                                                              \
     76         v0 = ROTL(v0, 32);                                                     \
     77         v2 += v3;                                                              \
     78         v3 = ROTL(v3, 16);                                                     \
     79         v3 ^= v2;                                                              \
     80         v0 += v3;                                                              \
     81         v3 = ROTL(v3, 21);                                                     \
     82         v3 ^= v0;                                                              \
     83         v2 += v1;                                                              \
     84         v1 = ROTL(v1, 17);                                                     \
     85         v1 ^= v2;                                                              \
     86         v2 = ROTL(v2, 32);                                                     \
     87     } while (0)
     88 
     89 /** Generic hash function in UnrealIRCd - raw version.
     90  * Note that you probably want siphash() or siphash_nocase() instead.
     91  * @param in    The data to hash
     92  * @param inlen The length of the data
     93  * @param k     The key to use for hashing (SIPHASH_KEY_LENGTH bytes,
     94  *              which is actually 16, not NUL terminated)
     95  * @returns Hash result as a 64 bit unsigned integer.
     96  * @note  The key (k) should be random and must stay the same for
     97  *        as long as you use the function for your specific hash table.
     98  *        Simply use the following on boot: siphash_generate_key(k);
     99  *
    100  *        This siphash_raw() version is meant for non-strings,
    101  *        such as raw IP address structs and such.
    102  */
    103 uint64_t siphash_raw(const char *in, size_t inlen, const char *k)
    104 {
    105     uint64_t hash;
    106     char *out = (char*) &hash;
    107     uint64_t v0 = 0x736f6d6570736575ULL;
    108     uint64_t v1 = 0x646f72616e646f6dULL;
    109     uint64_t v2 = 0x6c7967656e657261ULL;
    110     uint64_t v3 = 0x7465646279746573ULL;
    111     uint64_t k0 = U8TO64_LE(k);
    112     uint64_t k1 = U8TO64_LE(k + 8);
    113     uint64_t m;
    114     const char *end = in + inlen - (inlen % sizeof(uint64_t));
    115     const int left = inlen & 7;
    116     uint64_t b = ((uint64_t)inlen) << 56;
    117     v3 ^= k1;
    118     v2 ^= k0;
    119     v1 ^= k1;
    120     v0 ^= k0;
    121 
    122     for (; in != end; in += 8) {
    123         m = U8TO64_LE(in);
    124         v3 ^= m;
    125 
    126         SIPROUND;
    127         SIPROUND;
    128 
    129         v0 ^= m;
    130     }
    131 
    132     switch (left) {
    133     case 7: b |= ((uint64_t)in[6]) << 48; /* fallthrough */
    134     case 6: b |= ((uint64_t)in[5]) << 40; /* fallthrough */
    135     case 5: b |= ((uint64_t)in[4]) << 32; /* fallthrough */
    136     case 4: b |= ((uint64_t)in[3]) << 24; /* fallthrough */
    137     case 3: b |= ((uint64_t)in[2]) << 16; /* fallthrough */
    138     case 2: b |= ((uint64_t)in[1]) << 8;  /* fallthrough */
    139     case 1: b |= ((uint64_t)in[0]); break;
    140     case 0: break;
    141     }
    142 
    143     v3 ^= b;
    144 
    145     SIPROUND;
    146     SIPROUND;
    147 
    148     v0 ^= b;
    149     v2 ^= 0xff;
    150 
    151     SIPROUND;
    152     SIPROUND;
    153     SIPROUND;
    154     SIPROUND;
    155 
    156     b = v0 ^ v1 ^ v2 ^ v3;
    157     U64TO8_LE(out, b);
    158 
    159     return hash;
    160 }
    161 
    162 /** Generic hash function in UnrealIRCd - case insensitive.
    163  * This deals with IRC case-insensitive matches, which is
    164  * what you need for things like nicks and channels.
    165  * @param str   The string to hash (NUL-terminated)
    166  * @param k     The key to use for hashing (SIPHASH_KEY_LENGTH bytes,
    167  *              which is actually 16, not NUL terminated)
    168  * @returns Hash result as a 64 bit unsigned integer.
    169  * @note  The key (k) should be random and must stay the same for
    170  *        as long as you use the function for your specific hash table.
    171  *        Simply use the following on boot: siphash_generate_key(k);
    172  */
    173 uint64_t siphash_nocase(const char *in, const char *k)
    174 {
    175     uint64_t hash;
    176     char *out = (char*) &hash;
    177     size_t inlen = strlen(in);
    178     uint64_t v0 = 0x736f6d6570736575ULL;
    179     uint64_t v1 = 0x646f72616e646f6dULL;
    180     uint64_t v2 = 0x6c7967656e657261ULL;
    181     uint64_t v3 = 0x7465646279746573ULL;
    182     uint64_t k0 = U8TO64_LE(k);
    183     uint64_t k1 = U8TO64_LE(k + 8);
    184     uint64_t m;
    185     const char *end = in + inlen - (inlen % sizeof(uint64_t));
    186     const int left = inlen & 7;
    187     uint64_t b = ((uint64_t)inlen) << 56;
    188     v3 ^= k1;
    189     v2 ^= k0;
    190     v1 ^= k1;
    191     v0 ^= k0;
    192 
    193     for (; in != end; in += 8) {
    194         m = U8TO64_LE_NOCASE(in);
    195         v3 ^= m;
    196 
    197         SIPROUND;
    198         SIPROUND;
    199 
    200         v0 ^= m;
    201     }
    202 
    203     switch (left) {
    204     case 7: b |= ((uint64_t)tolower(in[6])) << 48; /* fallthrough */
    205     case 6: b |= ((uint64_t)tolower(in[5])) << 40; /* fallthrough */
    206     case 5: b |= ((uint64_t)tolower(in[4])) << 32; /* fallthrough */
    207     case 4: b |= ((uint64_t)tolower(in[3])) << 24; /* fallthrough */
    208     case 3: b |= ((uint64_t)tolower(in[2])) << 16; /* fallthrough */
    209     case 2: b |= ((uint64_t)tolower(in[1])) << 8;  /* fallthrough */
    210     case 1: b |= ((uint64_t)tolower(in[0])); break;
    211     case 0: break;
    212     }
    213 
    214     v3 ^= b;
    215 
    216     SIPROUND;
    217     SIPROUND;
    218 
    219     v0 ^= b;
    220     v2 ^= 0xff;
    221 
    222     SIPROUND;
    223     SIPROUND;
    224     SIPROUND;
    225     SIPROUND;
    226 
    227     b = v0 ^ v1 ^ v2 ^ v3;
    228     U64TO8_LE(out, b);
    229 
    230     return hash;
    231 }
    232 
    233 /* End of imported code */
    234 
    235 /** Generic hash function in UnrealIRCd.
    236  * @param str   The string to hash (NUL-terminated)
    237  * @param k     The key to use for hashing (SIPHASH_KEY_LENGTH bytes,
    238  *              which is actually 16, not NUL terminated)
    239  * @returns Hash result as a 64 bit unsigned integer.
    240  * @note  The key (k) should be random and must stay the same for
    241  *        as long as you use the function for your specific hash table.
    242  *        Simply use the following on boot: siphash_generate_key(k);
    243  */
    244 uint64_t siphash(const char *in, const char *k)
    245 {
    246     size_t inlen = strlen(in);
    247 
    248     return siphash_raw(in, inlen, k);
    249 }
    250 /** Generate a key that is used by siphash() and siphash_nocase().
    251  * @param k   The key, this must be a char array of size 16.
    252  */
    253 void siphash_generate_key(char *k)
    254 {
    255 	int i;
    256 	for (i = 0; i < 16; i++)
    257 		k[i] = getrandom8();
    258 }
    259 
    260 static struct list_head clientTable[NICK_HASH_TABLE_SIZE];
    261 static struct list_head idTable[NICK_HASH_TABLE_SIZE];
    262 static Channel *channelTable[CHAN_HASH_TABLE_SIZE];
    263 
    264 static char siphashkey_nick[SIPHASH_KEY_LENGTH];
    265 static char siphashkey_chan[SIPHASH_KEY_LENGTH];
    266 static char siphashkey_whowas[SIPHASH_KEY_LENGTH];
    267 static char siphashkey_throttling[SIPHASH_KEY_LENGTH];
    268 
    269 extern char unreallogo[];
    270 
    271 /** Initialize all hash tables */
    272 void init_hash(void)
    273 {
    274 	int i;
    275 
    276 	siphash_generate_key(siphashkey_nick);
    277 	siphash_generate_key(siphashkey_chan);
    278 	siphash_generate_key(siphashkey_whowas);
    279 	siphash_generate_key(siphashkey_throttling);
    280 
    281 	for (i = 0; i < NICK_HASH_TABLE_SIZE; i++)
    282 		INIT_LIST_HEAD(&clientTable[i]);
    283 
    284 	for (i = 0; i < NICK_HASH_TABLE_SIZE; i++)
    285 		INIT_LIST_HEAD(&idTable[i]);
    286 
    287 	memset(channelTable, 0, sizeof(channelTable));
    288 
    289 	memset(ThrottlingHash, 0, sizeof(ThrottlingHash));
    290 	/* do not call init_throttling() here, as
    291 	 * config file has not been read yet.
    292 	 * The hash table is ready, anyway.
    293 	 */
    294 
    295 	if (strcmp(BASE_VERSION, &unreallogo[337]))
    296 		loop.tainted = 1;
    297 }
    298 
    299 uint64_t hash_client_name(const char *name)
    300 {
    301 	return siphash_nocase(name, siphashkey_nick) % NICK_HASH_TABLE_SIZE;
    302 }
    303 
    304 uint64_t hash_channel_name(const char *name)
    305 {
    306 	return siphash_nocase(name, siphashkey_chan) % CHAN_HASH_TABLE_SIZE;
    307 }
    308 
    309 uint64_t hash_whowas_name(const char *name)
    310 {
    311 	return siphash_nocase(name, siphashkey_whowas) % WHOWAS_HASH_TABLE_SIZE;
    312 }
    313 
    314 /*
    315  * add_to_client_hash_table
    316  */
    317 int add_to_client_hash_table(const char *name, Client *client)
    318 {
    319 	unsigned int hashv;
    320 	/*
    321 	 * If you see this, you have probably found your way to why changing the 
    322 	 * base version made the IRCd become weird. This has been the case in all
    323 	 * UnrealIRCd versions since 3.0. I'm sick of people ripping the IRCd off and 
    324 	 * just slapping on some random <theirnet> BASE_VERSION while not changing
    325 	 * a single bit of code. YOU DID NOT WRITE ALL OF THIS THEREFORE YOU DO NOT
    326 	 * DESERVE TO BE ABLE TO DO THAT. If you found this however, I'm OK with you 
    327 	 * removing the checks. However, keep in mind that the copyright headers must
    328 	 * stay in place, which means no wiping of /credits and /info. We haven't 
    329 	 * sat up late at night so some lamer could steal all our work without even
    330 	 * giving us credit. Remember to follow all regulations in LICENSE.
    331 	 * -Stskeeps
    332 	*/
    333 	if (loop.tainted)
    334 		return 0;
    335 	hashv = hash_client_name(name);
    336 	list_add(&client->client_hash, &clientTable[hashv]);
    337 	return 0;
    338 }
    339 
    340 /*
    341  * add_to_client_hash_table
    342  */
    343 int add_to_id_hash_table(const char *name, Client *client)
    344 {
    345 	unsigned int hashv;
    346 	hashv = hash_client_name(name);
    347 	list_add(&client->id_hash, &idTable[hashv]);
    348 	return 0;
    349 }
    350 
    351 /*
    352  * add_to_channel_hash_table
    353  */
    354 int add_to_channel_hash_table(const char *name, Channel *channel)
    355 {
    356 	unsigned int hashv;
    357 
    358 	hashv = hash_channel_name(name);
    359 	channel->hnextch = channelTable[hashv];
    360 	channelTable[hashv] = channel;
    361 	return 0;
    362 }
    363 /*
    364  * del_from_client_hash_table
    365  */
    366 int del_from_client_hash_table(const char *name, Client *client)
    367 {
    368 	if (!list_empty(&client->client_hash))
    369 		list_del(&client->client_hash);
    370 
    371 	INIT_LIST_HEAD(&client->client_hash);
    372 
    373 	return 0;
    374 }
    375 
    376 int del_from_id_hash_table(const char *name, Client *client)
    377 {
    378 	if (!list_empty(&client->id_hash))
    379 		list_del(&client->id_hash);
    380 
    381 	INIT_LIST_HEAD(&client->id_hash);
    382 
    383 	return 0;
    384 }
    385 
    386 /*
    387  * del_from_channel_hash_table
    388  */
    389 void del_from_channel_hash_table(const char *name, Channel *channel)
    390 {
    391 	Channel *tmp, *prev = NULL;
    392 	unsigned int hashv;
    393 
    394 	hashv = hash_channel_name(name);
    395 	for (tmp = channelTable[hashv]; tmp; tmp = tmp->hnextch)
    396 	{
    397 		if (tmp == channel)
    398 		{
    399 			if (prev)
    400 				prev->hnextch = tmp->hnextch;
    401 			else
    402 				channelTable[hashv] = tmp->hnextch;
    403 			tmp->hnextch = NULL;
    404 			return; /* DONE */
    405 		}
    406 		prev = tmp;
    407 	}
    408 	return; /* NOTFOUND */
    409 }
    410 
    411 /*
    412  * hash_find_client
    413  */
    414 Client *hash_find_client(const char *name, Client *client)
    415 {
    416 	Client *tmp;
    417 	unsigned int hashv;
    418 
    419 	hashv = hash_client_name(name);
    420 	list_for_each_entry(tmp, &clientTable[hashv], client_hash)
    421 	{
    422 		if (smycmp(name, tmp->name) == 0)
    423 			return tmp;
    424 	}
    425 
    426 	return client;
    427 }
    428 
    429 Client *hash_find_id(const char *name, Client *client)
    430 {
    431 	Client *tmp;
    432 	unsigned int hashv;
    433 
    434 	hashv = hash_client_name(name);
    435 	list_for_each_entry(tmp, &idTable[hashv], id_hash)
    436 	{
    437 		if (smycmp(name, tmp->id) == 0)
    438 			return tmp;
    439 	}
    440 
    441 	return client;
    442 }
    443 
    444 /*
    445  * hash_find_nickatserver
    446  */
    447 Client *hash_find_nickatserver(const char *str, Client *def)
    448 {
    449 	char *serv;
    450 	char nick[NICKLEN+HOSTLEN+1];
    451 	Client *client;
    452 	
    453 	strlcpy(nick, str, sizeof(nick)); /* let's work on a copy */
    454 
    455 	serv = strchr(nick, '@');
    456 	if (serv)
    457 		*serv++ = '\0';
    458 
    459 	client = find_user(nick, NULL);
    460 	if (!client)
    461 		return NULL; /* client not found */
    462 	
    463 	if (!serv)
    464 		return client; /* validated: was just 'nick' and not 'nick@serv' */
    465 
    466 	/* Now validate the server portion */
    467 	if (client->user && !smycmp(serv, client->user->server))
    468 		return client; /* validated */
    469 	
    470 	return def;
    471 }
    472 /*
    473  * hash_find_server
    474  */
    475 Client *hash_find_server(const char *server, Client *def)
    476 {
    477 	Client *tmp;
    478 	unsigned int hashv;
    479 
    480 	hashv = hash_client_name(server);
    481 	list_for_each_entry(tmp, &clientTable[hashv], client_hash)
    482 	{
    483 		if (!IsServer(tmp) && !IsMe(tmp))
    484 			continue;
    485 		if (smycmp(server, tmp->name) == 0)
    486 		{
    487 			return tmp;
    488 		}
    489 	}
    490 
    491 	return def;
    492 }
    493 
    494 /** Find a client, user (person), server or channel by name.
    495  * If you are looking for "other find functions", then the alphabetical index of functions
    496  * at 'f' is your best bet: https://www.unrealircd.org/api/5/globals_func_f.html#index_f
    497  * @defgroup FindFunctions Find functions
    498  * @{
    499  */
    500 
    501 /** Find a client by name.
    502  * This searches in the list of all types of clients, user/person, servers or an unregistered clients.
    503  * If you know what type of client to search for, then use find_server() or find_user() instead!
    504  * @param name        The name to search for (eg: "nick" or "irc.example.net")
    505  * @param requester   The client that is searching for this name
    506  * @note  If 'requester' is a server or NULL, then we also check
    507  *        the ID table, otherwise not.
    508  * @returns If the client is found then the Client is returned, otherwise NULL.
    509  */
    510 Client *find_client(const char *name, Client *requester)
    511 {
    512 	if (requester == NULL || IsServer(requester))
    513 	{
    514 		Client *client;
    515 
    516 		if ((client = hash_find_id(name, NULL)) != NULL)
    517 			return client;
    518 	}
    519 
    520 	return hash_find_client(name, NULL);
    521 }
    522 
    523 /** Find a server by name.
    524  * @param name        The server name to search for (eg: 'irc.example.net'
    525  *                    or '001')
    526  * @param requester   The client searching for the name.
    527  * @note  If 'requester' is a server or NULL, then we also check
    528  *        the ID table, otherwise not.
    529  * @returns If the server is found then the Client is returned, otherwise NULL.
    530  */
    531 Client *find_server(const char *name, Client *requester)
    532 {
    533 	if (name)
    534 	{
    535 		Client *client;
    536 
    537 		if ((client = find_client(name, NULL)) != NULL && (IsServer(client) || IsMe(client)))
    538 			return client;
    539 	}
    540 
    541 	return NULL;
    542 }
    543 
    544 /** Find a user (a person)
    545  * @param name        The name to search for (eg: "nick" or "001ABCDEFG")
    546  * @param requester   The client that is searching for this name
    547  * @note  If 'requester' is a server or NULL, then we also check
    548  *        the ID table, otherwise not.
    549  * @returns If the user is found then the Client is returned, otherwise NULL.
    550  */
    551 Client *find_user(const char *name, Client *requester)
    552 {
    553 	Client *c2ptr;
    554 
    555 	c2ptr = find_client(name, requester);
    556 
    557 	if (c2ptr && IsUser(c2ptr) && c2ptr->user)
    558 		return c2ptr;
    559 
    560 	return NULL;
    561 }
    562 
    563 
    564 /** Find a channel by name.
    565  * @param name			The channel name to search for
    566  * @returns If the channel exists then the Channel is returned, otherwise NULL.
    567  */
    568 Channel *find_channel(const char *name)
    569 {
    570 	unsigned int hashv;
    571 	Channel *channel;
    572 
    573 	hashv = hash_channel_name(name);
    574 
    575 	for (channel = channelTable[hashv]; channel; channel = channel->hnextch)
    576 		if (smycmp(name, channel->name) == 0)
    577 			return channel;
    578 
    579 	return NULL;
    580 }
    581 
    582 /** @} */
    583 
    584 Channel *hash_get_chan_bucket(uint64_t hashv)
    585 {
    586 	if (hashv > CHAN_HASH_TABLE_SIZE)
    587 		return NULL;
    588 	return channelTable[hashv];
    589 }
    590 
    591 /* Throttling - originally by Stskeeps */
    592 
    593 /* Note that we call this set::anti-flood::connect-flood nowadays */
    594 
    595 struct MODVAR ThrottlingBucket *ThrottlingHash[THROTTLING_HASH_TABLE_SIZE];
    596 
    597 void update_throttling_timer_settings(void)
    598 {
    599 	long v;
    600 	EventInfo eInfo;
    601 
    602 	if (!THROTTLING_PERIOD)
    603 	{
    604 		v = 120*1000;
    605 	} else
    606 	{
    607 		v = (THROTTLING_PERIOD*1000)/2;
    608 		if (v > 5000)
    609 			v = 5000; /* run at least every 5s */
    610 		if (v < 1000)
    611 			v = 1000; /* run at max once every 1s */
    612 	}
    613 
    614 	memset(&eInfo, 0, sizeof(eInfo));
    615 	eInfo.flags = EMOD_EVERY;
    616 	eInfo.every_msec = v;
    617 	EventMod(EventFind("throttling_check_expire"), &eInfo);
    618 }
    619 
    620 uint64_t hash_throttling(const char *ip)
    621 {
    622 	return siphash(ip, siphashkey_throttling) % THROTTLING_HASH_TABLE_SIZE;
    623 }
    624 
    625 struct ThrottlingBucket *find_throttling_bucket(Client *client)
    626 {
    627 	int hash = 0;
    628 	struct ThrottlingBucket *p;
    629 	hash = hash_throttling(client->ip);
    630 	
    631 	for (p = ThrottlingHash[hash]; p; p = p->next)
    632 	{
    633 		if (!strcmp(p->ip, client->ip))
    634 			return p;
    635 	}
    636 	
    637 	return NULL;
    638 }
    639 
    640 EVENT(throttling_check_expire)
    641 {
    642 	struct ThrottlingBucket *n, *n_next;
    643 	int	i;
    644 	static time_t t = 0;
    645 		
    646 	for (i = 0; i < THROTTLING_HASH_TABLE_SIZE; i++)
    647 	{
    648 		for (n = ThrottlingHash[i]; n; n = n_next)
    649 		{
    650 			n_next = n->next;
    651 			if ((TStime() - n->since) > (THROTTLING_PERIOD ? THROTTLING_PERIOD : 15))
    652 			{
    653 				DelListItem(n, ThrottlingHash[i]);
    654 				safe_free(n->ip);
    655 				safe_free(n);
    656 			}
    657 		}
    658 	}
    659 
    660 	if (!t || (TStime() - t > 30))
    661 	{
    662 		extern Module *Modules;
    663 		char *p = serveropts + strlen(serveropts);
    664 		Module *mi;
    665 		t = TStime();
    666 		if (!Hooks[HOOKTYPE_USERMSG] && strchr(serveropts, 'm'))
    667 		{ p = strchr(serveropts, 'm'); *p = '\0'; }
    668 		if (!Hooks[HOOKTYPE_CHANMSG] && strchr(serveropts, 'M'))
    669 		{ p = strchr(serveropts, 'M'); *p = '\0'; }
    670 		if (Hooks[HOOKTYPE_USERMSG] && !strchr(serveropts, 'm'))
    671 			*p++ = 'm';
    672 		if (Hooks[HOOKTYPE_CHANMSG] && !strchr(serveropts, 'M'))
    673 			*p++ = 'M';
    674 		*p = '\0';
    675 		for (mi = Modules; mi; mi = mi->next)
    676 			if (!(mi->options & MOD_OPT_OFFICIAL))
    677 				tainted = 99;
    678 	}
    679 
    680 	return;
    681 }
    682 
    683 void add_throttling_bucket(Client *client)
    684 {
    685 	int hash;
    686 	struct ThrottlingBucket *n;
    687 
    688 	n = safe_alloc(sizeof(struct ThrottlingBucket));	
    689 	n->next = n->prev = NULL; 
    690 	safe_strdup(n->ip, client->ip);
    691 	n->since = TStime();
    692 	n->count = 1;
    693 	hash = hash_throttling(client->ip);
    694 	AddListItem(n, ThrottlingHash[hash]);
    695 	return;
    696 }
    697 
    698 /** Checks whether the user is connect-flooding.
    699  * @retval 0 Denied, throttled.
    700  * @retval 1 Allowed, but known in the list.
    701  * @retval 2 Allowed, not in list or is an exception.
    702  * @see add_connection()
    703  */
    704 int throttle_can_connect(Client *client)
    705 {
    706 	struct ThrottlingBucket *b;
    707 
    708 	if (!THROTTLING_PERIOD || !THROTTLING_COUNT)
    709 		return 2;
    710 
    711 	if (!(b = find_throttling_bucket(client)))
    712 		return 1;
    713 	else
    714 	{
    715 		if (find_tkl_exception(TKL_CONNECT_FLOOD, client))
    716 			return 2;
    717 		if (b->count+1 > (THROTTLING_COUNT ? THROTTLING_COUNT : 3))
    718 			return 0;
    719 		b->count++;
    720 		return 2;
    721 	}
    722 }
    723 
    724 /** Find a server by the SID-part of a UID.
    725  * Eg you pass "001ABCDEFG" and it would look up server "001".
    726  *
    727  * @param uid	The UID, eg 001ABCDEFG
    728  * @returns Server where the UID would be hosted on, or NULL
    729  * if no such server is linked.
    730  */
    731 Client *find_server_by_uid(const char *uid)
    732 {
    733 	char sid[SIDLEN+1];
    734 
    735 	if (!isdigit(*uid))
    736 		return NULL; /* not a UID/SID */
    737 
    738 	strlcpy(sid, uid, sizeof(sid));
    739 	return hash_find_id(sid, NULL);
    740 }