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 memcpy(data->fingerprint, idata->fingerprint, sizeof(data->fingerprint));
46 data->rekey = idata->rekey;
47 data->last_receive = idata->last_receive;
48 data->last_sent = idata->last_sent;
49 data->status = idata->status;
51 data->created = time(0); /* Update creation time */
54 /* Free's all data in the common ID entry data structure. */
56 void silc_idlist_del_data(void *entry)
58 SilcIDListData idata = (SilcIDListData)entry;
60 silc_cipher_free(idata->send_key);
61 if (idata->receive_key)
62 silc_cipher_free(idata->receive_key);
64 if (idata->rekey->send_enc_key) {
65 memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
66 silc_free(idata->rekey->send_enc_key);
68 silc_free(idata->rekey);
71 silc_hmac_free(idata->hmac_send);
72 if (idata->hmac_receive)
73 silc_hmac_free(idata->hmac_receive);
75 silc_hash_free(idata->hash);
76 if (idata->public_key)
77 silc_pkcs_public_key_free(idata->public_key);
82 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
84 SilcIDListPurge i = (SilcIDListPurge)context;
86 SILC_LOG_DEBUG(("Start"));
88 silc_idcache_purge(i->cache);
89 silc_schedule_task_add(i->schedule, 0,
91 (void *)i, i->timeout, 0,
92 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
95 /******************************************************************************
97 Server entry functions
99 ******************************************************************************/
101 /* Add new server entry. This adds the new server entry to ID cache and
102 returns the allocated entry object or NULL on error. This is called
103 when new server connects to us. We also add ourselves to cache with
107 silc_idlist_add_server(SilcIDList id_list,
108 char *server_name, int server_type,
109 SilcServerID *id, SilcServerEntry router,
112 SilcServerEntry server;
114 SILC_LOG_DEBUG(("Adding new server entry"));
116 server = silc_calloc(1, sizeof(*server));
117 server->server_name = server_name;
118 server->server_type = server_type;
120 server->router = router;
121 server->connection = connection;
123 if (!silc_idcache_add(id_list->servers, server->server_name,
124 (void *)server->id, (void *)server, 0, NULL)) {
132 /* Finds server by Server ID */
135 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
136 bool registered, SilcIDCacheEntry *ret_entry)
138 SilcIDCacheEntry id_cache = NULL;
139 SilcServerEntry server;
144 SILC_LOG_DEBUG(("Server ID (%s)",
145 silc_id_render(id, SILC_ID_SERVER)));
147 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
151 server = (SilcServerEntry)id_cache->context;
154 *ret_entry = id_cache;
156 if (server && registered &&
157 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
160 SILC_LOG_DEBUG(("Found"));
165 /* Find server by name */
168 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
169 bool registered, SilcIDCacheEntry *ret_entry)
171 SilcIDCacheEntry id_cache = NULL;
172 SilcServerEntry server;
174 SILC_LOG_DEBUG(("Server by name `%s'", name));
176 if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
179 server = (SilcServerEntry)id_cache->context;
182 *ret_entry = id_cache;
184 if (server && registered &&
185 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
188 SILC_LOG_DEBUG(("Found"));
193 /* Find server by connection parameters, hostname and port */
196 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
197 int port, bool registered,
198 SilcIDCacheEntry *ret_entry)
200 SilcIDCacheList list = NULL;
201 SilcIDCacheEntry id_cache = NULL;
202 SilcServerEntry server = NULL;
203 SilcSocketConnection sock;
205 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
207 if (!silc_idcache_get_all(id_list->servers, &list))
210 if (!silc_idcache_list_first(list, &id_cache)) {
211 silc_idcache_list_free(list);
216 server = (SilcServerEntry)id_cache->context;
217 sock = (SilcSocketConnection)server->connection;
219 if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
220 (sock->ip && !strcasecmp(sock->ip, hostname)))
221 && sock->port == port)
227 if (!silc_idcache_list_next(list, &id_cache))
231 silc_idcache_list_free(list);
234 *ret_entry = id_cache;
236 if (server && registered &&
237 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
240 SILC_LOG_DEBUG(("Found"));
245 /* Replaces old Server ID with new one */
248 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
249 SilcServerID *new_id)
251 SilcIDCacheEntry id_cache = NULL;
252 SilcServerEntry server;
254 if (!old_id || !new_id)
257 SILC_LOG_DEBUG(("Replacing Server ID"));
259 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
263 server = (SilcServerEntry)id_cache->context;
265 /* Remove the old entry and add a new one */
267 silc_idcache_del_by_id(id_list->servers, (void *)server->id);
269 silc_free(server->id);
272 silc_idcache_add(id_list->servers, server->server_name, server->id,
275 SILC_LOG_DEBUG(("Found"));
280 /* Removes and free's server entry from ID list */
282 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
284 SILC_LOG_DEBUG(("Start"));
287 /* Remove from cache */
289 if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
293 silc_free(entry->server_name);
294 silc_free(entry->id);
296 memset(entry, 'F', sizeof(*entry));
304 /******************************************************************************
306 Client entry functions
308 ******************************************************************************/
310 /* Add new client entry. This adds the client entry to ID cache system
311 and returns the allocated client entry or NULL on error. This is
312 called when new client connection is accepted to the server. If The
313 `router' is provided then the all server routines assume that the client
314 is not directly connected local client but it has router set and is
315 remote. If this is the case then `connection' must be NULL. If, on the
316 other hand, the `connection' is provided then the client is assumed
317 to be directly connected local client and `router' must be NULL. */
320 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
321 char *userinfo, SilcClientID *id,
322 SilcServerEntry router, void *connection,
325 SilcClientEntry client;
327 SILC_LOG_DEBUG(("Adding new client entry"));
329 client = silc_calloc(1, sizeof(*client));
330 client->nickname = nickname;
331 client->username = username;
332 client->userinfo = userinfo;
334 client->router = router;
335 client->connection = connection;
336 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
337 NULL, NULL, NULL, NULL, TRUE);
339 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
340 (void *)client, expire, NULL)) {
341 silc_hash_table_free(client->channels);
349 /* Free client entry. This free's everything and removes the entry
350 from ID cache. Call silc_idlist_del_data before calling this one. */
352 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
354 SILC_LOG_DEBUG(("Start"));
357 /* Remove from cache */
359 if (!silc_idcache_del_by_context(id_list->clients, entry))
363 silc_free(entry->nickname);
364 silc_free(entry->username);
365 silc_free(entry->userinfo);
366 silc_free(entry->id);
367 silc_hash_table_free(entry->channels);
369 memset(entry, 'F', sizeof(*entry));
378 /* Returns all clients matching requested nickname. Number of clients is
379 returned to `clients_count'. Caller must free the returned table. */
381 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
383 SilcClientEntry **clients,
384 SilcUInt32 *clients_count)
386 SilcIDCacheList list = NULL;
387 SilcIDCacheEntry id_cache = NULL;
389 SILC_LOG_DEBUG(("Start"));
391 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
394 *clients = silc_realloc(*clients,
395 (silc_idcache_list_count(list) + *clients_count) *
398 silc_idcache_list_first(list, &id_cache);
399 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
401 while (silc_idcache_list_next(list, &id_cache))
402 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
404 silc_idcache_list_free(list);
406 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
411 /* Returns all clients matching requested nickname hash. Number of clients
412 is returned to `clients_count'. Caller must free the returned table. */
414 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
416 SilcClientEntry **clients,
417 SilcUInt32 *clients_count)
419 SilcIDCacheList list = NULL;
420 SilcIDCacheEntry id_cache = NULL;
421 unsigned char hash[32];
422 SilcClientID client_id;
424 SILC_LOG_DEBUG(("Start"));
426 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
428 /* As the Client ID is hashed in the ID cache by hashing only the hash
429 from the Client ID, we can do a lookup with only the hash not the
430 other parts of the ID and get all the clients with that hash, ie.
431 with that nickname, as the hash is from the nickname. */
432 memset(&client_id, 0, sizeof(client_id));
433 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
434 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
437 *clients = silc_realloc(*clients,
438 (silc_idcache_list_count(list) + *clients_count) *
441 silc_idcache_list_first(list, &id_cache);
442 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
444 while (silc_idcache_list_next(list, &id_cache))
445 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
447 silc_idcache_list_free(list);
449 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
454 /* Finds client by Client ID */
457 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
458 bool registered, SilcIDCacheEntry *ret_entry)
460 SilcIDCacheEntry id_cache = NULL;
461 SilcClientEntry client;
466 SILC_LOG_DEBUG(("Client ID (%s)",
467 silc_id_render(id, SILC_ID_CLIENT)));
469 /* Do extended search since the normal ID comparison function for
470 Client ID's compares only the hash from the Client ID and not the
471 entire ID. The silc_hash_client_id_compare compares the entire
472 Client ID as we want to find one specific Client ID. */
473 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
475 silc_hash_client_id_compare, NULL,
479 client = (SilcClientEntry)id_cache->context;
482 *ret_entry = id_cache;
484 if (client && registered &&
485 !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
488 SILC_LOG_DEBUG(("Found"));
493 /* Replaces old Client ID with new one */
496 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
497 SilcClientID *new_id)
499 SilcIDCacheEntry id_cache = NULL;
500 SilcClientEntry client;
502 if (!old_id || !new_id)
505 SILC_LOG_DEBUG(("Replacing Client ID"));
507 /* Do extended search since the normal ID comparison function for
508 Client ID's compares only the hash from the Client ID and not the
509 entire ID. The silc_hash_client_id_compare compares the entire
510 Client ID as we want to find one specific Client ID. */
511 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
513 silc_hash_client_id_compare, NULL,
517 client = (SilcClientEntry)id_cache->context;
519 /* Remove the old entry and add a new one */
521 silc_idcache_del_by_context(id_list->clients, client);
523 silc_free(client->id);
526 silc_idcache_add(id_list->clients, NULL, client->id, client, 0, NULL);
528 SILC_LOG_DEBUG(("Replaced"));
533 /* Client cache entry destructor that is called when the cache is purged. */
535 void silc_idlist_client_destructor(SilcIDCache cache,
536 SilcIDCacheEntry entry)
538 SilcClientEntry client;
540 client = (SilcClientEntry)entry->context;
542 silc_free(client->nickname);
543 silc_free(client->username);
544 silc_free(client->userinfo);
545 silc_free(client->id);
546 silc_hash_table_free(client->channels);
548 memset(client, 'F', sizeof(*client));
553 /******************************************************************************
555 Channel entry functions
557 ******************************************************************************/
559 /* Add new channel entry. This add the new channel entry to the ID cache
560 system and returns the allocated entry or NULL on error. */
563 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
564 SilcChannelID *id, SilcServerEntry router,
565 SilcCipher channel_key, SilcHmac hmac,
568 SilcChannelEntry channel;
570 SILC_LOG_DEBUG(("Adding new channel entry"));
572 channel = silc_calloc(1, sizeof(*channel));
573 channel->channel_name = channel_name;
574 channel->mode = mode;
576 channel->router = router;
577 channel->channel_key = channel_key;
578 channel->hmac = hmac;
579 channel->created = time(0);
581 if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
586 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
587 NULL, NULL, NULL, TRUE);
589 if (!silc_idcache_add(id_list->channels, channel->channel_name,
590 (void *)channel->id, (void *)channel, expire, NULL)) {
591 silc_hmac_free(channel->hmac);
592 silc_hash_table_free(channel->user_list);
600 /* Foreach callbcak to free all users from the channel when deleting a
603 static void silc_idlist_del_channel_foreach(void *key, void *context,
606 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
608 SILC_LOG_DEBUG(("Removing client %s from channel %s",
609 chl->client->nickname, chl->channel->channel_name));
611 /* Remove the context from the client's channel hash table as that
612 table and channel's user_list hash table share this same context. */
613 silc_hash_table_del(chl->client->channels, chl->channel);
617 /* Free channel entry. This free's everything. */
619 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
621 SILC_LOG_DEBUG(("Start"));
624 /* Remove from cache */
626 if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
629 /* Free all client entrys from the users list. The silc_hash_table_free
630 will free all the entries so they are not freed at the foreach
632 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
634 silc_hash_table_free(entry->user_list);
637 silc_free(entry->channel_name);
638 silc_free(entry->id);
639 silc_free(entry->topic);
640 if (entry->channel_key)
641 silc_cipher_free(entry->channel_key);
643 memset(entry->key, 0, entry->key_len / 8);
644 silc_free(entry->key);
646 silc_free(entry->cipher);
648 silc_hmac_free(entry->hmac);
649 silc_free(entry->hmac_name);
650 silc_free(entry->rekey);
652 memset(entry, 'F', sizeof(*entry));
660 /* Finds channel by channel name. Channel names are unique and they
661 are not case-sensitive. */
664 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
665 SilcIDCacheEntry *ret_entry)
667 SilcIDCacheEntry id_cache = NULL;
669 SILC_LOG_DEBUG(("Channel by name %s", name));
671 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
675 *ret_entry = id_cache;
677 SILC_LOG_DEBUG(("Found"));
679 return id_cache->context;
682 /* Finds channel by Channel ID. */
685 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
686 SilcIDCacheEntry *ret_entry)
688 SilcIDCacheEntry id_cache = NULL;
689 SilcChannelEntry channel;
694 SILC_LOG_DEBUG(("Channel ID (%s)",
695 silc_id_render(id, SILC_ID_CHANNEL)));
697 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
700 channel = (SilcChannelEntry)id_cache->context;
703 *ret_entry = id_cache;
705 SILC_LOG_DEBUG(("Found"));
710 /* Replaces old Channel ID with new one. This is done when router forces
711 normal server to change Channel ID. */
714 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
715 SilcChannelID *new_id)
717 SilcIDCacheEntry id_cache = NULL;
718 SilcChannelEntry channel;
720 if (!old_id || !new_id)
723 SILC_LOG_DEBUG(("Replacing Channel ID"));
725 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
729 channel = (SilcChannelEntry)id_cache->context;
731 /* Remove the old entry and add a new one */
733 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
735 silc_free(channel->id);
736 channel->id = new_id;
738 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
741 SILC_LOG_DEBUG(("Replaced"));
746 /* Returns channels from the ID list. If the `channel_id' is NULL then
747 all channels are returned. */
750 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
751 SilcUInt32 *channels_count)
753 SilcIDCacheList list = NULL;
754 SilcIDCacheEntry id_cache = NULL;
755 SilcChannelEntry *channels = NULL;
758 SILC_LOG_DEBUG(("Start"));
761 if (!silc_idcache_get_all(id_list->channels, &list))
764 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
767 silc_idcache_list_first(list, &id_cache);
768 channels[i++] = (SilcChannelEntry)id_cache->context;
770 while (silc_idcache_list_next(list, &id_cache))
771 channels[i++] = (SilcChannelEntry)id_cache->context;
773 silc_idcache_list_free(list);
775 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
779 channels = silc_calloc(1, sizeof(*channels));
780 channels[0] = (SilcChannelEntry)id_cache->context;