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 SilcIDListPurge i = (SilcIDListPurge)context;
95 SILC_LOG_DEBUG(("Start"));
97 silc_idcache_purge(i->cache);
98 silc_schedule_task_add(i->schedule, 0,
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;
163 *ret_entry = id_cache;
165 if (server && registered &&
166 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
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;
191 *ret_entry = id_cache;
193 if (server && registered &&
194 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
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);
243 *ret_entry = id_cache;
245 if (server && registered &&
246 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
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)
293 SILC_LOG_DEBUG(("Start"));
296 /* Remove from cache */
297 if (!silc_idcache_del_by_context(id_list->servers, entry))
301 silc_free(entry->server_name);
302 silc_free(entry->id);
303 silc_free(entry->server_info);
305 memset(entry, 'F', sizeof(*entry));
313 /******************************************************************************
315 Client entry functions
317 ******************************************************************************/
319 /* Add new client entry. This adds the client entry to ID cache system
320 and returns the allocated client entry or NULL on error. This is
321 called when new client connection is accepted to the server. If The
322 `router' is provided then the all server routines assume that the client
323 is not directly connected local client but it has router set and is
324 remote. If this is the case then `connection' must be NULL. If, on the
325 other hand, the `connection' is provided then the client is assumed
326 to be directly connected local client and `router' must be NULL. */
329 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
330 char *userinfo, SilcClientID *id,
331 SilcServerEntry router, void *connection,
334 SilcClientEntry client;
336 SILC_LOG_DEBUG(("Adding new client entry"));
338 client = silc_calloc(1, sizeof(*client));
339 client->nickname = nickname;
340 client->username = username;
341 client->userinfo = userinfo;
343 client->router = router;
344 client->connection = connection;
345 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
346 NULL, NULL, NULL, NULL, TRUE);
348 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
349 (void *)client, expire, NULL)) {
350 silc_hash_table_free(client->channels);
358 /* Free client entry. This free's everything and removes the entry
359 from ID cache. Call silc_idlist_del_data before calling this one. */
361 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
363 SILC_LOG_DEBUG(("Start"));
366 /* Remove from cache */
367 if (!silc_idcache_del_by_context(id_list->clients, entry))
371 silc_free(entry->nickname);
372 silc_free(entry->servername);
373 silc_free(entry->username);
374 silc_free(entry->userinfo);
375 silc_free(entry->id);
376 silc_hash_table_free(entry->channels);
378 memset(entry, 'F', sizeof(*entry));
387 /* Returns all clients matching requested nickname. Number of clients is
388 returned to `clients_count'. Caller must free the returned table. */
390 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
392 SilcClientEntry **clients,
393 SilcUInt32 *clients_count)
395 SilcIDCacheList list = NULL;
396 SilcIDCacheEntry id_cache = NULL;
398 SILC_LOG_DEBUG(("Start"));
400 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
403 *clients = silc_realloc(*clients,
404 (silc_idcache_list_count(list) + *clients_count) *
407 silc_idcache_list_first(list, &id_cache);
408 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
410 while (silc_idcache_list_next(list, &id_cache))
411 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
413 silc_idcache_list_free(list);
415 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
420 /* Returns all clients matching requested nickname hash. Number of clients
421 is returned to `clients_count'. Caller must free the returned table. */
423 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
425 SilcClientEntry **clients,
426 SilcUInt32 *clients_count)
428 SilcIDCacheList list = NULL;
429 SilcIDCacheEntry id_cache = NULL;
430 unsigned char hash[32];
431 SilcClientID client_id;
434 SILC_LOG_DEBUG(("Start"));
436 memset(nick, 0, sizeof(nick));
437 silc_to_lower(nickname, nick, sizeof(nick) - 1);
438 silc_hash_make(md5hash, nick, strlen(nick), hash);
440 /* As the Client ID is hashed in the ID cache by hashing only the hash
441 from the Client ID, we can do a lookup with only the hash not the
442 other parts of the ID and get all the clients with that hash, ie.
443 with that nickname, as the hash is from the nickname. */
444 memset(&client_id, 0, sizeof(client_id));
445 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
446 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
449 *clients = silc_realloc(*clients,
450 (silc_idcache_list_count(list) + *clients_count) *
453 silc_idcache_list_first(list, &id_cache);
454 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
456 while (silc_idcache_list_next(list, &id_cache))
457 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
459 silc_idcache_list_free(list);
461 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
466 /* Finds client by Client ID */
469 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
470 bool registered, SilcIDCacheEntry *ret_entry)
472 SilcIDCacheEntry id_cache = NULL;
473 SilcClientEntry client;
478 SILC_LOG_DEBUG(("Client ID (%s)",
479 silc_id_render(id, SILC_ID_CLIENT)));
481 /* Do extended search since the normal ID comparison function for
482 Client ID's compares only the hash from the Client ID and not the
483 entire ID. The silc_hash_client_id_compare compares the entire
484 Client ID as we want to find one specific Client ID. */
485 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
487 silc_hash_client_id_compare, NULL,
491 client = (SilcClientEntry)id_cache->context;
494 *ret_entry = id_cache;
496 if (client && registered &&
497 !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
500 SILC_LOG_DEBUG(("Found"));
505 /* Replaces old Client ID with new one */
508 silc_idlist_replace_client_id(SilcServer server,
509 SilcIDList id_list, SilcClientID *old_id,
510 SilcClientID *new_id, const char *nickname)
512 SilcIDCacheEntry id_cache = NULL;
513 SilcClientEntry client;
515 if (!old_id || !new_id)
518 SILC_LOG_DEBUG(("Replacing Client ID"));
520 /* Do extended search since the normal ID comparison function for
521 Client ID's compares only the hash from the Client ID and not the
522 entire ID. The silc_hash_client_id_compare compares the entire
523 Client ID as we want to find one specific Client ID. */
524 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
526 silc_hash_client_id_compare, NULL,
530 client = (SilcClientEntry)id_cache->context;
532 /* Remove the old entry and add a new one */
534 if (!silc_idcache_del_by_context(id_list->clients, client))
537 /* Check if anyone is watching old nickname */
538 if (server->server_type == SILC_ROUTER)
539 silc_server_check_watcher_list(server, client, nickname,
540 SILC_NOTIFY_TYPE_NICK_CHANGE);
542 silc_free(client->id);
543 silc_free(client->nickname);
545 client->nickname = nickname ? strdup(nickname) : NULL;
547 /* Check if anyone is watching new nickname */
548 if (server->server_type == SILC_ROUTER)
549 silc_server_check_watcher_list(server, client, nickname,
550 SILC_NOTIFY_TYPE_NICK_CHANGE);
552 if (!silc_idcache_add(id_list->clients, client->nickname, client->id,
556 SILC_LOG_DEBUG(("Replaced"));
561 /* Client cache entry destructor that is called when the cache is purged. */
563 void silc_idlist_client_destructor(SilcIDCache cache,
564 SilcIDCacheEntry entry)
566 SilcClientEntry client;
568 client = (SilcClientEntry)entry->context;
570 silc_free(client->nickname);
571 silc_free(client->username);
572 silc_free(client->userinfo);
573 silc_free(client->id);
574 silc_hash_table_free(client->channels);
576 memset(client, 'A', sizeof(*client));
581 /******************************************************************************
583 Channel entry functions
585 ******************************************************************************/
587 /* Add new channel entry. This add the new channel entry to the ID cache
588 system and returns the allocated entry or NULL on error. */
591 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
592 SilcChannelID *id, SilcServerEntry router,
593 SilcCipher channel_key, SilcHmac hmac,
596 SilcChannelEntry channel;
598 SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
600 channel = silc_calloc(1, sizeof(*channel));
601 channel->channel_name = channel_name;
602 channel->mode = mode;
604 channel->router = router;
605 channel->channel_key = channel_key;
606 channel->hmac = hmac;
607 channel->created = time(0);
609 if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
614 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
615 NULL, NULL, NULL, TRUE);
617 if (!silc_idcache_add(id_list->channels, channel->channel_name,
618 (void *)channel->id, (void *)channel, expire, NULL)) {
619 silc_hmac_free(channel->hmac);
620 silc_hash_table_free(channel->user_list);
628 /* Foreach callbcak to free all users from the channel when deleting a
631 static void silc_idlist_del_channel_foreach(void *key, void *context,
634 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
636 SILC_LOG_DEBUG(("Removing client %s from channel %s",
637 chl->client->nickname, chl->channel->channel_name));
639 /* Remove the context from the client's channel hash table as that
640 table and channel's user_list hash table share this same context. */
641 silc_hash_table_del(chl->client->channels, chl->channel);
645 /* Free channel entry. This free's everything. */
647 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
650 SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
652 /* Remove from cache */
653 if (!silc_idcache_del_by_context(id_list->channels, entry))
656 /* Free all client entrys from the users list. The silc_hash_table_free
657 will free all the entries so they are not freed at the foreach
659 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
661 silc_hash_table_free(entry->user_list);
664 silc_free(entry->channel_name);
665 silc_free(entry->id);
666 silc_free(entry->topic);
667 if (entry->channel_key)
668 silc_cipher_free(entry->channel_key);
670 memset(entry->key, 0, entry->key_len / 8);
671 silc_free(entry->key);
673 silc_free(entry->cipher);
675 silc_hmac_free(entry->hmac);
676 silc_free(entry->hmac_name);
677 silc_free(entry->rekey);
678 if (entry->founder_key)
679 silc_pkcs_public_key_free(entry->founder_key);
681 memset(entry, 'F', sizeof(*entry));
689 /* Finds channel by channel name. Channel names are unique and they
690 are not case-sensitive. */
693 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
694 SilcIDCacheEntry *ret_entry)
696 SilcIDCacheEntry id_cache = NULL;
698 SILC_LOG_DEBUG(("Channel by name %s", name));
700 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
704 *ret_entry = id_cache;
706 SILC_LOG_DEBUG(("Found"));
708 return id_cache->context;
711 /* Finds channel by Channel ID. */
714 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
715 SilcIDCacheEntry *ret_entry)
717 SilcIDCacheEntry id_cache = NULL;
718 SilcChannelEntry channel;
723 SILC_LOG_DEBUG(("Channel ID (%s)",
724 silc_id_render(id, SILC_ID_CHANNEL)));
726 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
729 channel = (SilcChannelEntry)id_cache->context;
732 *ret_entry = id_cache;
734 SILC_LOG_DEBUG(("Found"));
739 /* Replaces old Channel ID with new one. This is done when router forces
740 normal server to change Channel ID. */
743 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
744 SilcChannelID *new_id)
746 SilcIDCacheEntry id_cache = NULL;
747 SilcChannelEntry channel;
749 if (!old_id || !new_id)
752 SILC_LOG_DEBUG(("Replacing Channel ID"));
754 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
758 channel = (SilcChannelEntry)id_cache->context;
760 /* Remove the old entry and add a new one */
762 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
764 silc_free(channel->id);
765 channel->id = new_id;
767 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
770 SILC_LOG_DEBUG(("Replaced"));
775 /* Returns channels from the ID list. If the `channel_id' is NULL then
776 all channels are returned. */
779 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
780 SilcUInt32 *channels_count)
782 SilcIDCacheList list = NULL;
783 SilcIDCacheEntry id_cache = NULL;
784 SilcChannelEntry *channels = NULL;
787 SILC_LOG_DEBUG(("Start"));
790 if (!silc_idcache_get_all(id_list->channels, &list))
793 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
796 silc_idcache_list_first(list, &id_cache);
797 channels[i++] = (SilcChannelEntry)id_cache->context;
799 while (silc_idcache_list_next(list, &id_cache))
800 channels[i++] = (SilcChannelEntry)id_cache->context;
802 silc_idcache_list_free(list);
804 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
808 channels = silc_calloc(1, sizeof(*channels));
809 channels[0] = (SilcChannelEntry)id_cache->context;