5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 Pekka Riikonen
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.
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.
22 #include "serverincludes.h"
25 /******************************************************************************
29 ******************************************************************************/
31 /* This function is used to add keys and stuff to common ID entry data
34 void silc_idlist_add_data(void *entry, SilcIDListData idata)
36 SilcIDListData data = (SilcIDListData)entry;
37 data->send_key = idata->send_key;
38 data->receive_key = idata->receive_key;
39 data->rekey = idata->rekey;
40 data->hash = idata->hash;
41 data->hmac_send = idata->hmac_send;
42 data->hmac_receive = idata->hmac_receive;
43 data->public_key = idata->public_key;
44 data->last_receive = idata->last_receive;
45 data->last_sent = idata->last_sent;
46 data->registered = idata->registered;
49 /* Free's all data in the common ID entry data structure. */
51 void silc_idlist_del_data(void *entry)
53 SilcIDListData idata = (SilcIDListData)entry;
55 silc_cipher_free(idata->send_key);
56 if (idata->receive_key)
57 silc_cipher_free(idata->receive_key);
59 if (idata->rekey->send_enc_key) {
60 memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
61 silc_free(idata->rekey->send_enc_key);
63 silc_free(idata->rekey);
65 if (idata->hmac_send) /* Same as idata->hmac_receive */
66 silc_hmac_free(idata->hmac_send);
67 if (idata->public_key)
68 silc_pkcs_public_key_free(idata->public_key);
73 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
75 SilcIDListPurge i = (SilcIDListPurge)context;
77 SILC_LOG_DEBUG(("Start"));
79 silc_idcache_purge(i->cache);
80 silc_task_register(i->timeout_queue, 0,
83 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
86 /******************************************************************************
88 Server entry functions
90 ******************************************************************************/
92 /* Add new server entry. This adds the new server entry to ID cache and
93 returns the allocated entry object or NULL on error. This is called
94 when new server connects to us. We also add ourselves to cache with
98 silc_idlist_add_server(SilcIDList id_list,
99 char *server_name, int server_type,
100 SilcServerID *id, SilcServerEntry router,
103 SilcServerEntry server;
105 SILC_LOG_DEBUG(("Adding new server entry"));
107 server = silc_calloc(1, sizeof(*server));
108 server->server_name = server_name;
109 server->server_type = server_type;
111 server->router = router;
112 server->connection = connection;
114 if (!silc_idcache_add(id_list->servers, server->server_name,
115 (void *)server->id, (void *)server, FALSE)) {
123 /* Finds server by Server ID */
126 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
127 SilcIDCacheEntry *ret_entry)
129 SilcIDCacheEntry id_cache = NULL;
130 SilcServerEntry server;
135 SILC_LOG_DEBUG(("Server ID (%s)",
136 silc_id_render(id, SILC_ID_SERVER)));
138 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
142 server = (SilcServerEntry)id_cache->context;
145 *ret_entry = id_cache;
150 /* Find server by name */
153 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
154 SilcIDCacheEntry *ret_entry)
156 SilcIDCacheEntry id_cache = NULL;
157 SilcServerEntry server;
159 SILC_LOG_DEBUG(("Server by name `%s'", name));
161 if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
164 server = (SilcServerEntry)id_cache->context;
167 *ret_entry = id_cache;
169 SILC_LOG_DEBUG(("Found"));
174 /* Find server by connection parameters, hostname and port */
177 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
178 int port, SilcIDCacheEntry *ret_entry)
180 SilcIDCacheList list = NULL;
181 SilcIDCacheEntry id_cache = NULL;
182 SilcServerEntry server = NULL;
183 SilcSocketConnection sock;
185 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
187 if (!silc_idcache_get_all(id_list->servers, &list))
190 if (!silc_idcache_list_first(list, &id_cache)) {
191 silc_idcache_list_free(list);
196 server = (SilcServerEntry)id_cache->context;
197 sock = (SilcSocketConnection)server->connection;
199 if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
200 (sock->ip && !strcasecmp(sock->ip, hostname)))
201 && sock->port == port)
207 if (!silc_idcache_list_next(list, &id_cache))
211 silc_idcache_list_free(list);
214 *ret_entry = id_cache;
216 SILC_LOG_DEBUG(("Found"));
221 /* Replaces old Server ID with new one */
224 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
225 SilcServerID *new_id)
227 SilcIDCacheEntry id_cache = NULL;
228 SilcServerEntry server;
230 if (!old_id || !new_id)
233 SILC_LOG_DEBUG(("Replacing Server ID"));
235 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
239 server = (SilcServerEntry)id_cache->context;
241 /* Remove the old entry and add a new one */
243 silc_idcache_del_by_id(id_list->servers, (void *)server->id);
245 silc_free(server->id);
248 silc_idcache_add(id_list->servers, server->server_name, server->id,
251 SILC_LOG_DEBUG(("Found"));
256 /* Removes and free's server entry from ID list */
258 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
260 SILC_LOG_DEBUG(("Start"));
263 /* Remove from cache */
265 if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
269 if (entry->server_name)
270 silc_free(entry->server_name);
272 silc_free(entry->id);
274 memset(entry, 'F', sizeof(*entry));
282 /******************************************************************************
284 Client entry functions
286 ******************************************************************************/
288 /* Add new client entry. This adds the client entry to ID cache system
289 and returns the allocated client entry or NULL on error. This is
290 called when new client connection is accepted to the server. If The
291 `router' is provided then the all server routines assume that the client
292 is not directly connected local client but it has router set and is
293 remote. If this is the case then `connection' must be NULL. If, on the
294 other hand, the `connection' is provided then the client is assumed
295 to be directly connected local client and `router' must be NULL. */
298 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
299 char *userinfo, SilcClientID *id,
300 SilcServerEntry router, void *connection)
302 SilcClientEntry client;
304 SILC_LOG_DEBUG(("Adding new client entry"));
306 client = silc_calloc(1, sizeof(*client));
307 client->nickname = nickname;
308 client->username = username;
309 client->userinfo = userinfo;
311 client->router = router;
312 client->connection = connection;
313 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
314 NULL, NULL, NULL, NULL, TRUE);
316 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
317 (void *)client, FALSE)) {
318 silc_hash_table_free(client->channels);
326 /* Free client entry. This free's everything and removes the entry
327 from ID cache. Call silc_idlist_del_data before calling this one. */
329 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
331 SILC_LOG_DEBUG(("Start"));
334 /* Remove from cache */
336 if (!silc_idcache_del_by_context(id_list->clients, entry))
341 silc_free(entry->nickname);
343 silc_free(entry->username);
345 silc_free(entry->userinfo);
347 silc_free(entry->id);
349 memset(entry, 'F', sizeof(*entry));
358 /* Returns all clients matching requested nickname. Number of clients is
359 returned to `clients_count'. Caller must free the returned table. */
361 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
363 SilcClientEntry **clients,
364 uint32 *clients_count)
366 SilcIDCacheList list = NULL;
367 SilcIDCacheEntry id_cache = NULL;
370 SILC_LOG_DEBUG(("Start"));
372 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
375 *clients = silc_realloc(*clients,
376 (silc_idcache_list_count(list) + *clients_count) *
380 silc_idcache_list_first(list, &id_cache);
381 (*clients)[i++] = (SilcClientEntry)id_cache->context;
383 while (silc_idcache_list_next(list, &id_cache))
384 (*clients)[i++] = (SilcClientEntry)id_cache->context;
386 silc_idcache_list_free(list);
390 SILC_LOG_DEBUG(("Found %d clients", *clients_count));
395 /* Returns all clients matching requested nickname hash. Number of clients
396 is returned to `clients_count'. Caller must free the returned table. */
398 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
400 SilcClientEntry **clients,
401 uint32 *clients_count)
403 SilcIDCacheList list = NULL;
404 SilcIDCacheEntry id_cache = NULL;
405 unsigned char hash[32];
407 SilcClientID client_id;
409 SILC_LOG_DEBUG(("Start"));
411 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
413 /* As the Client ID is hashed in the ID cache by hashing only the hash
414 from the Client ID, we can do a lookup with only the hash not the
415 other parts of the ID and get all the clients with that hash, ie.
416 with that nickname, as the hash is from the nickname. */
417 memset(&client_id, 0, sizeof(client_id));
418 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
419 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
422 *clients = silc_realloc(*clients,
423 (silc_idcache_list_count(list) + *clients_count) *
427 silc_idcache_list_first(list, &id_cache);
428 (*clients)[i++] = (SilcClientEntry)id_cache->context;
430 while (silc_idcache_list_next(list, &id_cache))
431 (*clients)[i++] = (SilcClientEntry)id_cache->context;
433 silc_idcache_list_free(list);
437 SILC_LOG_DEBUG(("Found %d clients", *clients_count));
442 /* Finds client by Client ID */
445 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
446 SilcIDCacheEntry *ret_entry)
448 SilcIDCacheEntry id_cache = NULL;
449 SilcClientEntry client;
454 SILC_LOG_DEBUG(("Client ID (%s)",
455 silc_id_render(id, SILC_ID_CLIENT)));
457 /* Do extended search since the normal ID comparison function for
458 Client ID's compares only the hash from the Client ID and not the
459 entire ID. The silc_hash_client_id_compare compares the entire
460 Client ID as we want to find one specific Client ID. */
461 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
463 silc_hash_client_id_compare, NULL,
467 client = (SilcClientEntry)id_cache->context;
470 *ret_entry = id_cache;
472 SILC_LOG_DEBUG(("Found"));
477 /* Replaces old Client ID with new one */
480 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
481 SilcClientID *new_id)
483 SilcIDCacheEntry id_cache = NULL;
484 SilcClientEntry client;
486 if (!old_id || !new_id)
489 SILC_LOG_DEBUG(("Replacing Client ID"));
491 /* Do extended search since the normal ID comparison function for
492 Client ID's compares only the hash from the Client ID and not the
493 entire ID. The silc_hash_client_id_compare compares the entire
494 Client ID as we want to find one specific Client ID. */
495 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
497 silc_hash_client_id_compare, NULL,
501 client = (SilcClientEntry)id_cache->context;
503 /* Remove the old entry and add a new one */
505 silc_idcache_del_by_context(id_list->clients, client);
507 silc_free(client->id);
510 silc_idcache_add(id_list->clients, client->nickname, client->id,
513 SILC_LOG_DEBUG(("Replaced"));
518 /* Client cache entry destructor that is called when the cache is purged. */
520 void silc_idlist_client_destructor(SilcIDCache cache,
521 SilcIDCacheEntry entry)
523 SilcClientEntry client;
525 SILC_LOG_DEBUG(("Start"));
527 client = (SilcClientEntry)entry->context;
529 if (client->nickname)
530 silc_free(client->nickname);
531 if (client->username)
532 silc_free(client->username);
533 if (client->userinfo)
534 silc_free(client->userinfo);
536 silc_free(client->id);
538 memset(client, 'F', sizeof(*client));
543 /******************************************************************************
545 Channel entry functions
547 ******************************************************************************/
549 /* Add new channel entry. This add the new channel entry to the ID cache
550 system and returns the allocated entry or NULL on error. */
553 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
554 SilcChannelID *id, SilcServerEntry router,
555 SilcCipher channel_key, SilcHmac hmac)
557 SilcChannelEntry channel;
559 channel = silc_calloc(1, sizeof(*channel));
560 channel->channel_name = channel_name;
561 channel->mode = mode;
563 channel->router = router;
564 channel->channel_key = channel_key;
565 channel->hmac = hmac;
567 if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
572 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
573 NULL, NULL, NULL, TRUE);
575 if (!silc_idcache_add(id_list->channels, channel->channel_name,
576 (void *)channel->id, (void *)channel, FALSE)) {
577 silc_hmac_free(channel->hmac);
578 silc_hash_table_free(channel->user_list);
586 /* Foreach callbcak to free all users from the channel when deleting a
589 static void silc_idlist_del_channel_foreach(void *key, void *context,
592 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
594 /* Remove the context from the client's channel hash table as that
595 table and channel's user_list hash table share this same context. */
596 silc_hash_table_del(chl->client->channels, chl->channel);
600 /* Free channel entry. This free's everything. */
602 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
604 SILC_LOG_DEBUG(("Start"));
607 /* Remove from cache */
609 if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
613 if (entry->channel_name)
614 silc_free(entry->channel_name);
616 silc_free(entry->id);
618 silc_free(entry->topic);
619 if (entry->channel_key)
620 silc_cipher_free(entry->channel_key);
622 memset(entry->key, 0, entry->key_len / 8);
623 silc_free(entry->key);
626 silc_free(entry->cipher);
627 if (entry->hmac_name)
628 silc_free(entry->hmac_name);
630 silc_free(entry->rekey);
632 /* Free all client entrys from the users list. The silc_hash_table_free
633 will free all the entries so they are not freed at the foreach
635 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
637 silc_hash_table_free(entry->user_list);
639 memset(entry, 'F', sizeof(*entry));
647 /* Finds channel by channel name. Channel names are unique and they
648 are not case-sensitive. */
651 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
652 SilcIDCacheEntry *ret_entry)
654 SilcIDCacheEntry id_cache = NULL;
656 SILC_LOG_DEBUG(("Channel by name"));
658 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
662 *ret_entry = id_cache;
664 SILC_LOG_DEBUG(("Found"));
666 return id_cache->context;
669 /* Finds channel by Channel ID. */
672 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
673 SilcIDCacheEntry *ret_entry)
675 SilcIDCacheEntry id_cache = NULL;
676 SilcChannelEntry channel;
681 SILC_LOG_DEBUG(("Channel ID (%s)",
682 silc_id_render(id, SILC_ID_CHANNEL)));
684 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
687 channel = (SilcChannelEntry)id_cache->context;
690 *ret_entry = id_cache;
692 SILC_LOG_DEBUG(("Found"));
697 /* Replaces old Channel ID with new one. This is done when router forces
698 normal server to change Channel ID. */
701 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
702 SilcChannelID *new_id)
704 SilcIDCacheEntry id_cache = NULL;
705 SilcChannelEntry channel;
707 if (!old_id || !new_id)
710 SILC_LOG_DEBUG(("Replacing Channel ID"));
712 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
716 channel = (SilcChannelEntry)id_cache->context;
718 /* Remove the old entry and add a new one */
720 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
722 silc_free(channel->id);
723 channel->id = new_id;
725 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
728 SILC_LOG_DEBUG(("Replaced"));
733 /* Returns channels from the ID list. If the `channel_id' is NULL then
734 all channels are returned. */
737 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
738 uint32 *channels_count)
740 SilcIDCacheList list = NULL;
741 SilcIDCacheEntry id_cache = NULL;
742 SilcChannelEntry *channels = NULL;
745 SILC_LOG_DEBUG(("Start"));
748 if (!silc_idcache_get_all(id_list->channels, &list))
751 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
754 silc_idcache_list_first(list, &id_cache);
755 channels[i++] = (SilcChannelEntry)id_cache->context;
757 while (silc_idcache_list_next(list, &id_cache))
758 channels[i++] = (SilcChannelEntry)id_cache->context;
760 silc_idcache_list_free(list);
762 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
766 channels = silc_calloc(1, sizeof(*channels));
767 channels[0] = (SilcChannelEntry)id_cache->context;