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);
83 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
85 SilcIDListPurge i = (SilcIDListPurge)context;
87 SILC_LOG_DEBUG(("Start"));
89 silc_idcache_purge(i->cache);
90 silc_schedule_task_add(i->schedule, 0,
92 (void *)i, i->timeout, 0,
93 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
96 /******************************************************************************
98 Server entry functions
100 ******************************************************************************/
102 /* Add new server entry. This adds the new server entry to ID cache and
103 returns the allocated entry object or NULL on error. This is called
104 when new server connects to us. We also add ourselves to cache with
108 silc_idlist_add_server(SilcIDList id_list,
109 char *server_name, int server_type,
110 SilcServerID *id, SilcServerEntry router,
113 SilcServerEntry server;
115 SILC_LOG_DEBUG(("Adding new server entry"));
117 server = silc_calloc(1, sizeof(*server));
118 server->server_name = server_name;
119 server->server_type = server_type;
121 server->router = router;
122 server->connection = connection;
124 if (!silc_idcache_add(id_list->servers, server->server_name,
125 (void *)server->id, (void *)server, 0, NULL)) {
133 /* Finds server by Server ID */
136 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
137 bool registered, SilcIDCacheEntry *ret_entry)
139 SilcIDCacheEntry id_cache = NULL;
140 SilcServerEntry server;
145 SILC_LOG_DEBUG(("Server ID (%s)",
146 silc_id_render(id, SILC_ID_SERVER)));
148 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
152 server = (SilcServerEntry)id_cache->context;
155 *ret_entry = id_cache;
157 if (server && registered &&
158 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
161 SILC_LOG_DEBUG(("Found"));
166 /* Find server by name */
169 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
170 bool registered, SilcIDCacheEntry *ret_entry)
172 SilcIDCacheEntry id_cache = NULL;
173 SilcServerEntry server;
175 SILC_LOG_DEBUG(("Server by name `%s'", name));
177 if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
180 server = (SilcServerEntry)id_cache->context;
183 *ret_entry = id_cache;
185 if (server && registered &&
186 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
189 SILC_LOG_DEBUG(("Found"));
194 /* Find server by connection parameters, hostname and port */
197 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
198 int port, bool registered,
199 SilcIDCacheEntry *ret_entry)
201 SilcIDCacheList list = NULL;
202 SilcIDCacheEntry id_cache = NULL;
203 SilcServerEntry server = NULL;
204 SilcSocketConnection sock;
206 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
208 if (!silc_idcache_get_all(id_list->servers, &list))
211 if (!silc_idcache_list_first(list, &id_cache)) {
212 silc_idcache_list_free(list);
217 server = (SilcServerEntry)id_cache->context;
218 sock = (SilcSocketConnection)server->connection;
220 if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
221 (sock->ip && !strcasecmp(sock->ip, hostname)))
222 && sock->port == port)
228 if (!silc_idcache_list_next(list, &id_cache))
232 silc_idcache_list_free(list);
235 *ret_entry = id_cache;
237 if (server && registered &&
238 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
241 SILC_LOG_DEBUG(("Found"));
246 /* Replaces old Server ID with new one */
249 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
250 SilcServerID *new_id)
252 SilcIDCacheEntry id_cache = NULL;
253 SilcServerEntry server;
255 if (!old_id || !new_id)
258 SILC_LOG_DEBUG(("Replacing Server ID"));
260 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
264 server = (SilcServerEntry)id_cache->context;
266 /* Remove the old entry and add a new one */
268 silc_idcache_del_by_id(id_list->servers, (void *)server->id);
270 silc_free(server->id);
273 silc_idcache_add(id_list->servers, server->server_name, server->id,
276 SILC_LOG_DEBUG(("Found"));
281 /* Removes and free's server entry from ID list */
283 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
285 SILC_LOG_DEBUG(("Start"));
288 /* Remove from cache */
290 if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
294 silc_free(entry->server_name);
295 silc_free(entry->id);
297 memset(entry, 'F', sizeof(*entry));
305 /******************************************************************************
307 Client entry functions
309 ******************************************************************************/
311 /* Add new client entry. This adds the client entry to ID cache system
312 and returns the allocated client entry or NULL on error. This is
313 called when new client connection is accepted to the server. If The
314 `router' is provided then the all server routines assume that the client
315 is not directly connected local client but it has router set and is
316 remote. If this is the case then `connection' must be NULL. If, on the
317 other hand, the `connection' is provided then the client is assumed
318 to be directly connected local client and `router' must be NULL. */
321 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
322 char *userinfo, SilcClientID *id,
323 SilcServerEntry router, void *connection,
326 SilcClientEntry client;
328 SILC_LOG_DEBUG(("Adding new client entry"));
330 client = silc_calloc(1, sizeof(*client));
331 client->nickname = nickname;
332 client->username = username;
333 client->userinfo = userinfo;
335 client->router = router;
336 client->connection = connection;
337 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
338 NULL, NULL, NULL, NULL, TRUE);
340 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
341 (void *)client, expire, NULL)) {
342 silc_hash_table_free(client->channels);
350 /* Free client entry. This free's everything and removes the entry
351 from ID cache. Call silc_idlist_del_data before calling this one. */
353 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
355 SILC_LOG_DEBUG(("Start"));
358 /* Remove from cache */
360 if (!silc_idcache_del_by_context(id_list->clients, entry))
364 silc_free(entry->nickname);
365 silc_free(entry->username);
366 silc_free(entry->userinfo);
367 silc_free(entry->id);
368 silc_hash_table_free(entry->channels);
370 memset(entry, 'F', sizeof(*entry));
379 /* Returns all clients matching requested nickname. Number of clients is
380 returned to `clients_count'. Caller must free the returned table. */
382 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
384 SilcClientEntry **clients,
385 SilcUInt32 *clients_count)
387 SilcIDCacheList list = NULL;
388 SilcIDCacheEntry id_cache = NULL;
390 SILC_LOG_DEBUG(("Start"));
392 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
395 *clients = silc_realloc(*clients,
396 (silc_idcache_list_count(list) + *clients_count) *
399 silc_idcache_list_first(list, &id_cache);
400 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
402 while (silc_idcache_list_next(list, &id_cache))
403 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
405 silc_idcache_list_free(list);
407 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
412 /* Returns all clients matching requested nickname hash. Number of clients
413 is returned to `clients_count'. Caller must free the returned table. */
415 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
417 SilcClientEntry **clients,
418 SilcUInt32 *clients_count)
420 SilcIDCacheList list = NULL;
421 SilcIDCacheEntry id_cache = NULL;
422 unsigned char hash[32];
423 SilcClientID client_id;
426 SILC_LOG_DEBUG(("Start"));
428 memset(nick, 0, sizeof(nick));
429 silc_to_lower(nickname, nick, sizeof(nick) - 1);
430 silc_hash_make(md5hash, nick, strlen(nick), hash);
432 /* As the Client ID is hashed in the ID cache by hashing only the hash
433 from the Client ID, we can do a lookup with only the hash not the
434 other parts of the ID and get all the clients with that hash, ie.
435 with that nickname, as the hash is from the nickname. */
436 memset(&client_id, 0, sizeof(client_id));
437 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
438 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
441 *clients = silc_realloc(*clients,
442 (silc_idcache_list_count(list) + *clients_count) *
445 silc_idcache_list_first(list, &id_cache);
446 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
448 while (silc_idcache_list_next(list, &id_cache))
449 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
451 silc_idcache_list_free(list);
453 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
458 /* Finds client by Client ID */
461 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
462 bool registered, SilcIDCacheEntry *ret_entry)
464 SilcIDCacheEntry id_cache = NULL;
465 SilcClientEntry client;
470 SILC_LOG_DEBUG(("Client ID (%s)",
471 silc_id_render(id, SILC_ID_CLIENT)));
473 /* Do extended search since the normal ID comparison function for
474 Client ID's compares only the hash from the Client ID and not the
475 entire ID. The silc_hash_client_id_compare compares the entire
476 Client ID as we want to find one specific Client ID. */
477 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
479 silc_hash_client_id_compare, NULL,
483 client = (SilcClientEntry)id_cache->context;
486 *ret_entry = id_cache;
488 if (client && registered &&
489 !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
492 SILC_LOG_DEBUG(("Found"));
497 /* Replaces old Client ID with new one */
500 silc_idlist_replace_client_id(SilcServer server,
501 SilcIDList id_list, SilcClientID *old_id,
502 SilcClientID *new_id, const char *nickname)
504 SilcIDCacheEntry id_cache = NULL;
505 SilcClientEntry client;
507 if (!old_id || !new_id)
510 SILC_LOG_DEBUG(("Replacing Client ID"));
512 /* Do extended search since the normal ID comparison function for
513 Client ID's compares only the hash from the Client ID and not the
514 entire ID. The silc_hash_client_id_compare compares the entire
515 Client ID as we want to find one specific Client ID. */
516 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
518 silc_hash_client_id_compare, NULL,
522 client = (SilcClientEntry)id_cache->context;
524 /* Remove the old entry and add a new one */
526 if (!silc_idcache_del_by_context(id_list->clients, client))
529 /* Check if anyone is watching old nickname */
530 if (server->server_type == SILC_ROUTER)
531 silc_server_check_watcher_list(server, client, nickname,
532 SILC_NOTIFY_TYPE_NICK_CHANGE);
534 silc_free(client->id);
535 silc_free(client->nickname);
537 client->nickname = nickname ? strdup(nickname) : NULL;
539 /* Check if anyone is watching new nickname */
540 if (server->server_type == SILC_ROUTER)
541 silc_server_check_watcher_list(server, client, nickname,
542 SILC_NOTIFY_TYPE_NICK_CHANGE);
544 if (!silc_idcache_add(id_list->clients, client->nickname, client->id,
548 SILC_LOG_DEBUG(("Replaced"));
553 /* Client cache entry destructor that is called when the cache is purged. */
555 void silc_idlist_client_destructor(SilcIDCache cache,
556 SilcIDCacheEntry entry)
558 SilcClientEntry client;
560 client = (SilcClientEntry)entry->context;
562 silc_free(client->nickname);
563 silc_free(client->username);
564 silc_free(client->userinfo);
565 silc_free(client->id);
566 silc_hash_table_free(client->channels);
568 memset(client, 'F', sizeof(*client));
573 /******************************************************************************
575 Channel entry functions
577 ******************************************************************************/
579 /* Add new channel entry. This add the new channel entry to the ID cache
580 system and returns the allocated entry or NULL on error. */
583 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
584 SilcChannelID *id, SilcServerEntry router,
585 SilcCipher channel_key, SilcHmac hmac,
588 SilcChannelEntry channel;
590 SILC_LOG_DEBUG(("Adding new channel entry"));
592 channel = silc_calloc(1, sizeof(*channel));
593 channel->channel_name = channel_name;
594 channel->mode = mode;
596 channel->router = router;
597 channel->channel_key = channel_key;
598 channel->hmac = hmac;
599 channel->created = time(0);
601 if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
606 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
607 NULL, NULL, NULL, TRUE);
609 if (!silc_idcache_add(id_list->channels, channel->channel_name,
610 (void *)channel->id, (void *)channel, expire, NULL)) {
611 silc_hmac_free(channel->hmac);
612 silc_hash_table_free(channel->user_list);
620 /* Foreach callbcak to free all users from the channel when deleting a
623 static void silc_idlist_del_channel_foreach(void *key, void *context,
626 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
628 SILC_LOG_DEBUG(("Removing client %s from channel %s",
629 chl->client->nickname, chl->channel->channel_name));
631 /* Remove the context from the client's channel hash table as that
632 table and channel's user_list hash table share this same context. */
633 silc_hash_table_del(chl->client->channels, chl->channel);
637 /* Free channel entry. This free's everything. */
639 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
641 SILC_LOG_DEBUG(("Start"));
644 /* Remove from cache */
646 if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
649 /* Free all client entrys from the users list. The silc_hash_table_free
650 will free all the entries so they are not freed at the foreach
652 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
654 silc_hash_table_free(entry->user_list);
657 silc_free(entry->channel_name);
658 silc_free(entry->id);
659 silc_free(entry->topic);
660 if (entry->channel_key)
661 silc_cipher_free(entry->channel_key);
663 memset(entry->key, 0, entry->key_len / 8);
664 silc_free(entry->key);
666 silc_free(entry->cipher);
668 silc_hmac_free(entry->hmac);
669 silc_free(entry->hmac_name);
670 silc_free(entry->rekey);
671 if (entry->founder_key)
672 silc_pkcs_public_key_free(entry->founder_key);
674 memset(entry, 'F', sizeof(*entry));
682 /* Finds channel by channel name. Channel names are unique and they
683 are not case-sensitive. */
686 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
687 SilcIDCacheEntry *ret_entry)
689 SilcIDCacheEntry id_cache = NULL;
691 SILC_LOG_DEBUG(("Channel by name %s", name));
693 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
697 *ret_entry = id_cache;
699 SILC_LOG_DEBUG(("Found"));
701 return id_cache->context;
704 /* Finds channel by Channel ID. */
707 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
708 SilcIDCacheEntry *ret_entry)
710 SilcIDCacheEntry id_cache = NULL;
711 SilcChannelEntry channel;
716 SILC_LOG_DEBUG(("Channel ID (%s)",
717 silc_id_render(id, SILC_ID_CHANNEL)));
719 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
722 channel = (SilcChannelEntry)id_cache->context;
725 *ret_entry = id_cache;
727 SILC_LOG_DEBUG(("Found"));
732 /* Replaces old Channel ID with new one. This is done when router forces
733 normal server to change Channel ID. */
736 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
737 SilcChannelID *new_id)
739 SilcIDCacheEntry id_cache = NULL;
740 SilcChannelEntry channel;
742 if (!old_id || !new_id)
745 SILC_LOG_DEBUG(("Replacing Channel ID"));
747 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
751 channel = (SilcChannelEntry)id_cache->context;
753 /* Remove the old entry and add a new one */
755 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
757 silc_free(channel->id);
758 channel->id = new_id;
760 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
763 SILC_LOG_DEBUG(("Replaced"));
768 /* Returns channels from the ID list. If the `channel_id' is NULL then
769 all channels are returned. */
772 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
773 SilcUInt32 *channels_count)
775 SilcIDCacheList list = NULL;
776 SilcIDCacheEntry id_cache = NULL;
777 SilcChannelEntry *channels = NULL;
780 SILC_LOG_DEBUG(("Start"));
783 if (!silc_idcache_get_all(id_list->channels, &list))
786 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
789 silc_idcache_list_first(list, &id_cache);
790 channels[i++] = (SilcChannelEntry)id_cache->context;
792 while (silc_idcache_list_next(list, &id_cache))
793 channels[i++] = (SilcChannelEntry)id_cache->context;
795 silc_idcache_list_free(list);
797 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
801 channels = silc_calloc(1, sizeof(*channels));
802 channels[0] = (SilcChannelEntry)id_cache->context;