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;
147 SILC_LOG_DEBUG(("Found"));
152 /* Find server by name */
155 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
156 SilcIDCacheEntry *ret_entry)
158 SilcIDCacheEntry id_cache = NULL;
159 SilcServerEntry server;
161 SILC_LOG_DEBUG(("Server by name `%s'", name));
163 if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
166 server = (SilcServerEntry)id_cache->context;
169 *ret_entry = id_cache;
171 SILC_LOG_DEBUG(("Found"));
176 /* Find server by connection parameters, hostname and port */
179 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
180 int port, SilcIDCacheEntry *ret_entry)
182 SilcIDCacheList list = NULL;
183 SilcIDCacheEntry id_cache = NULL;
184 SilcServerEntry server = NULL;
185 SilcSocketConnection sock;
187 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
189 if (!silc_idcache_get_all(id_list->servers, &list))
192 if (!silc_idcache_list_first(list, &id_cache)) {
193 silc_idcache_list_free(list);
198 server = (SilcServerEntry)id_cache->context;
199 sock = (SilcSocketConnection)server->connection;
201 if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
202 (sock->ip && !strcasecmp(sock->ip, hostname)))
203 && sock->port == port)
209 if (!silc_idcache_list_next(list, &id_cache))
213 silc_idcache_list_free(list);
216 *ret_entry = id_cache;
218 SILC_LOG_DEBUG(("Found"));
223 /* Replaces old Server ID with new one */
226 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
227 SilcServerID *new_id)
229 SilcIDCacheEntry id_cache = NULL;
230 SilcServerEntry server;
232 if (!old_id || !new_id)
235 SILC_LOG_DEBUG(("Replacing Server ID"));
237 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
241 server = (SilcServerEntry)id_cache->context;
243 /* Remove the old entry and add a new one */
245 silc_idcache_del_by_id(id_list->servers, (void *)server->id);
247 silc_free(server->id);
250 silc_idcache_add(id_list->servers, server->server_name, server->id,
253 SILC_LOG_DEBUG(("Found"));
258 /* Removes and free's server entry from ID list */
260 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
262 SILC_LOG_DEBUG(("Start"));
265 /* Remove from cache */
267 if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
271 if (entry->server_name)
272 silc_free(entry->server_name);
274 silc_free(entry->id);
276 memset(entry, 'F', sizeof(*entry));
284 /******************************************************************************
286 Client entry functions
288 ******************************************************************************/
290 /* Add new client entry. This adds the client entry to ID cache system
291 and returns the allocated client entry or NULL on error. This is
292 called when new client connection is accepted to the server. If The
293 `router' is provided then the all server routines assume that the client
294 is not directly connected local client but it has router set and is
295 remote. If this is the case then `connection' must be NULL. If, on the
296 other hand, the `connection' is provided then the client is assumed
297 to be directly connected local client and `router' must be NULL. */
300 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
301 char *userinfo, SilcClientID *id,
302 SilcServerEntry router, void *connection)
304 SilcClientEntry client;
306 SILC_LOG_DEBUG(("Adding new client entry"));
308 client = silc_calloc(1, sizeof(*client));
309 client->nickname = nickname;
310 client->username = username;
311 client->userinfo = userinfo;
313 client->router = router;
314 client->connection = connection;
315 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
316 NULL, NULL, NULL, NULL, TRUE);
318 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
319 (void *)client, FALSE)) {
320 silc_hash_table_free(client->channels);
328 /* Free client entry. This free's everything and removes the entry
329 from ID cache. Call silc_idlist_del_data before calling this one. */
331 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
333 SILC_LOG_DEBUG(("Start"));
336 /* Remove from cache */
338 if (!silc_idcache_del_by_context(id_list->clients, entry))
343 silc_free(entry->nickname);
345 silc_free(entry->username);
347 silc_free(entry->userinfo);
349 silc_free(entry->id);
351 memset(entry, 'F', sizeof(*entry));
360 /* Returns all clients matching requested nickname. Number of clients is
361 returned to `clients_count'. Caller must free the returned table. */
363 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
365 SilcClientEntry **clients,
366 uint32 *clients_count)
368 SilcIDCacheList list = NULL;
369 SilcIDCacheEntry id_cache = NULL;
372 SILC_LOG_DEBUG(("Start"));
374 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
377 *clients = silc_realloc(*clients,
378 (silc_idcache_list_count(list) + *clients_count) *
382 silc_idcache_list_first(list, &id_cache);
383 (*clients)[i++] = (SilcClientEntry)id_cache->context;
385 while (silc_idcache_list_next(list, &id_cache))
386 (*clients)[i++] = (SilcClientEntry)id_cache->context;
388 silc_idcache_list_free(list);
392 SILC_LOG_DEBUG(("Found %d clients", *clients_count));
397 /* Returns all clients matching requested nickname hash. Number of clients
398 is returned to `clients_count'. Caller must free the returned table. */
400 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
402 SilcClientEntry **clients,
403 uint32 *clients_count)
405 SilcIDCacheList list = NULL;
406 SilcIDCacheEntry id_cache = NULL;
407 unsigned char hash[32];
409 SilcClientID client_id;
411 SILC_LOG_DEBUG(("Start"));
413 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
415 /* As the Client ID is hashed in the ID cache by hashing only the hash
416 from the Client ID, we can do a lookup with only the hash not the
417 other parts of the ID and get all the clients with that hash, ie.
418 with that nickname, as the hash is from the nickname. */
419 memset(&client_id, 0, sizeof(client_id));
420 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
421 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
424 *clients = silc_realloc(*clients,
425 (silc_idcache_list_count(list) + *clients_count) *
429 silc_idcache_list_first(list, &id_cache);
430 (*clients)[i++] = (SilcClientEntry)id_cache->context;
432 while (silc_idcache_list_next(list, &id_cache))
433 (*clients)[i++] = (SilcClientEntry)id_cache->context;
435 silc_idcache_list_free(list);
439 SILC_LOG_DEBUG(("Found %d clients", *clients_count));
444 /* Finds client by Client ID */
447 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
448 SilcIDCacheEntry *ret_entry)
450 SilcIDCacheEntry id_cache = NULL;
451 SilcClientEntry client;
456 SILC_LOG_DEBUG(("Client ID (%s)",
457 silc_id_render(id, SILC_ID_CLIENT)));
459 /* Do extended search since the normal ID comparison function for
460 Client ID's compares only the hash from the Client ID and not the
461 entire ID. The silc_hash_client_id_compare compares the entire
462 Client ID as we want to find one specific Client ID. */
463 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
465 silc_hash_client_id_compare, NULL,
469 client = (SilcClientEntry)id_cache->context;
472 *ret_entry = id_cache;
474 SILC_LOG_DEBUG(("Found"));
479 /* Replaces old Client ID with new one */
482 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
483 SilcClientID *new_id)
485 SilcIDCacheEntry id_cache = NULL;
486 SilcClientEntry client;
488 if (!old_id || !new_id)
491 SILC_LOG_DEBUG(("Replacing Client ID"));
493 /* Do extended search since the normal ID comparison function for
494 Client ID's compares only the hash from the Client ID and not the
495 entire ID. The silc_hash_client_id_compare compares the entire
496 Client ID as we want to find one specific Client ID. */
497 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
499 silc_hash_client_id_compare, NULL,
503 client = (SilcClientEntry)id_cache->context;
505 /* Remove the old entry and add a new one */
507 silc_idcache_del_by_context(id_list->clients, client);
509 silc_free(client->id);
512 silc_idcache_add(id_list->clients, client->nickname, client->id,
515 SILC_LOG_DEBUG(("Replaced"));
520 /* Client cache entry destructor that is called when the cache is purged. */
522 void silc_idlist_client_destructor(SilcIDCache cache,
523 SilcIDCacheEntry entry)
525 SilcClientEntry client;
527 SILC_LOG_DEBUG(("Start"));
529 client = (SilcClientEntry)entry->context;
531 if (client->nickname)
532 silc_free(client->nickname);
533 if (client->username)
534 silc_free(client->username);
535 if (client->userinfo)
536 silc_free(client->userinfo);
538 silc_free(client->id);
540 memset(client, 'F', sizeof(*client));
545 /******************************************************************************
547 Channel entry functions
549 ******************************************************************************/
551 /* Add new channel entry. This add the new channel entry to the ID cache
552 system and returns the allocated entry or NULL on error. */
555 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
556 SilcChannelID *id, SilcServerEntry router,
557 SilcCipher channel_key, SilcHmac hmac)
559 SilcChannelEntry channel;
561 SILC_LOG_DEBUG(("Adding new channel entry"));
563 channel = silc_calloc(1, sizeof(*channel));
564 channel->channel_name = channel_name;
565 channel->mode = mode;
567 channel->router = router;
568 channel->channel_key = channel_key;
569 channel->hmac = hmac;
571 if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
576 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
577 NULL, NULL, NULL, TRUE);
579 if (!silc_idcache_add(id_list->channels, channel->channel_name,
580 (void *)channel->id, (void *)channel, FALSE)) {
581 silc_hmac_free(channel->hmac);
582 silc_hash_table_free(channel->user_list);
590 /* Foreach callbcak to free all users from the channel when deleting a
593 static void silc_idlist_del_channel_foreach(void *key, void *context,
596 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
598 /* Remove the context from the client's channel hash table as that
599 table and channel's user_list hash table share this same context. */
600 silc_hash_table_del(chl->client->channels, chl->channel);
604 /* Free channel entry. This free's everything. */
606 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
608 SILC_LOG_DEBUG(("Start"));
611 /* Remove from cache */
613 if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
617 if (entry->channel_name)
618 silc_free(entry->channel_name);
620 silc_free(entry->id);
622 silc_free(entry->topic);
623 if (entry->channel_key)
624 silc_cipher_free(entry->channel_key);
626 memset(entry->key, 0, entry->key_len / 8);
627 silc_free(entry->key);
630 silc_free(entry->cipher);
631 if (entry->hmac_name)
632 silc_free(entry->hmac_name);
634 silc_free(entry->rekey);
636 /* Free all client entrys from the users list. The silc_hash_table_free
637 will free all the entries so they are not freed at the foreach
639 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
641 silc_hash_table_free(entry->user_list);
643 memset(entry, 'F', sizeof(*entry));
651 /* Finds channel by channel name. Channel names are unique and they
652 are not case-sensitive. */
655 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
656 SilcIDCacheEntry *ret_entry)
658 SilcIDCacheEntry id_cache = NULL;
660 SILC_LOG_DEBUG(("Channel by name"));
662 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
666 *ret_entry = id_cache;
668 SILC_LOG_DEBUG(("Found"));
670 return id_cache->context;
673 /* Finds channel by Channel ID. */
676 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
677 SilcIDCacheEntry *ret_entry)
679 SilcIDCacheEntry id_cache = NULL;
680 SilcChannelEntry channel;
685 SILC_LOG_DEBUG(("Channel ID (%s)",
686 silc_id_render(id, SILC_ID_CHANNEL)));
688 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
691 channel = (SilcChannelEntry)id_cache->context;
694 *ret_entry = id_cache;
696 SILC_LOG_DEBUG(("Found"));
701 /* Replaces old Channel ID with new one. This is done when router forces
702 normal server to change Channel ID. */
705 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
706 SilcChannelID *new_id)
708 SilcIDCacheEntry id_cache = NULL;
709 SilcChannelEntry channel;
711 if (!old_id || !new_id)
714 SILC_LOG_DEBUG(("Replacing Channel ID"));
716 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
720 channel = (SilcChannelEntry)id_cache->context;
722 /* Remove the old entry and add a new one */
724 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
726 silc_free(channel->id);
727 channel->id = new_id;
729 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
732 SILC_LOG_DEBUG(("Replaced"));
737 /* Returns channels from the ID list. If the `channel_id' is NULL then
738 all channels are returned. */
741 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
742 uint32 *channels_count)
744 SilcIDCacheList list = NULL;
745 SilcIDCacheEntry id_cache = NULL;
746 SilcChannelEntry *channels = NULL;
749 SILC_LOG_DEBUG(("Start"));
752 if (!silc_idcache_get_all(id_list->channels, &list))
755 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
758 silc_idcache_list_first(list, &id_cache);
759 channels[i++] = (SilcChannelEntry)id_cache->context;
761 while (silc_idcache_list_next(list, &id_cache))
762 channels[i++] = (SilcChannelEntry)id_cache->context;
764 silc_idcache_list_free(list);
766 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
770 channels = silc_calloc(1, sizeof(*channels));
771 channels[0] = (SilcChannelEntry)id_cache->context;