5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2002 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"
24 #include "server_internal.h"
26 /******************************************************************************
30 ******************************************************************************/
32 /* This function is used to add keys and stuff to common ID entry data
35 void silc_idlist_add_data(void *entry, SilcIDListData idata)
37 SilcIDListData data = (SilcIDListData)entry;
38 data->send_key = idata->send_key;
39 data->receive_key = idata->receive_key;
40 data->hmac_send = idata->hmac_send;
41 data->hmac_receive = idata->hmac_receive;
42 data->psn_send = idata->psn_send;
43 data->psn_receive = idata->psn_receive;
44 data->hash = idata->hash;
45 data->public_key = idata->public_key;
46 memcpy(data->fingerprint, idata->fingerprint, sizeof(data->fingerprint));
47 data->rekey = idata->rekey;
48 data->last_receive = idata->last_receive;
49 data->last_sent = idata->last_sent;
50 data->status = idata->status;
52 data->created = time(0); /* Update creation time */
55 /* Free's all data in the common ID entry data structure. */
57 void silc_idlist_del_data(void *entry)
59 SilcIDListData idata = (SilcIDListData)entry;
61 silc_cipher_free(idata->send_key);
62 if (idata->receive_key)
63 silc_cipher_free(idata->receive_key);
65 if (idata->rekey->send_enc_key) {
66 memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
67 silc_free(idata->rekey->send_enc_key);
69 silc_free(idata->rekey);
72 silc_hmac_free(idata->hmac_send);
73 if (idata->hmac_receive)
74 silc_hmac_free(idata->hmac_receive);
76 silc_hash_free(idata->hash);
77 if (idata->public_key)
78 silc_pkcs_public_key_free(idata->public_key);
80 idata->send_key = NULL;
81 idata->receive_key = NULL;
83 idata->hmac_send = NULL;
84 idata->hmac_receive = NULL;
86 idata->public_key = NULL;
91 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
93 SilcServer server = app_context;
94 SilcIDListPurge i = (SilcIDListPurge)context;
96 SILC_LOG_DEBUG(("Purging cache"));
98 silc_idcache_purge(i->cache);
99 silc_schedule_task_add(server->schedule, 0, silc_idlist_purge,
100 (void *)i, i->timeout, 0,
101 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
104 /******************************************************************************
106 Server entry functions
108 ******************************************************************************/
110 /* Add new server entry. This adds the new server entry to ID cache and
111 returns the allocated entry object or NULL on error. This is called
112 when new server connects to us. We also add ourselves to cache with
116 silc_idlist_add_server(SilcIDList id_list,
117 char *server_name, int server_type,
118 SilcServerID *id, SilcServerEntry router,
121 SilcServerEntry server;
123 SILC_LOG_DEBUG(("Adding new server entry"));
125 server = silc_calloc(1, sizeof(*server));
126 server->server_name = server_name;
127 server->server_type = server_type;
129 server->router = router;
130 server->connection = connection;
132 if (!silc_idcache_add(id_list->servers, server->server_name,
133 (void *)server->id, (void *)server, 0, NULL)) {
141 /* Finds server by Server ID */
144 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
145 bool registered, SilcIDCacheEntry *ret_entry)
147 SilcIDCacheEntry id_cache = NULL;
148 SilcServerEntry server;
153 SILC_LOG_DEBUG(("Server ID (%s)",
154 silc_id_render(id, SILC_ID_SERVER)));
156 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
160 server = (SilcServerEntry)id_cache->context;
162 if (server && registered &&
163 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
167 *ret_entry = id_cache;
169 SILC_LOG_DEBUG(("Found"));
174 /* Find server by name */
177 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
178 bool registered, SilcIDCacheEntry *ret_entry)
180 SilcIDCacheEntry id_cache = NULL;
181 SilcServerEntry server;
183 SILC_LOG_DEBUG(("Server by name `%s'", name));
185 if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
188 server = (SilcServerEntry)id_cache->context;
190 if (server && registered &&
191 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
195 *ret_entry = id_cache;
197 SILC_LOG_DEBUG(("Found"));
202 /* Find server by connection parameters, hostname and port */
205 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
206 int port, bool registered,
207 SilcIDCacheEntry *ret_entry)
209 SilcIDCacheList list = NULL;
210 SilcIDCacheEntry id_cache = NULL;
211 SilcServerEntry server = NULL;
212 SilcSocketConnection sock;
214 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
216 if (!silc_idcache_get_all(id_list->servers, &list))
219 if (!silc_idcache_list_first(list, &id_cache)) {
220 silc_idcache_list_free(list);
225 server = (SilcServerEntry)id_cache->context;
226 sock = (SilcSocketConnection)server->connection;
228 if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
229 (sock->ip && !strcasecmp(sock->ip, hostname)))
230 && sock->port == port)
236 if (!silc_idcache_list_next(list, &id_cache))
240 silc_idcache_list_free(list);
242 if (server && registered &&
243 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
247 *ret_entry = id_cache;
249 SILC_LOG_DEBUG(("Found"));
254 /* Replaces old Server ID with new one */
257 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
258 SilcServerID *new_id)
260 SilcIDCacheEntry id_cache = NULL;
261 SilcServerEntry server;
263 if (!old_id || !new_id)
266 SILC_LOG_DEBUG(("Replacing Server ID"));
268 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
272 server = (SilcServerEntry)id_cache->context;
274 /* Remove the old entry and add a new one */
276 silc_idcache_del_by_id(id_list->servers, (void *)server->id);
278 silc_free(server->id);
281 silc_idcache_add(id_list->servers, server->server_name, server->id,
284 SILC_LOG_DEBUG(("Found"));
289 /* Removes and free's server entry from ID list */
291 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
294 /* Remove from cache */
295 if (!silc_idcache_del_by_context(id_list->servers, entry))
298 SILC_LOG_DEBUG(("Deleting server %s", entry->server_name ?
299 entry->server_name : ""));
302 silc_free(entry->server_name);
303 silc_free(entry->id);
304 silc_free(entry->server_info);
306 memset(entry, 'F', sizeof(*entry));
314 /******************************************************************************
316 Client entry functions
318 ******************************************************************************/
320 /* Add new client entry. This adds the client entry to ID cache system
321 and returns the allocated client entry or NULL on error. This is
322 called when new client connection is accepted to the server. If The
323 `router' is provided then the all server routines assume that the client
324 is not directly connected local client but it has router set and is
325 remote. If this is the case then `connection' must be NULL. If, on the
326 other hand, the `connection' is provided then the client is assumed
327 to be directly connected local client and `router' must be NULL. */
330 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
331 char *userinfo, SilcClientID *id,
332 SilcServerEntry router, void *connection,
335 SilcClientEntry client;
337 SILC_LOG_DEBUG(("Adding new client entry"));
339 client = silc_calloc(1, sizeof(*client));
340 client->nickname = nickname;
341 client->username = username;
342 client->userinfo = userinfo;
344 client->router = router;
345 client->connection = connection;
346 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
347 NULL, NULL, NULL, NULL, TRUE);
349 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
350 (void *)client, expire, NULL)) {
351 silc_hash_table_free(client->channels);
359 /* Free client entry. This free's everything and removes the entry
360 from ID cache. Call silc_idlist_del_data before calling this one. */
362 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
364 SILC_LOG_DEBUG(("Start"));
367 /* Remove from cache */
368 if (!silc_idcache_del_by_context(id_list->clients, entry))
371 assert(!silc_hash_table_count(entry->channels));
374 silc_free(entry->nickname);
375 silc_free(entry->servername);
376 silc_free(entry->username);
377 silc_free(entry->userinfo);
378 silc_free(entry->id);
379 silc_free(entry->attrs);
380 silc_hash_table_free(entry->channels);
382 memset(entry, 'F', sizeof(*entry));
391 /* Returns all clients matching requested nickname. Number of clients is
392 returned to `clients_count'. Caller must free the returned table. */
394 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
396 SilcClientEntry **clients,
397 SilcUInt32 *clients_count)
399 SilcIDCacheList list = NULL;
400 SilcIDCacheEntry id_cache = NULL;
402 SILC_LOG_DEBUG(("Start"));
404 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
407 *clients = silc_realloc(*clients,
408 (silc_idcache_list_count(list) + *clients_count) *
411 silc_idcache_list_first(list, &id_cache);
412 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
414 while (silc_idcache_list_next(list, &id_cache))
415 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
417 silc_idcache_list_free(list);
419 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
424 /* Returns all clients matching requested nickname hash. Number of clients
425 is returned to `clients_count'. Caller must free the returned table. */
427 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
429 SilcClientEntry **clients,
430 SilcUInt32 *clients_count)
432 SilcIDCacheList list = NULL;
433 SilcIDCacheEntry id_cache = NULL;
434 unsigned char hash[32];
435 SilcClientID client_id;
438 SILC_LOG_DEBUG(("Start"));
440 memset(nick, 0, sizeof(nick));
441 silc_to_lower(nickname, nick, sizeof(nick) - 1);
442 silc_hash_make(md5hash, nick, strlen(nick), hash);
444 /* As the Client ID is hashed in the ID cache by hashing only the hash
445 from the Client ID, we can do a lookup with only the hash not the
446 other parts of the ID and get all the clients with that hash, ie.
447 with that nickname, as the hash is from the nickname. */
448 memset(&client_id, 0, sizeof(client_id));
449 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
450 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
453 *clients = silc_realloc(*clients,
454 (silc_idcache_list_count(list) + *clients_count) *
457 silc_idcache_list_first(list, &id_cache);
458 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
460 while (silc_idcache_list_next(list, &id_cache))
461 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
463 silc_idcache_list_free(list);
465 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
470 /* Finds client by Client ID */
473 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
474 bool registered, SilcIDCacheEntry *ret_entry)
476 SilcIDCacheEntry id_cache = NULL;
477 SilcClientEntry client;
482 SILC_LOG_DEBUG(("Client ID (%s)",
483 silc_id_render(id, SILC_ID_CLIENT)));
485 /* Do extended search since the normal ID comparison function for
486 Client ID's compares only the hash from the Client ID and not the
487 entire ID. The silc_hash_client_id_compare compares the entire
488 Client ID as we want to find one specific Client ID. */
489 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
491 silc_hash_client_id_compare, NULL,
495 client = (SilcClientEntry)id_cache->context;
497 if (client && registered &&
498 !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
502 *ret_entry = id_cache;
504 SILC_LOG_DEBUG(("Found"));
509 /* Replaces old Client ID with new one */
512 silc_idlist_replace_client_id(SilcServer server,
513 SilcIDList id_list, SilcClientID *old_id,
514 SilcClientID *new_id, const char *nickname)
516 SilcIDCacheEntry id_cache = NULL;
517 SilcClientEntry client;
519 if (!old_id || !new_id)
522 SILC_LOG_DEBUG(("Replacing Client ID"));
524 /* Do extended search since the normal ID comparison function for
525 Client ID's compares only the hash from the Client ID and not the
526 entire ID. The silc_hash_client_id_compare compares the entire
527 Client ID as we want to find one specific Client ID. */
528 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
530 silc_hash_client_id_compare, NULL,
534 client = (SilcClientEntry)id_cache->context;
536 /* Remove the old entry and add a new one */
538 if (!silc_idcache_del_by_context(id_list->clients, client))
541 /* Check if anyone is watching old nickname */
542 if (server->server_type == SILC_ROUTER)
543 silc_server_check_watcher_list(server, client, nickname,
544 SILC_NOTIFY_TYPE_NICK_CHANGE);
546 silc_free(client->id);
547 silc_free(client->nickname);
549 client->nickname = nickname ? strdup(nickname) : NULL;
551 /* Check if anyone is watching new nickname */
552 if (server->server_type == SILC_ROUTER)
553 silc_server_check_watcher_list(server, client, nickname,
554 SILC_NOTIFY_TYPE_NICK_CHANGE);
556 if (!silc_idcache_add(id_list->clients, client->nickname, client->id,
560 SILC_LOG_DEBUG(("Replaced"));
565 /* Client cache entry destructor that is called when the cache is purged. */
567 void silc_idlist_client_destructor(SilcIDCache cache,
568 SilcIDCacheEntry entry)
570 SilcClientEntry client;
572 client = (SilcClientEntry)entry->context;
574 assert(!silc_hash_table_count(client->channels));
575 silc_free(client->nickname);
576 silc_free(client->username);
577 silc_free(client->userinfo);
578 silc_free(client->id);
579 silc_free(client->attrs);
580 silc_hash_table_free(client->channels);
582 memset(client, 'A', sizeof(*client));
587 /******************************************************************************
589 Channel entry functions
591 ******************************************************************************/
593 /* Add new channel entry. This add the new channel entry to the ID cache
594 system and returns the allocated entry or NULL on error. */
597 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
598 SilcChannelID *id, SilcServerEntry router,
599 SilcCipher channel_key, SilcHmac hmac,
602 SilcChannelEntry channel;
604 SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
606 channel = silc_calloc(1, sizeof(*channel));
607 channel->channel_name = channel_name;
608 channel->mode = mode;
610 channel->router = router;
611 channel->channel_key = channel_key;
612 channel->hmac = hmac;
613 channel->created = channel->updated = time(0);
615 if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
620 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
621 NULL, NULL, NULL, TRUE);
623 if (!silc_idcache_add(id_list->channels, channel->channel_name,
624 (void *)channel->id, (void *)channel, expire, NULL)) {
625 silc_hmac_free(channel->hmac);
626 silc_hash_table_free(channel->user_list);
634 /* Foreach callbcak to free all users from the channel when deleting a
637 static void silc_idlist_del_channel_foreach(void *key, void *context,
640 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
642 SILC_LOG_DEBUG(("Removing client %s from channel %s",
643 chl->client->nickname ? chl->client->nickname :
644 (unsigned char *)"", chl->channel->channel_name));
646 /* Remove the context from the client's channel hash table as that
647 table and channel's user_list hash table share this same context. */
648 silc_hash_table_del(chl->client->channels, chl->channel);
652 /* Free channel entry. This free's everything. */
654 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
657 SilcHashTableList htl;
661 /* Remove from cache */
662 if (!silc_idcache_del_by_context(id_list->channels, entry))
665 SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
667 /* Free all client entrys from the users list. The silc_hash_table_free
668 will free all the entries so they are not freed at the foreach
670 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
672 silc_hash_table_free(entry->user_list);
675 silc_free(entry->channel_name);
676 silc_free(entry->id);
677 silc_free(entry->topic);
679 if (entry->invite_list) {
680 silc_hash_table_list(entry->invite_list, &htl);
681 while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp)) {
683 silc_free((char *)tmp);
686 silc_buffer_free(tmp);
688 silc_hash_table_list_reset(&htl);
689 silc_hash_table_free(entry->invite_list);
692 if (entry->ban_list) {
693 silc_hash_table_list(entry->ban_list, &htl);
694 while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp)) {
696 silc_free((char *)tmp);
699 silc_buffer_free(tmp);
701 silc_hash_table_list_reset(&htl);
702 silc_hash_table_free(entry->ban_list);
705 if (entry->channel_key)
706 silc_cipher_free(entry->channel_key);
708 memset(entry->key, 0, entry->key_len / 8);
709 silc_free(entry->key);
711 silc_free(entry->cipher);
713 silc_hmac_free(entry->hmac);
714 silc_free(entry->hmac_name);
715 silc_free(entry->rekey);
716 if (entry->founder_key)
717 silc_pkcs_public_key_free(entry->founder_key);
719 memset(entry, 'F', sizeof(*entry));
727 /* Finds channel by channel name. Channel names are unique and they
728 are not case-sensitive. */
731 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
732 SilcIDCacheEntry *ret_entry)
734 SilcIDCacheEntry id_cache = NULL;
736 SILC_LOG_DEBUG(("Channel by name %s", name));
738 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
742 *ret_entry = id_cache;
744 SILC_LOG_DEBUG(("Found"));
747 ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
749 return id_cache->context;
752 /* Finds channel by Channel ID. */
755 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
756 SilcIDCacheEntry *ret_entry)
758 SilcIDCacheEntry id_cache = NULL;
759 SilcChannelEntry channel;
764 SILC_LOG_DEBUG(("Channel ID (%s)",
765 silc_id_render(id, SILC_ID_CHANNEL)));
767 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
770 channel = (SilcChannelEntry)id_cache->context;
773 *ret_entry = id_cache;
775 SILC_LOG_DEBUG(("Found"));
778 channel->updated = time(NULL);
783 /* Replaces old Channel ID with new one. This is done when router forces
784 normal server to change Channel ID. */
787 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
788 SilcChannelID *new_id)
790 SilcIDCacheEntry id_cache = NULL;
791 SilcChannelEntry channel;
793 if (!old_id || !new_id)
796 SILC_LOG_DEBUG(("Replacing Channel ID"));
798 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
802 channel = (SilcChannelEntry)id_cache->context;
804 /* Remove the old entry and add a new one */
806 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
808 silc_free(channel->id);
809 channel->id = new_id;
811 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
814 SILC_LOG_DEBUG(("Replaced"));
817 channel->updated = time(NULL);
822 /* Returns channels from the ID list. If the `channel_id' is NULL then
823 all channels are returned. */
826 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
827 SilcUInt32 *channels_count)
829 SilcIDCacheList list = NULL;
830 SilcIDCacheEntry id_cache = NULL;
831 SilcChannelEntry *channels = NULL;
834 SILC_LOG_DEBUG(("Start"));
837 if (!silc_idcache_get_all(id_list->channels, &list))
840 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
843 silc_idcache_list_first(list, &id_cache);
844 channels[i++] = (SilcChannelEntry)id_cache->context;
846 while (silc_idcache_list_next(list, &id_cache))
847 channels[i++] = (SilcChannelEntry)id_cache->context;
849 silc_idcache_list_free(list);
851 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
855 channels = silc_calloc(1, sizeof(*channels));
856 channels[0] = (SilcChannelEntry)id_cache->context;