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->hmac_send = idata->hmac_send;
40 data->hmac_receive = idata->hmac_receive;
41 data->psn_send = idata->psn_send;
42 data->psn_receive = idata->psn_receive;
43 data->hash = idata->hash;
44 data->public_key = idata->public_key;
45 data->rekey = idata->rekey;
46 data->last_receive = idata->last_receive;
47 data->last_sent = idata->last_sent;
48 data->status = idata->status;
50 data->created = time(0); /* Update creation time */
53 /* Free's all data in the common ID entry data structure. */
55 void silc_idlist_del_data(void *entry)
57 SilcIDListData idata = (SilcIDListData)entry;
59 silc_cipher_free(idata->send_key);
60 if (idata->receive_key)
61 silc_cipher_free(idata->receive_key);
63 if (idata->rekey->send_enc_key) {
64 memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
65 silc_free(idata->rekey->send_enc_key);
67 silc_free(idata->rekey);
69 if (idata->hmac_send) /* Same as idata->hmac_receive */
70 silc_hmac_free(idata->hmac_send);
71 if (idata->public_key)
72 silc_pkcs_public_key_free(idata->public_key);
77 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
79 SilcIDListPurge i = (SilcIDListPurge)context;
81 SILC_LOG_DEBUG(("Start"));
83 silc_idcache_purge(i->cache);
84 silc_schedule_task_add(i->schedule, 0,
87 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
90 /******************************************************************************
92 Server entry functions
94 ******************************************************************************/
96 /* Add new server entry. This adds the new server entry to ID cache and
97 returns the allocated entry object or NULL on error. This is called
98 when new server connects to us. We also add ourselves to cache with
102 silc_idlist_add_server(SilcIDList id_list,
103 char *server_name, int server_type,
104 SilcServerID *id, SilcServerEntry router,
107 SilcServerEntry server;
109 SILC_LOG_DEBUG(("Adding new server entry"));
111 server = silc_calloc(1, sizeof(*server));
112 server->server_name = server_name;
113 server->server_type = server_type;
115 server->router = router;
116 server->connection = connection;
118 if (!silc_idcache_add(id_list->servers, server->server_name,
119 (void *)server->id, (void *)server, FALSE)) {
127 /* Finds server by Server ID */
130 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
131 bool registered, SilcIDCacheEntry *ret_entry)
133 SilcIDCacheEntry id_cache = NULL;
134 SilcServerEntry server;
139 SILC_LOG_DEBUG(("Server ID (%s)",
140 silc_id_render(id, SILC_ID_SERVER)));
142 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
146 server = (SilcServerEntry)id_cache->context;
149 *ret_entry = id_cache;
151 if (server && registered &&
152 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
155 SILC_LOG_DEBUG(("Found"));
160 /* Find server by name */
163 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
164 bool registered, SilcIDCacheEntry *ret_entry)
166 SilcIDCacheEntry id_cache = NULL;
167 SilcServerEntry server;
169 SILC_LOG_DEBUG(("Server by name `%s'", name));
171 if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
174 server = (SilcServerEntry)id_cache->context;
177 *ret_entry = id_cache;
179 if (server && registered &&
180 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
183 SILC_LOG_DEBUG(("Found"));
188 /* Find server by connection parameters, hostname and port */
191 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
192 int port, bool registered,
193 SilcIDCacheEntry *ret_entry)
195 SilcIDCacheList list = NULL;
196 SilcIDCacheEntry id_cache = NULL;
197 SilcServerEntry server = NULL;
198 SilcSocketConnection sock;
200 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
202 if (!silc_idcache_get_all(id_list->servers, &list))
205 if (!silc_idcache_list_first(list, &id_cache)) {
206 silc_idcache_list_free(list);
211 server = (SilcServerEntry)id_cache->context;
212 sock = (SilcSocketConnection)server->connection;
214 if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
215 (sock->ip && !strcasecmp(sock->ip, hostname)))
216 && sock->port == port)
222 if (!silc_idcache_list_next(list, &id_cache))
226 silc_idcache_list_free(list);
229 *ret_entry = id_cache;
231 if (server && registered &&
232 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
235 SILC_LOG_DEBUG(("Found"));
240 /* Replaces old Server ID with new one */
243 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
244 SilcServerID *new_id)
246 SilcIDCacheEntry id_cache = NULL;
247 SilcServerEntry server;
249 if (!old_id || !new_id)
252 SILC_LOG_DEBUG(("Replacing Server ID"));
254 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
258 server = (SilcServerEntry)id_cache->context;
260 /* Remove the old entry and add a new one */
262 silc_idcache_del_by_id(id_list->servers, (void *)server->id);
264 silc_free(server->id);
267 silc_idcache_add(id_list->servers, server->server_name, server->id,
270 SILC_LOG_DEBUG(("Found"));
275 /* Removes and free's server entry from ID list */
277 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
279 SILC_LOG_DEBUG(("Start"));
282 /* Remove from cache */
284 if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
288 silc_free(entry->server_name);
289 silc_free(entry->id);
291 memset(entry, 'F', sizeof(*entry));
299 /******************************************************************************
301 Client entry functions
303 ******************************************************************************/
305 /* Add new client entry. This adds the client entry to ID cache system
306 and returns the allocated client entry or NULL on error. This is
307 called when new client connection is accepted to the server. If The
308 `router' is provided then the all server routines assume that the client
309 is not directly connected local client but it has router set and is
310 remote. If this is the case then `connection' must be NULL. If, on the
311 other hand, the `connection' is provided then the client is assumed
312 to be directly connected local client and `router' must be NULL. */
315 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
316 char *userinfo, SilcClientID *id,
317 SilcServerEntry router, void *connection)
319 SilcClientEntry client;
321 SILC_LOG_DEBUG(("Adding new client entry"));
323 client = silc_calloc(1, sizeof(*client));
324 client->nickname = nickname;
325 client->username = username;
326 client->userinfo = userinfo;
328 client->router = router;
329 client->connection = connection;
330 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
331 NULL, NULL, NULL, NULL, TRUE);
333 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
334 (void *)client, FALSE)) {
335 silc_hash_table_free(client->channels);
343 /* Free client entry. This free's everything and removes the entry
344 from ID cache. Call silc_idlist_del_data before calling this one. */
346 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
348 SILC_LOG_DEBUG(("Start"));
351 /* Remove from cache */
353 if (!silc_idcache_del_by_context(id_list->clients, entry))
357 silc_free(entry->nickname);
358 silc_free(entry->username);
359 silc_free(entry->userinfo);
360 silc_free(entry->id);
362 memset(entry, 'F', sizeof(*entry));
371 /* Returns all clients matching requested nickname. Number of clients is
372 returned to `clients_count'. Caller must free the returned table. */
374 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
376 SilcClientEntry **clients,
377 uint32 *clients_count)
379 SilcIDCacheList list = NULL;
380 SilcIDCacheEntry id_cache = NULL;
382 SILC_LOG_DEBUG(("Start"));
384 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
387 *clients = silc_realloc(*clients,
388 (silc_idcache_list_count(list) + *clients_count) *
391 silc_idcache_list_first(list, &id_cache);
392 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
394 while (silc_idcache_list_next(list, &id_cache))
395 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
397 silc_idcache_list_free(list);
399 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
404 /* Returns all clients matching requested nickname hash. Number of clients
405 is returned to `clients_count'. Caller must free the returned table. */
407 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
409 SilcClientEntry **clients,
410 uint32 *clients_count)
412 SilcIDCacheList list = NULL;
413 SilcIDCacheEntry id_cache = NULL;
414 unsigned char hash[32];
415 SilcClientID client_id;
417 SILC_LOG_DEBUG(("Start"));
419 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
421 /* As the Client ID is hashed in the ID cache by hashing only the hash
422 from the Client ID, we can do a lookup with only the hash not the
423 other parts of the ID and get all the clients with that hash, ie.
424 with that nickname, as the hash is from the nickname. */
425 memset(&client_id, 0, sizeof(client_id));
426 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
427 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
430 *clients = silc_realloc(*clients,
431 (silc_idcache_list_count(list) + *clients_count) *
434 silc_idcache_list_first(list, &id_cache);
435 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
437 while (silc_idcache_list_next(list, &id_cache))
438 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
440 silc_idcache_list_free(list);
442 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
447 /* Finds client by Client ID */
450 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
451 bool registered, SilcIDCacheEntry *ret_entry)
453 SilcIDCacheEntry id_cache = NULL;
454 SilcClientEntry client;
459 SILC_LOG_DEBUG(("Client ID (%s)",
460 silc_id_render(id, SILC_ID_CLIENT)));
462 /* Do extended search since the normal ID comparison function for
463 Client ID's compares only the hash from the Client ID and not the
464 entire ID. The silc_hash_client_id_compare compares the entire
465 Client ID as we want to find one specific Client ID. */
466 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
468 silc_hash_client_id_compare, NULL,
472 client = (SilcClientEntry)id_cache->context;
475 *ret_entry = id_cache;
477 if (client && registered &&
478 !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
481 SILC_LOG_DEBUG(("Found"));
486 /* Replaces old Client ID with new one */
489 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
490 SilcClientID *new_id)
492 SilcIDCacheEntry id_cache = NULL;
493 SilcClientEntry client;
495 if (!old_id || !new_id)
498 SILC_LOG_DEBUG(("Replacing Client ID"));
500 /* Do extended search since the normal ID comparison function for
501 Client ID's compares only the hash from the Client ID and not the
502 entire ID. The silc_hash_client_id_compare compares the entire
503 Client ID as we want to find one specific Client ID. */
504 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
506 silc_hash_client_id_compare, NULL,
510 client = (SilcClientEntry)id_cache->context;
512 /* Remove the old entry and add a new one */
514 silc_idcache_del_by_context(id_list->clients, client);
516 silc_free(client->id);
519 silc_idcache_add(id_list->clients, NULL, client->id, client, FALSE);
521 SILC_LOG_DEBUG(("Replaced"));
526 /* Client cache entry destructor that is called when the cache is purged. */
528 void silc_idlist_client_destructor(SilcIDCache cache,
529 SilcIDCacheEntry entry)
531 SilcClientEntry client;
533 SILC_LOG_DEBUG(("Start"));
535 client = (SilcClientEntry)entry->context;
537 if (client->nickname)
538 silc_free(client->nickname);
539 if (client->username)
540 silc_free(client->username);
541 if (client->userinfo)
542 silc_free(client->userinfo);
544 silc_free(client->id);
546 memset(client, 'F', sizeof(*client));
551 /******************************************************************************
553 Channel entry functions
555 ******************************************************************************/
557 /* Add new channel entry. This add the new channel entry to the ID cache
558 system and returns the allocated entry or NULL on error. */
561 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
562 SilcChannelID *id, SilcServerEntry router,
563 SilcCipher channel_key, SilcHmac hmac)
565 SilcChannelEntry channel;
567 SILC_LOG_DEBUG(("Adding new channel entry"));
569 channel = silc_calloc(1, sizeof(*channel));
570 channel->channel_name = channel_name;
571 channel->mode = mode;
573 channel->router = router;
574 channel->channel_key = channel_key;
575 channel->hmac = hmac;
576 channel->created = time(0);
578 if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
583 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
584 NULL, NULL, NULL, TRUE);
586 if (!silc_idcache_add(id_list->channels, channel->channel_name,
587 (void *)channel->id, (void *)channel, FALSE)) {
588 silc_hmac_free(channel->hmac);
589 silc_hash_table_free(channel->user_list);
597 /* Foreach callbcak to free all users from the channel when deleting a
600 static void silc_idlist_del_channel_foreach(void *key, void *context,
603 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
605 /* Remove the context from the client's channel hash table as that
606 table and channel's user_list hash table share this same context. */
607 silc_hash_table_del(chl->client->channels, chl->channel);
611 /* Free channel entry. This free's everything. */
613 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
615 SILC_LOG_DEBUG(("Start"));
618 /* Remove from cache */
620 if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
624 silc_free(entry->channel_name);
625 silc_free(entry->id);
626 silc_free(entry->topic);
627 if (entry->channel_key)
628 silc_cipher_free(entry->channel_key);
630 memset(entry->key, 0, entry->key_len / 8);
631 silc_free(entry->key);
633 silc_free(entry->cipher);
634 silc_free(entry->hmac_name);
635 silc_free(entry->rekey);
637 /* Free all client entrys from the users list. The silc_hash_table_free
638 will free all the entries so they are not freed at the foreach
640 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
642 silc_hash_table_free(entry->user_list);
644 memset(entry, 'F', sizeof(*entry));
652 /* Finds channel by channel name. Channel names are unique and they
653 are not case-sensitive. */
656 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
657 SilcIDCacheEntry *ret_entry)
659 SilcIDCacheEntry id_cache = NULL;
661 SILC_LOG_DEBUG(("Channel by name"));
663 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
667 *ret_entry = id_cache;
669 SILC_LOG_DEBUG(("Found"));
671 return id_cache->context;
674 /* Finds channel by Channel ID. */
677 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
678 SilcIDCacheEntry *ret_entry)
680 SilcIDCacheEntry id_cache = NULL;
681 SilcChannelEntry channel;
686 SILC_LOG_DEBUG(("Channel ID (%s)",
687 silc_id_render(id, SILC_ID_CHANNEL)));
689 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
692 channel = (SilcChannelEntry)id_cache->context;
695 *ret_entry = id_cache;
697 SILC_LOG_DEBUG(("Found"));
702 /* Replaces old Channel ID with new one. This is done when router forces
703 normal server to change Channel ID. */
706 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
707 SilcChannelID *new_id)
709 SilcIDCacheEntry id_cache = NULL;
710 SilcChannelEntry channel;
712 if (!old_id || !new_id)
715 SILC_LOG_DEBUG(("Replacing Channel ID"));
717 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
721 channel = (SilcChannelEntry)id_cache->context;
723 /* Remove the old entry and add a new one */
725 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
727 silc_free(channel->id);
728 channel->id = new_id;
730 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
733 SILC_LOG_DEBUG(("Replaced"));
738 /* Returns channels from the ID list. If the `channel_id' is NULL then
739 all channels are returned. */
742 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
743 uint32 *channels_count)
745 SilcIDCacheList list = NULL;
746 SilcIDCacheEntry id_cache = NULL;
747 SilcChannelEntry *channels = NULL;
750 SILC_LOG_DEBUG(("Start"));
753 if (!silc_idcache_get_all(id_list->channels, &list))
756 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
759 silc_idcache_list_first(list, &id_cache);
760 channels[i++] = (SilcChannelEntry)id_cache->context;
762 while (silc_idcache_list_next(list, &id_cache))
763 channels[i++] = (SilcChannelEntry)id_cache->context;
765 silc_idcache_list_free(list);
767 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
771 channels = silc_calloc(1, sizeof(*channels));
772 channels[0] = (SilcChannelEntry)id_cache->context;