]> jfr.im git - irc/rqf/shadowircd.git/blobdiff - src/hash.c
Disable autoconnect for a server with excessive TS delta.
[irc/rqf/shadowircd.git] / src / hash.c
index bdd90dbc0e4e1d1011b87901e4b82b13532e74b2..10e35066d83ef9088e2a4644a43e796aa9223235 100644 (file)
-/*\r
- *  ircd-ratbox: A slightly useful ircd.\r
- *  hash.c: Maintains hashtables.\r
- *\r
- *  Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center\r
- *  Copyright (C) 1996-2002 Hybrid Development Team\r
- *  Copyright (C) 2002-2005 ircd-ratbox development team\r
- *\r
- *  This program is free software; you can redistribute it and/or modify\r
- *  it under the terms of the GNU General Public License as published by\r
- *  the Free Software Foundation; either version 2 of the License, or\r
- *  (at your option) any later version.\r
- *\r
- *  This program is distributed in the hope that it will be useful,\r
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *  GNU General Public License for more details.\r
- *\r
- *  You should have received a copy of the GNU General Public License\r
- *  along with this program; if not, write to the Free Software\r
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301\r
- *  USA\r
- *\r
- *  $Id: hash.c 25119 2008-03-13 16:57:05Z androsyn $\r
- */\r
-\r
-#include "stdinc.h"\r
-#include "s_conf.h"\r
-#include "channel.h"\r
-#include "client.h"\r
-#include "hash.h"\r
-#include "ircd.h"\r
-#include "numeric.h"\r
-#include "send.h"\r
-#include "cache.h"\r
-#include "s_newconf.h"\r
-\r
-#define hash_nick(x) (fnv_hash_upper((const unsigned char *)(x), U_MAX_BITS, 0))\r
-#define hash_id(x) (fnv_hash((const unsigned char *)(x), U_MAX_BITS, 0))\r
-#define hash_channel(x) (fnv_hash_upper_len((const unsigned char *)(x), CH_MAX_BITS, 30))\r
-#define hash_hostname(x) (fnv_hash_upper_len((const unsigned char *)(x), HOST_MAX_BITS, 30))\r
-#define hash_resv(x) (fnv_hash_upper_len((const unsigned char *)(x), R_MAX_BITS, 30))\r
-#define hash_cli_fd(x) (x % CLI_FD_MAX)\r
-\r
-\r
-static rb_dlink_list clientbyfdTable[U_MAX];\r
-static rb_dlink_list clientTable[U_MAX];\r
-static rb_dlink_list channelTable[CH_MAX];\r
-static rb_dlink_list idTable[U_MAX];\r
-rb_dlink_list resvTable[R_MAX];\r
-static rb_dlink_list hostTable[HOST_MAX];\r
-static rb_dlink_list helpTable[HELP_MAX];\r
-rb_dlink_list ndTable[U_MAX];\r
-\r
-/*\r
- * Hashing.\r
- *\r
- *   The server uses a chained hash table to provide quick and efficient\r
- * hash table maintenance (providing the hash function works evenly over\r
- * the input range).  The hash table is thus not susceptible to problems\r
- * of filling all the buckets or the need to rehash.\r
- *    It is expected that the hash table would look something like this\r
- * during use:\r
- *                   +-----+    +-----+    +-----+   +-----+\r
- *                ---| 224 |----| 225 |----| 226 |---| 227 |---\r
- *                   +-----+    +-----+    +-----+   +-----+\r
- *                      |          |          |\r
- *                   +-----+    +-----+    +-----+\r
- *                   |  A  |    |  C  |    |  D  |\r
- *                   +-----+    +-----+    +-----+\r
- *                      |\r
- *                   +-----+\r
- *                   |  B  |\r
- *                   +-----+\r
- *\r
- * A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU\r
- *\r
- * The order shown above is just one instant of the server. \r
- *\r
- *\r
- * The hash functions currently used are based Fowler/Noll/Vo hashes\r
- * which work amazingly well and have a extremely low collision rate\r
- * For more info see http://www.isthe.com/chongo/tech/comp/fnv/index.html\r
- *\r
- * \r
- */\r
-\r
-/* init_hash()\r
- *\r
- * clears the various hashtables\r
- */\r
-void\r
-init_hash(void)\r
-{\r
-       /* nothing to do here */\r
-}\r
-\r
-\r
-uint32_t\r
-fnv_hash_upper(const unsigned char *s, unsigned int bits, unsigned int unused)\r
-{\r
-       uint32_t h = FNV1_32_INIT;\r
-       bits = 32 - bits;\r
-       while (*s)\r
-       {\r
-               h ^= ToUpper(*s++);\r
-               h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);\r
-       }\r
-       h = (h >> bits) ^ (h & ((2^bits)-1));\r
-       return h;\r
-}\r
-\r
-uint32_t\r
-fnv_hash(const unsigned char *s, unsigned int bits, unsigned int unused)\r
-{\r
-       uint32_t h = FNV1_32_INIT;\r
-       bits = 32 - bits;\r
-       while (*s)\r
-       {\r
-               h ^= *s++;\r
-               h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);\r
-       }\r
-       h = (h >> bits) ^ (h & ((2^bits)-1));\r
-       return h;\r
-}\r
-\r
-uint32_t\r
-fnv_hash_len(const unsigned char *s, unsigned int bits, unsigned int len)\r
-{\r
-       uint32_t h = FNV1_32_INIT;\r
-       bits = 32 - bits;\r
-       const unsigned char *x = s + len;\r
-       while (*s && s < x)\r
-       {\r
-               h ^= *s++;\r
-               h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);\r
-       }\r
-       h = (h >> bits) ^ (h & ((2^bits)-1));\r
-       return h;\r
-}\r
-\r
-uint32_t\r
-fnv_hash_upper_len(const unsigned char *s, unsigned int bits, unsigned int len)\r
-{\r
-       uint32_t h = FNV1_32_INIT;\r
-       bits = 32 - bits;\r
-       const unsigned char *x = s + len;\r
-       while (*s && s < x)\r
-       {\r
-               h ^= ToUpper(*s++);\r
-               h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);\r
-       }\r
-       h = (h >> bits) ^ (h & ((2^bits)-1));\r
-       return h;\r
-}\r
-\r
-static unsigned int\r
-hash_help(const char *name)\r
-{\r
-       unsigned int h = 0;\r
-\r
-       while(*name)\r
-       {\r
-               h += (unsigned int) (ToLower(*name++) & 0xDF);\r
-       }\r
-\r
-       return (h % HELP_MAX);\r
-}\r
-\r
-static struct _hash_function\r
-{\r
-       uint32_t (*func) (unsigned const char *, unsigned int, unsigned int);\r
-       rb_dlink_list *table;\r
-       unsigned int hashbits;\r
-       unsigned int hashlen;\r
-} hash_function[] = {\r
-       { fnv_hash_upper,       clientTable,    U_MAX_BITS,     0       },\r
-       { fnv_hash,             idTable,        U_MAX_BITS,     0       },\r
-       { fnv_hash_upper_len,   channelTable,   CH_MAX_BITS,    30      },\r
-       { fnv_hash_upper_len,   hostTable,      HOST_MAX_BITS,  30      },\r
-       { fnv_hash_upper_len,   resvTable,      R_MAX_BITS,     30      }\r
-};\r
-\r
-void\r
-add_to_hash(hash_type type, const char *hashindex, void *pointer)\r
-{\r
-       rb_dlink_list *table = hash_function[type].table;\r
-       unsigned int hashv;\r
-\r
-       if(EmptyString(hashindex) || (pointer == NULL))\r
-               return;\r
-\r
-       hashv = (hash_function[type].func)((const unsigned char *) hashindex, \r
-                                       hash_function[type].hashbits, \r
-                                       hash_function[type].hashlen);\r
-//     rb_dlinkAddAlloc(pointer, &hash_function[type].table[hashv]);\r
-       rb_dlinkAddAlloc(pointer, &table[hashv]);\r
-}\r
-\r
-void\r
-del_from_hash(hash_type type, const char *hashindex, void *pointer)\r
-{\r
-       rb_dlink_list *table = hash_function[type].table;\r
-       unsigned int hashv;\r
-\r
-       if(EmptyString(hashindex) || (pointer == NULL))\r
-               return;\r
-\r
-       hashv = (hash_function[type].func)((const unsigned char *) hashindex,\r
-                                       hash_function[type].hashbits,\r
-                                       hash_function[type].hashlen);\r
-       rb_dlinkFindDestroy(pointer, &table[hashv]);\r
-}\r
-\r
-void\r
-add_to_help_hash(const char *name, struct cachefile *hptr)\r
-{\r
-       unsigned int hashv;\r
-\r
-       if(EmptyString(name) || hptr == NULL)\r
-               return;\r
-\r
-       hashv = hash_help(name);\r
-       rb_dlinkAddAlloc(hptr, &helpTable[hashv]);\r
-}\r
-\r
-void\r
-add_to_nd_hash(const char *name, struct nd_entry *nd)\r
-{\r
-       nd->hashv = hash_nick(name);\r
-       rb_dlinkAdd(nd, &nd->hnode, &ndTable[nd->hashv]);\r
-}\r
-\r
-void\r
-clear_help_hash(void)\r
-{\r
-       rb_dlink_node *ptr;\r
-       rb_dlink_node *next_ptr;\r
-       int i;\r
-\r
-       HASH_WALK_SAFE(i, HELP_MAX, ptr, next_ptr, helpTable)\r
-       {\r
-               free_cachefile(ptr->data);\r
-               rb_dlinkDestroy(ptr, &helpTable[i]);\r
-       }\r
-       HASH_WALK_END\r
-}\r
-\r
-\r
-\r
-\r
-/* find_id()\r
- *\r
- * finds a client entry from the id hash table\r
- */\r
-struct Client *\r
-find_id(const char *name)\r
-{\r
-       struct Client *target_p;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-\r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       hashv = hash_id(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, idTable[hashv].head)\r
-       {\r
-               target_p = ptr->data;\r
-\r
-               if(strcmp(name, target_p->id) == 0)\r
-                       return target_p;\r
-       }\r
-\r
-       return NULL;\r
-}\r
-\r
-/* hash_find_masked_server()\r
- * \r
- * Whats happening in this next loop ? Well, it takes a name like\r
- * foo.bar.edu and proceeds to earch for *.edu and then *.bar.edu.\r
- * This is for checking full server names against masks although\r
- * it isnt often done this way in lieu of using matches().\r
- *\r
- * Rewrote to do *.bar.edu first, which is the most likely case,\r
- * also made const correct\r
- * --Bleep\r
- */\r
-static struct Client *\r
-hash_find_masked_server(struct Client *source_p, const char *name)\r
-{\r
-       char buf[HOSTLEN + 1];\r
-       char *p = buf;\r
-       char *s;\r
-       struct Client *server;\r
-\r
-       if('*' == *name || '.' == *name)\r
-               return NULL;\r
-\r
-       /* copy it across to give us a buffer to work on */\r
-       rb_strlcpy(buf, name, sizeof(buf));\r
-\r
-       while ((s = strchr(p, '.')) != 0)\r
-       {\r
-               *--s = '*';\r
-               /*\r
-                * Dont need to check IsServer() here since nicknames cant\r
-                * have *'s in them anyway.\r
-                */\r
-               if((server = find_server(source_p, s)))\r
-                       return server;\r
-               p = s + 2;\r
-       }\r
-\r
-       return NULL;\r
-}\r
-\r
-/* find_any_client()\r
- *\r
- * finds a client/server/masked server entry from the hash\r
- */\r
-struct Client *\r
-find_any_client(const char *name)\r
-{\r
-       struct Client *target_p;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-\r
-       s_assert(name != NULL);\r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       /* hunting for an id, not a nick */\r
-       if(IsDigit(*name))\r
-               return (find_id(name));\r
-\r
-       hashv = hash_nick(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, clientTable[hashv].head)\r
-       {\r
-               target_p = ptr->data;\r
-\r
-               if(irccmp(name, target_p->name) == 0)\r
-                       return target_p;\r
-       }\r
-\r
-       /* wasnt found, look for a masked server */\r
-       return hash_find_masked_server(NULL, name);\r
-}\r
-\r
-/* find_client()\r
- *\r
- * finds a client/server entry from the client hash table\r
- */\r
-struct Client *\r
-find_client(const char *name)\r
-{\r
-       struct Client *target_p;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-\r
-       s_assert(name != NULL);\r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       /* hunting for an id, not a nick */\r
-       if(IsDigit(*name))\r
-               return (find_id(name));\r
-\r
-       hashv = hash_nick(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, clientTable[hashv].head)\r
-       {\r
-               target_p = ptr->data;\r
-\r
-               if(irccmp(name, target_p->name) == 0)\r
-                       return target_p;\r
-       }\r
-\r
-       return NULL;\r
-}\r
-\r
-/* find_named_client()\r
- *\r
- * finds a client/server entry from the client hash table\r
- */\r
-struct Client *\r
-find_named_client(const char *name)\r
-{\r
-       struct Client *target_p;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-\r
-       s_assert(name != NULL);\r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       hashv = hash_nick(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, clientTable[hashv].head)\r
-       {\r
-               target_p = ptr->data;\r
-\r
-               if(irccmp(name, target_p->name) == 0)\r
-                       return target_p;\r
-       }\r
-\r
-       return NULL;\r
-}\r
-\r
-/* find_server()\r
- *\r
- * finds a server from the client hash table\r
- */\r
-struct Client *\r
-find_server(struct Client *source_p, const char *name)\r
-{\r
-       struct Client *target_p;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-  \r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       if((source_p == NULL || !MyClient(source_p)) && \r
-          IsDigit(*name) && strlen(name) == 3)\r
-       {\r
-               target_p = find_id(name);\r
-               return(target_p);\r
-       }\r
-\r
-       hashv = hash_nick(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, clientTable[hashv].head)\r
-       {\r
-               target_p = ptr->data;\r
-\r
-               if((IsServer(target_p) || IsMe(target_p)) &&\r
-                  irccmp(name, target_p->name) == 0)\r
-                               return target_p;\r
-       }\r
-\r
-       /* wasnt found, look for a masked server */\r
-       return hash_find_masked_server(source_p, name);\r
-}\r
-\r
-/* find_hostname()\r
- *\r
- * finds a hostname dlink list from the hostname hash table.\r
- * we return the full dlink list, because you can have multiple\r
- * entries with the same hostname\r
- */\r
-rb_dlink_node *\r
-find_hostname(const char *hostname)\r
-{\r
-       unsigned int hashv;\r
-\r
-       if(EmptyString(hostname))\r
-               return NULL;\r
-\r
-       hashv = hash_hostname(hostname);\r
-\r
-       return hostTable[hashv].head;\r
-}\r
-\r
-/* find_channel()\r
- *\r
- * finds a channel from the channel hash table\r
- */\r
-struct Channel *\r
-find_channel(const char *name)\r
-{\r
-       struct Channel *chptr;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-\r
-       s_assert(name != NULL);\r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       hashv = hash_channel(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, channelTable[hashv].head)\r
-       {\r
-               chptr = ptr->data;\r
-\r
-               if(irccmp(name, chptr->chname) == 0)\r
-                       return chptr;\r
-       }\r
-\r
-       return NULL;\r
-}\r
-\r
-/*\r
- * get_or_create_channel\r
- * inputs       - client pointer\r
- *              - channel name\r
- *              - pointer to int flag whether channel was newly created or not\r
- * output       - returns channel block or NULL if illegal name\r
- *             - also modifies *isnew\r
- *\r
- *  Get Channel block for chname (and allocate a new channel\r
- *  block, if it didn't exist before).\r
- */\r
-struct Channel *\r
-get_or_create_channel(struct Client *client_p, const char *chname, int *isnew)\r
-{\r
-       struct Channel *chptr;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-       int len;\r
-       const char *s = chname;\r
-\r
-       if(EmptyString(s))\r
-               return NULL;\r
-\r
-       len = strlen(s);\r
-       if(len > CHANNELLEN)\r
-       {\r
-               if(IsServer(client_p))\r
-               {\r
-                       sendto_realops_flags(UMODE_DEBUG, L_ALL,\r
-                                            "*** Long channel name from %s (%d > %d): %s",\r
-                                            client_p->name, len, CHANNELLEN, s);\r
-               }\r
-               len = CHANNELLEN;\r
-               s = LOCAL_COPY_N(s, CHANNELLEN);\r
-       }\r
-\r
-       hashv = hash_channel(s);\r
-\r
-       RB_DLINK_FOREACH(ptr, channelTable[hashv].head)\r
-       {\r
-               chptr = ptr->data;\r
-\r
-               if(irccmp(s, chptr->chname) == 0)\r
-               {\r
-                       if(isnew != NULL)\r
-                               *isnew = 0;\r
-                       return chptr;\r
-               }\r
-       }\r
-\r
-       if(isnew != NULL)\r
-               *isnew = 1;\r
-\r
-       chptr = allocate_channel(s);\r
-\r
-       rb_dlinkAdd(chptr, &chptr->node, &global_channel_list);\r
-\r
-       chptr->channelts = rb_current_time();   /* doesn't hurt to set it here */\r
-\r
-       rb_dlinkAddAlloc(chptr, &channelTable[hashv]);\r
-\r
-       return chptr;\r
-}\r
-\r
-/* hash_find_resv()\r
- *\r
- * hunts for a resv entry in the resv hash table\r
- */\r
-struct ConfItem *\r
-hash_find_resv(const char *name)\r
-{\r
-       struct ConfItem *aconf;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-\r
-       s_assert(name != NULL);\r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       hashv = hash_resv(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, resvTable[hashv].head)\r
-       {\r
-               aconf = ptr->data;\r
-\r
-               if(!irccmp(name, aconf->host))\r
-               {\r
-                       aconf->port++;\r
-                       return aconf;\r
-               }\r
-       }\r
-\r
-       return NULL;\r
-}\r
-\r
-struct cachefile *\r
-hash_find_help(const char *name, int flags)\r
-{\r
-       struct cachefile *hptr;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-\r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       hashv = hash_help(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, helpTable[hashv].head)\r
-       {\r
-               hptr = ptr->data;\r
-\r
-               if((irccmp(name, hptr->name) == 0) &&\r
-                  (hptr->flags & flags))\r
-                       return hptr;\r
-       }\r
-\r
-       return NULL;\r
-}\r
-\r
-void\r
-clear_resv_hash(void)\r
-{\r
-       struct ConfItem *aconf;\r
-       rb_dlink_node *ptr;\r
-       rb_dlink_node *next_ptr;\r
-       int i;\r
-\r
-       HASH_WALK_SAFE(i, R_MAX, ptr, next_ptr, resvTable)\r
-       {\r
-               aconf = ptr->data;\r
-\r
-               /* skip temp resvs */\r
-               if(aconf->flags & CONF_FLAGS_TEMPORARY)\r
-                       continue;\r
-\r
-               free_conf(ptr->data);\r
-               rb_dlinkDestroy(ptr, &resvTable[i]);\r
-       }\r
-       HASH_WALK_END\r
-}\r
-\r
-struct nd_entry *\r
-hash_find_nd(const char *name)\r
-{\r
-       struct nd_entry *nd;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-\r
-       if(EmptyString(name))\r
-               return NULL;\r
-\r
-       hashv = hash_nick(name);\r
-\r
-       RB_DLINK_FOREACH(ptr, ndTable[hashv].head)\r
-       {\r
-               nd = ptr->data;\r
-\r
-               if(!irccmp(name, nd->name))\r
-                       return nd;\r
-       }\r
-\r
-       return NULL;\r
-}\r
-\r
-void\r
-add_to_cli_fd_hash(struct Client *client_p)\r
-{\r
-       rb_dlinkAddAlloc(client_p, &clientbyfdTable[hash_cli_fd(rb_get_fd(client_p->localClient->F))]);\r
-}\r
-\r
-\r
-void\r
-del_from_cli_fd_hash(struct Client *client_p)\r
-{\r
-       unsigned int hashv;\r
-       hashv = hash_cli_fd(rb_get_fd(client_p->localClient->F));\r
-       rb_dlinkFindDestroy(client_p, &clientbyfdTable[hashv]);\r
-}\r
-\r
-struct Client *\r
-find_cli_fd_hash(int fd)\r
-{\r
-       struct Client *target_p;\r
-       rb_dlink_node *ptr;\r
-       unsigned int hashv;\r
-       hashv = hash_cli_fd(fd);\r
-       RB_DLINK_FOREACH(ptr, clientbyfdTable[hashv].head)\r
-       {\r
-               target_p = ptr->data;\r
-               if(rb_get_fd(target_p->localClient->F) == fd)\r
-                       return target_p;\r
-       }\r
-       return  NULL;   \r
-}\r
-\r
-static void\r
-output_hash(struct Client *source_p, const char *name, int length, int *counts, int deepest)\r
-{\r
-       unsigned long total = 0;\r
-       char buf[128];\r
-       int i;\r
-\r
-       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :%s Hash Statistics", name);\r
-\r
-       /* rb_snprintf which sendto_one_* uses doesn't support float formats */\r
-#ifdef HAVE_SNPRINTF\r
-       snprintf(buf, sizeof(buf), \r
-#else\r
-       sprintf(buf, \r
-#endif\r
-               "%.3f%%", (float) ((counts[0]*100) / (float) length));\r
-       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :Size: %d Empty: %d (%s)",\r
-                       length, counts[0], buf);\r
-\r
-       for(i = 1; i < 11; i++)\r
-       {\r
-               total += (counts[i] * i);\r
-       }\r
-\r
-       /* dont want to divide by 0! --fl */\r
-       if(counts[0] != length) \r
-       {\r
-#ifdef HAVE_SNPRINTF\r
-               snprintf(buf, sizeof(buf),\r
-#else\r
-               sprintf(buf,\r
-#endif\r
-                       "%.3f%%/%.3f%%", (float) (total / (length - counts[0])), \r
-                       (float) (total / length));\r
-               sendto_one_numeric(source_p, RPL_STATSDEBUG,\r
-                               "B :Average depth: %s Highest depth: %d",\r
-                               buf, deepest);\r
-       }\r
-       for(i = 0; i < 11; i++)\r
-       {\r
-               sendto_one_numeric(source_p, RPL_STATSDEBUG,\r
-                               "B :Nodes with %d entries: %d",\r
-                               i, counts[i]);\r
-       }\r
-}\r
-       \r
-\r
-static void\r
-count_hash(struct Client *source_p, rb_dlink_list *table, int length, const char *name)\r
-{\r
-       int counts[11];\r
-       unsigned long deepest = 0;\r
-       int i;\r
-\r
-       memset(counts, 0, sizeof(counts));\r
-       \r
-       for(i = 0; i < length; i++)\r
-       {\r
-               if(rb_dlink_list_length(&table[i]) >= 10)\r
-                       counts[10]++;\r
-               else\r
-                       counts[rb_dlink_list_length(&table[i])]++;\r
-\r
-               if(rb_dlink_list_length(&table[i]) > deepest)\r
-                       deepest = rb_dlink_list_length(&table[i]);\r
-       }\r
-\r
-       output_hash(source_p, name, length, counts, deepest);\r
-}\r
-\r
-void\r
-hash_stats(struct Client *source_p)\r
-{\r
-       count_hash(source_p, channelTable, CH_MAX, "Channel");\r
-       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");\r
-       count_hash(source_p, clientTable, U_MAX, "Client");\r
-       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");\r
-       count_hash(source_p, idTable, U_MAX, "ID");\r
-       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");\r
-       count_hash(source_p, hostTable, HOST_MAX, "Hostname");\r
-       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");\r
-       count_hash(source_p, clientbyfdTable, CLI_FD_MAX, "Client by FD");\r
-}      \r
+/*
+ *  ircd-ratbox: A slightly useful ircd.
+ *  hash.c: Maintains hashtables.
+ *
+ *  Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
+ *  Copyright (C) 1996-2002 Hybrid Development Team
+ *  Copyright (C) 2002-2005 ircd-ratbox development team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+ *  USA
+ *
+ *  $Id: hash.c 3177 2007-02-01 00:19:14Z jilles $
+ */
+
+#include "stdinc.h"
+#include "ircd_defs.h"
+#include "s_conf.h"
+#include "channel.h"
+#include "client.h"
+#include "common.h"
+#include "hash.h"
+#include "match.h"
+#include "ircd.h"
+#include "numeric.h"
+#include "send.h"
+#include "msg.h"
+#include "cache.h"
+#include "s_newconf.h"
+
+#define hash_cli_fd(x) (x % CLI_FD_MAX)
+
+static rb_dlink_list clientbyfdTable[U_MAX];
+
+rb_dlink_list *clientTable;
+rb_dlink_list *channelTable;
+rb_dlink_list *idTable;
+rb_dlink_list *resvTable;
+rb_dlink_list *hostTable; 
+
+/*
+ * look in whowas.c for the missing ...[WW_MAX]; entry
+ */
+
+/*
+ * Hashing.
+ *
+ *   The server uses a chained hash table to provide quick and efficient
+ * hash table maintenance (providing the hash function works evenly over
+ * the input range).  The hash table is thus not susceptible to problems
+ * of filling all the buckets or the need to rehash.
+ *    It is expected that the hash table would look something like this
+ * during use:
+ *                   +-----+    +-----+    +-----+   +-----+
+ *                ---| 224 |----| 225 |----| 226 |---| 227 |---
+ *                   +-----+    +-----+    +-----+   +-----+
+ *                      |          |          |
+ *                   +-----+    +-----+    +-----+
+ *                   |  A  |    |  C  |    |  D  |
+ *                   +-----+    +-----+    +-----+
+ *                      |
+ *                   +-----+
+ *                   |  B  |
+ *                   +-----+
+ *
+ * A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU
+ *
+ * The order shown above is just one instant of the server. 
+ *
+ *
+ * The hash functions currently used are based Fowler/Noll/Vo hashes
+ * which work amazingly well and have a extremely low collision rate
+ * For more info see http://www.isthe.com/chongo/tech/comp/fnv/index.html
+ *
+ * 
+ */
+
+/* init_hash()
+ *
+ * clears the various hashtables
+ */
+void
+init_hash(void)
+{
+       clientTable = rb_malloc(sizeof(rb_dlink_list) * U_MAX);
+       idTable = rb_malloc(sizeof(rb_dlink_list) * U_MAX);
+       channelTable = rb_malloc(sizeof(rb_dlink_list) * CH_MAX);
+       hostTable = rb_malloc(sizeof(rb_dlink_list) * HOST_MAX);
+       resvTable = rb_malloc(sizeof(rb_dlink_list) * R_MAX);
+}
+
+#ifndef RICER_HASHING
+u_int32_t
+fnv_hash_upper(const unsigned char *s, int bits)
+{
+       u_int32_t h = FNV1_32_INIT;
+
+       while (*s)
+       {
+               h ^= ToUpper(*s++);
+               h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
+       }
+       if (bits < 32)
+               h = ((h >> bits) ^ h) & ((1<<bits)-1);
+       return h;
+}
+
+u_int32_t
+fnv_hash(const unsigned char *s, int bits)
+{
+       u_int32_t h = FNV1_32_INIT;
+
+       while (*s)
+       {
+               h ^= *s++;
+               h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
+       }
+       if (bits < 32)
+               h = ((h >> bits) ^ h) & ((1<<bits)-1);
+       return h;
+}
+
+u_int32_t
+fnv_hash_len(const unsigned char *s, int bits, int len)
+{
+       u_int32_t h = FNV1_32_INIT;
+       const unsigned char *x = s + len;
+       while (*s && s < x)
+       {
+               h ^= *s++;
+               h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
+       }
+       if (bits < 32)
+               h = ((h >> bits) ^ h) & ((1<<bits)-1);
+       return h;
+}
+
+u_int32_t
+fnv_hash_upper_len(const unsigned char *s, int bits, int len)
+{
+       u_int32_t h = FNV1_32_INIT;
+       const unsigned char *x = s + len;
+       while (*s && s < x)
+       {
+               h ^= ToUpper(*s++);
+               h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
+       }
+       if (bits < 32)
+               h = ((h >> bits) ^ h) & ((1<<bits)-1);
+       return h;
+}
+#endif
+
+/* hash_nick()
+ *
+ * hashes a nickname, first converting to lowercase
+ */
+static u_int32_t
+hash_nick(const char *name)
+{
+       return fnv_hash_upper((const unsigned char *) name, U_MAX_BITS);
+}
+
+/* hash_id()
+ *
+ * hashes an id, case is kept
+ */
+static u_int32_t
+hash_id(const char *name)
+{
+       return fnv_hash((const unsigned char *) name, U_MAX_BITS);
+}
+
+/* hash_channel()
+ *
+ * hashes a channel name, based on first 30 chars only for efficiency
+ */
+static u_int32_t
+hash_channel(const char *name)
+{
+       return fnv_hash_upper_len((const unsigned char *) name, CH_MAX_BITS, 30);
+}
+
+/* hash_hostname()
+ *
+ * hashes a hostname, based on first 30 chars only, as thats likely to
+ * be more dynamic than rest.
+ */
+static u_int32_t
+hash_hostname(const char *name)
+{
+       return fnv_hash_upper_len((const unsigned char *) name, HOST_MAX_BITS, 30);
+}
+
+/* hash_resv()
+ *
+ * hashes a resv channel name, based on first 30 chars only
+ */
+static u_int32_t
+hash_resv(const char *name)
+{
+       return fnv_hash_upper_len((const unsigned char *) name, R_MAX_BITS, 30);
+}
+
+/* add_to_id_hash()
+ *
+ * adds an entry to the id hash table
+ */
+void
+add_to_id_hash(const char *name, struct Client *client_p)
+{
+       unsigned int hashv;
+
+       if(EmptyString(name) || (client_p == NULL))
+               return;
+
+       hashv = hash_id(name);
+       rb_dlinkAddAlloc(client_p, &idTable[hashv]);
+}
+
+/* add_to_client_hash()
+ *
+ * adds an entry (client/server) to the client hash table
+ */
+void
+add_to_client_hash(const char *name, struct Client *client_p)
+{
+       unsigned int hashv;
+
+       s_assert(name != NULL);
+       s_assert(client_p != NULL);
+       if(EmptyString(name) || (client_p == NULL))
+               return;
+
+       hashv = hash_nick(name);
+       rb_dlinkAddAlloc(client_p, &clientTable[hashv]);
+}
+
+/* add_to_hostname_hash()
+ *
+ * adds a client entry to the hostname hash table
+ */
+void
+add_to_hostname_hash(const char *hostname, struct Client *client_p)
+{
+       unsigned int hashv;
+
+       s_assert(hostname != NULL);
+       s_assert(client_p != NULL);
+       if(EmptyString(hostname) || (client_p == NULL))
+               return;
+
+       hashv = hash_hostname(hostname);
+       rb_dlinkAddAlloc(client_p, &hostTable[hashv]);
+}
+
+/* add_to_resv_hash()
+ *
+ * adds a resv channel entry to the resv hash table
+ */
+void
+add_to_resv_hash(const char *name, struct ConfItem *aconf)
+{
+       unsigned int hashv;
+
+       s_assert(!EmptyString(name));
+       s_assert(aconf != NULL);
+       if(EmptyString(name) || aconf == NULL)
+               return;
+
+       hashv = hash_resv(name);
+       rb_dlinkAddAlloc(aconf, &resvTable[hashv]);
+}
+
+/* del_from_id_hash()
+ *
+ * removes an id from the id hash table
+ */
+void
+del_from_id_hash(const char *id, struct Client *client_p)
+{
+       unsigned int hashv;
+
+       s_assert(id != NULL);
+       s_assert(client_p != NULL);
+       if(EmptyString(id) || client_p == NULL)
+               return;
+
+       hashv = hash_id(id);
+       rb_dlinkFindDestroy(client_p, &idTable[hashv]);
+}
+
+/* del_from_client_hash()
+ *
+ * removes a client/server from the client hash table
+ */
+void
+del_from_client_hash(const char *name, struct Client *client_p)
+{
+       unsigned int hashv;
+
+       /* no s_asserts, this can happen when removing a client that
+        * is unregistered.
+        */
+       if(EmptyString(name) || client_p == NULL)
+               return;
+
+       hashv = hash_nick(name);
+       rb_dlinkFindDestroy(client_p, &clientTable[hashv]);
+}
+
+/* del_from_channel_hash()
+ * 
+ * removes a channel from the channel hash table
+ */
+void
+del_from_channel_hash(const char *name, struct Channel *chptr)
+{
+       unsigned int hashv;
+
+       s_assert(name != NULL);
+       s_assert(chptr != NULL);
+
+       if(EmptyString(name) || chptr == NULL)
+               return;
+
+       hashv = hash_channel(name);
+       rb_dlinkFindDestroy(chptr, &channelTable[hashv]);
+}
+
+/* del_from_hostname_hash()
+ *
+ * removes a client entry from the hostname hash table
+ */
+void
+del_from_hostname_hash(const char *hostname, struct Client *client_p)
+{
+       unsigned int hashv;
+
+       if(hostname == NULL || client_p == NULL)
+               return;
+
+       hashv = hash_hostname(hostname);
+
+       rb_dlinkFindDestroy(client_p, &hostTable[hashv]);
+}
+
+/* del_from_resv_hash()
+ *
+ * removes a resv entry from the resv hash table
+ */
+void
+del_from_resv_hash(const char *name, struct ConfItem *aconf)
+{
+       unsigned int hashv;
+
+       s_assert(name != NULL);
+       s_assert(aconf != NULL);
+       if(EmptyString(name) || aconf == NULL)
+               return;
+
+       hashv = hash_resv(name);
+
+       rb_dlinkFindDestroy(aconf, &resvTable[hashv]);
+}
+
+/* find_id()
+ *
+ * finds a client entry from the id hash table
+ */
+struct Client *
+find_id(const char *name)
+{
+       struct Client *target_p;
+       rb_dlink_node *ptr;
+       unsigned int hashv;
+
+       if(EmptyString(name))
+               return NULL;
+
+       hashv = hash_id(name);
+
+       RB_DLINK_FOREACH(ptr, idTable[hashv].head)
+       {
+               target_p = ptr->data;
+
+               if(strcmp(name, target_p->id) == 0)
+                       return target_p;
+       }
+
+       return NULL;
+}
+
+/* find_client()
+ *
+ * finds a client/server entry from the client hash table
+ */
+struct Client *
+find_client(const char *name)
+{
+       struct Client *target_p;
+       rb_dlink_node *ptr;
+       unsigned int hashv;
+
+       s_assert(name != NULL);
+       if(EmptyString(name))
+               return NULL;
+
+       /* hunting for an id, not a nick */
+       if(IsDigit(*name))
+               return (find_id(name));
+
+       hashv = hash_nick(name);
+
+       RB_DLINK_FOREACH(ptr, clientTable[hashv].head)
+       {
+               target_p = ptr->data;
+
+               if(irccmp(name, target_p->name) == 0)
+                       return target_p;
+       }
+
+       return NULL;
+}
+
+/* find_named_client()
+ *
+ * finds a client/server entry from the client hash table
+ */
+struct Client *
+find_named_client(const char *name)
+{
+       struct Client *target_p;
+       rb_dlink_node *ptr;
+       unsigned int hashv;
+
+       s_assert(name != NULL);
+       if(EmptyString(name))
+               return NULL;
+
+       hashv = hash_nick(name);
+
+       RB_DLINK_FOREACH(ptr, clientTable[hashv].head)
+       {
+               target_p = ptr->data;
+
+               if(irccmp(name, target_p->name) == 0)
+                       return target_p;
+       }
+
+       return NULL;
+}
+
+/* find_server()
+ *
+ * finds a server from the client hash table
+ */
+struct Client *
+find_server(struct Client *source_p, const char *name)
+{
+       struct Client *target_p;
+       rb_dlink_node *ptr;
+       unsigned int hashv;
+  
+       if(EmptyString(name))
+               return NULL;
+
+       if((source_p == NULL || !MyClient(source_p)) && 
+          IsDigit(*name) && strlen(name) == 3)
+       {
+               target_p = find_id(name);
+               return(target_p);
+       }
+
+       hashv = hash_nick(name);
+
+       RB_DLINK_FOREACH(ptr, clientTable[hashv].head)
+       {
+               target_p = ptr->data;
+
+               if((IsServer(target_p) || IsMe(target_p)) &&
+                  irccmp(name, target_p->name) == 0)
+                               return target_p;
+       }
+
+       return NULL;
+}
+
+/* find_hostname()
+ *
+ * finds a hostname rb_dlink list from the hostname hash table.
+ * we return the full rb_dlink list, because you can have multiple
+ * entries with the same hostname
+ */
+rb_dlink_node *
+find_hostname(const char *hostname)
+{
+       unsigned int hashv;
+
+       if(EmptyString(hostname))
+               return NULL;
+
+       hashv = hash_hostname(hostname);
+
+       return hostTable[hashv].head;
+}
+
+/* find_channel()
+ *
+ * finds a channel from the channel hash table
+ */
+struct Channel *
+find_channel(const char *name)
+{
+       struct Channel *chptr;
+       rb_dlink_node *ptr;
+       unsigned int hashv;
+
+       s_assert(name != NULL);
+       if(EmptyString(name))
+               return NULL;
+
+       hashv = hash_channel(name);
+
+       RB_DLINK_FOREACH(ptr, channelTable[hashv].head)
+       {
+               chptr = ptr->data;
+
+               if(irccmp(name, chptr->chname) == 0)
+                       return chptr;
+       }
+
+       return NULL;
+}
+
+/*
+ * get_or_create_channel
+ * inputs       - client pointer
+ *              - channel name
+ *              - pointer to int flag whether channel was newly created or not
+ * output       - returns channel block or NULL if illegal name
+ *             - also modifies *isnew
+ *
+ *  Get Channel block for chname (and allocate a new channel
+ *  block, if it didn't exist before).
+ */
+struct Channel *
+get_or_create_channel(struct Client *client_p, const char *chname, int *isnew)
+{
+       struct Channel *chptr;
+       rb_dlink_node *ptr;
+       unsigned int hashv;
+       int len;
+       const char *s = chname;
+
+       if(EmptyString(s))
+               return NULL;
+
+       len = strlen(s);
+       if(len > CHANNELLEN)
+       {
+               char *t;
+               if(IsServer(client_p))
+               {
+                       sendto_realops_snomask(SNO_DEBUG, L_ALL,
+                                            "*** Long channel name from %s (%d > %d): %s",
+                                            client_p->name, len, CHANNELLEN, s);
+               }
+               len = CHANNELLEN;
+               t = LOCAL_COPY(s);
+               *(t + CHANNELLEN) = '\0';
+               s = t;
+       }
+
+       hashv = hash_channel(s);
+
+       RB_DLINK_FOREACH(ptr, channelTable[hashv].head)
+       {
+               chptr = ptr->data;
+
+               if(irccmp(s, chptr->chname) == 0)
+               {
+                       if(isnew != NULL)
+                               *isnew = 0;
+                       return chptr;
+               }
+       }
+
+       if(isnew != NULL)
+               *isnew = 1;
+
+       chptr = allocate_channel(s);
+
+       rb_dlinkAdd(chptr, &chptr->node, &global_channel_list);
+
+       chptr->channelts = rb_current_time();   /* doesn't hurt to set it here */
+
+       rb_dlinkAddAlloc(chptr, &channelTable[hashv]);
+
+       return chptr;
+}
+
+/* hash_find_resv()
+ *
+ * hunts for a resv entry in the resv hash table
+ */
+struct ConfItem *
+hash_find_resv(const char *name)
+{
+       struct ConfItem *aconf;
+       rb_dlink_node *ptr;
+       unsigned int hashv;
+
+       s_assert(name != NULL);
+       if(EmptyString(name))
+               return NULL;
+
+       hashv = hash_resv(name);
+
+       RB_DLINK_FOREACH(ptr, resvTable[hashv].head)
+       {
+               aconf = ptr->data;
+
+               if(!irccmp(name, aconf->name))
+               {
+                       aconf->port++;
+                       return aconf;
+               }
+       }
+
+       return NULL;
+}
+
+void
+clear_resv_hash(void)
+{
+       struct ConfItem *aconf;
+       rb_dlink_node *ptr;
+       rb_dlink_node *next_ptr;
+       int i;
+
+       HASH_WALK_SAFE(i, R_MAX, ptr, next_ptr, resvTable)
+       {
+               aconf = ptr->data;
+
+               /* skip temp resvs */
+               if(aconf->hold)
+                       continue;
+
+               free_conf(ptr->data);
+               rb_dlinkDestroy(ptr, &resvTable[i]);
+       }
+       HASH_WALK_END
+}
+
+void
+add_to_cli_fd_hash(struct Client *client_p)
+{
+       rb_dlinkAddAlloc(client_p, &clientbyfdTable[hash_cli_fd(rb_get_fd(client_p->localClient->F))]);
+}
+
+
+void
+del_from_cli_fd_hash(struct Client *client_p)
+{
+       unsigned int hashv;
+       hashv = hash_cli_fd(rb_get_fd(client_p->localClient->F));
+       rb_dlinkFindDestroy(client_p, &clientbyfdTable[hashv]);
+}
+
+struct Client *
+find_cli_fd_hash(int fd)
+{
+       struct Client *target_p;
+       rb_dlink_node *ptr;
+       unsigned int hashv;
+       hashv = hash_cli_fd(fd);
+       RB_DLINK_FOREACH(ptr, clientbyfdTable[hashv].head)
+       {
+               target_p = ptr->data;
+               if(rb_get_fd(target_p->localClient->F) == fd)
+                       return target_p;
+       }
+       return  NULL;   
+}
+
+static void
+output_hash(struct Client *source_p, const char *name, int length, int *counts, int deepest)
+{
+       unsigned long total = 0;
+       int i;
+       char buf[128];
+
+       sendto_one_numeric(source_p, RPL_STATSDEBUG,
+                       "B :%s Hash Statistics", name);
+
+       snprintf(buf, sizeof buf, "%.3f%%",
+                       (float) ((counts[0]*100) / (float) length));
+       sendto_one_numeric(source_p, RPL_STATSDEBUG,
+                       "B :Size: %d Empty: %d (%s)",
+                       length, counts[0], buf);
+
+       for(i = 1; i < 11; i++)
+       {
+               total += (counts[i] * i);
+       }
+
+       /* dont want to divide by 0! --fl */
+       if(counts[0] != length)
+       {
+               snprintf(buf, sizeof buf, "%.3f/%.3f",
+                               (float) (total / (length - counts[0])),
+                               (float) (total / length));
+               sendto_one_numeric(source_p, RPL_STATSDEBUG,
+                               "B :Average depth: %s Highest depth: %d",
+                               buf, deepest);
+       }
+
+       for(i = 0; i < 11; i++)
+       {
+               sendto_one_numeric(source_p, RPL_STATSDEBUG,
+                               "B :Nodes with %d entries: %d",
+                               i, counts[i]);
+       }
+}
+       
+
+static void
+count_hash(struct Client *source_p, rb_dlink_list *table, int length, const char *name)
+{
+       int counts[11];
+       int deepest = 0;
+       int i;
+
+       memset(counts, 0, sizeof(counts));
+       
+       for(i = 0; i < length; i++)
+       {
+               if(rb_dlink_list_length(&table[i]) >= 10)
+                       counts[10]++;
+               else
+                       counts[rb_dlink_list_length(&table[i])]++;
+
+               if(rb_dlink_list_length(&table[i]) > deepest)
+                       deepest = rb_dlink_list_length(&table[i]);
+       }
+
+       output_hash(source_p, name, length, counts, deepest);
+}
+
+void
+hash_stats(struct Client *source_p)
+{
+       count_hash(source_p, channelTable, CH_MAX, "Channel");
+       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
+       count_hash(source_p, clientTable, U_MAX, "Client");
+       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
+       count_hash(source_p, idTable, U_MAX, "ID");
+       sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
+       count_hash(source_p, hostTable, HOST_MAX, "Hostname");
+}