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);
74 if (idata->public_key)
75 silc_pkcs_public_key_free(idata->public_key);
80 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
82 SilcIDListPurge i = (SilcIDListPurge)context;
84 SILC_LOG_DEBUG(("Start"));
86 silc_idcache_purge(i->cache);
87 silc_schedule_task_add(i->schedule, 0,
90 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
93 /******************************************************************************
95 Server entry functions
97 ******************************************************************************/
99 /* Add new server entry. This adds the new server entry to ID cache and
100 returns the allocated entry object or NULL on error. This is called
101 when new server connects to us. We also add ourselves to cache with
105 silc_idlist_add_server(SilcIDList id_list,
106 char *server_name, int server_type,
107 SilcServerID *id, SilcServerEntry router,
110 SilcServerEntry server;
112 SILC_LOG_DEBUG(("Adding new server entry"));
114 server = silc_calloc(1, sizeof(*server));
115 server->server_name = server_name;
116 server->server_type = server_type;
118 server->router = router;
119 server->connection = connection;
121 if (!silc_idcache_add(id_list->servers, server->server_name,
122 (void *)server->id, (void *)server, FALSE)) {
130 /* Finds server by Server ID */
133 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
134 bool registered, SilcIDCacheEntry *ret_entry)
136 SilcIDCacheEntry id_cache = NULL;
137 SilcServerEntry server;
142 SILC_LOG_DEBUG(("Server ID (%s)",
143 silc_id_render(id, SILC_ID_SERVER)));
145 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
149 server = (SilcServerEntry)id_cache->context;
152 *ret_entry = id_cache;
154 if (server && registered &&
155 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
158 SILC_LOG_DEBUG(("Found"));
163 /* Find server by name */
166 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
167 bool registered, SilcIDCacheEntry *ret_entry)
169 SilcIDCacheEntry id_cache = NULL;
170 SilcServerEntry server;
172 SILC_LOG_DEBUG(("Server by name `%s'", name));
174 if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
177 server = (SilcServerEntry)id_cache->context;
180 *ret_entry = id_cache;
182 if (server && registered &&
183 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
186 SILC_LOG_DEBUG(("Found"));
191 /* Find server by connection parameters, hostname and port */
194 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
195 int port, bool registered,
196 SilcIDCacheEntry *ret_entry)
198 SilcIDCacheList list = NULL;
199 SilcIDCacheEntry id_cache = NULL;
200 SilcServerEntry server = NULL;
201 SilcSocketConnection sock;
203 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
205 if (!silc_idcache_get_all(id_list->servers, &list))
208 if (!silc_idcache_list_first(list, &id_cache)) {
209 silc_idcache_list_free(list);
214 server = (SilcServerEntry)id_cache->context;
215 sock = (SilcSocketConnection)server->connection;
217 if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
218 (sock->ip && !strcasecmp(sock->ip, hostname)))
219 && sock->port == port)
225 if (!silc_idcache_list_next(list, &id_cache))
229 silc_idcache_list_free(list);
232 *ret_entry = id_cache;
234 if (server && registered &&
235 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
238 SILC_LOG_DEBUG(("Found"));
243 /* Replaces old Server ID with new one */
246 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
247 SilcServerID *new_id)
249 SilcIDCacheEntry id_cache = NULL;
250 SilcServerEntry server;
252 if (!old_id || !new_id)
255 SILC_LOG_DEBUG(("Replacing Server ID"));
257 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
261 server = (SilcServerEntry)id_cache->context;
263 /* Remove the old entry and add a new one */
265 silc_idcache_del_by_id(id_list->servers, (void *)server->id);
267 silc_free(server->id);
270 silc_idcache_add(id_list->servers, server->server_name, server->id,
273 SILC_LOG_DEBUG(("Found"));
278 /* Removes and free's server entry from ID list */
280 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
282 SILC_LOG_DEBUG(("Start"));
285 /* Remove from cache */
287 if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
291 silc_free(entry->server_name);
292 silc_free(entry->id);
294 memset(entry, 'F', sizeof(*entry));
302 /******************************************************************************
304 Client entry functions
306 ******************************************************************************/
308 /* Add new client entry. This adds the client entry to ID cache system
309 and returns the allocated client entry or NULL on error. This is
310 called when new client connection is accepted to the server. If The
311 `router' is provided then the all server routines assume that the client
312 is not directly connected local client but it has router set and is
313 remote. If this is the case then `connection' must be NULL. If, on the
314 other hand, the `connection' is provided then the client is assumed
315 to be directly connected local client and `router' must be NULL. */
318 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
319 char *userinfo, SilcClientID *id,
320 SilcServerEntry router, void *connection)
322 SilcClientEntry client;
324 SILC_LOG_DEBUG(("Adding new client entry"));
326 client = silc_calloc(1, sizeof(*client));
327 client->nickname = nickname;
328 client->username = username;
329 client->userinfo = userinfo;
331 client->router = router;
332 client->connection = connection;
333 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
334 NULL, NULL, NULL, NULL, TRUE);
336 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
337 (void *)client, FALSE)) {
338 silc_hash_table_free(client->channels);
346 /* Free client entry. This free's everything and removes the entry
347 from ID cache. Call silc_idlist_del_data before calling this one. */
349 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
351 SILC_LOG_DEBUG(("Start"));
354 /* Remove from cache */
356 if (!silc_idcache_del_by_context(id_list->clients, entry))
360 silc_free(entry->nickname);
361 silc_free(entry->username);
362 silc_free(entry->userinfo);
363 silc_free(entry->id);
365 memset(entry, 'F', sizeof(*entry));
374 /* Returns all clients matching requested nickname. Number of clients is
375 returned to `clients_count'. Caller must free the returned table. */
377 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
379 SilcClientEntry **clients,
380 uint32 *clients_count)
382 SilcIDCacheList list = NULL;
383 SilcIDCacheEntry id_cache = NULL;
385 SILC_LOG_DEBUG(("Start"));
387 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
390 *clients = silc_realloc(*clients,
391 (silc_idcache_list_count(list) + *clients_count) *
394 silc_idcache_list_first(list, &id_cache);
395 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
397 while (silc_idcache_list_next(list, &id_cache))
398 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
400 silc_idcache_list_free(list);
402 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
407 /* Returns all clients matching requested nickname hash. Number of clients
408 is returned to `clients_count'. Caller must free the returned table. */
410 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
412 SilcClientEntry **clients,
413 uint32 *clients_count)
415 SilcIDCacheList list = NULL;
416 SilcIDCacheEntry id_cache = NULL;
417 unsigned char hash[32];
418 SilcClientID client_id;
420 SILC_LOG_DEBUG(("Start"));
422 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
424 /* As the Client ID is hashed in the ID cache by hashing only the hash
425 from the Client ID, we can do a lookup with only the hash not the
426 other parts of the ID and get all the clients with that hash, ie.
427 with that nickname, as the hash is from the nickname. */
428 memset(&client_id, 0, sizeof(client_id));
429 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
430 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
433 *clients = silc_realloc(*clients,
434 (silc_idcache_list_count(list) + *clients_count) *
437 silc_idcache_list_first(list, &id_cache);
438 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
440 while (silc_idcache_list_next(list, &id_cache))
441 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
443 silc_idcache_list_free(list);
445 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
450 /* Finds client by Client ID */
453 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
454 bool registered, SilcIDCacheEntry *ret_entry)
456 SilcIDCacheEntry id_cache = NULL;
457 SilcClientEntry client;
462 SILC_LOG_DEBUG(("Client ID (%s)",
463 silc_id_render(id, SILC_ID_CLIENT)));
465 /* Do extended search since the normal ID comparison function for
466 Client ID's compares only the hash from the Client ID and not the
467 entire ID. The silc_hash_client_id_compare compares the entire
468 Client ID as we want to find one specific Client ID. */
469 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
471 silc_hash_client_id_compare, NULL,
475 client = (SilcClientEntry)id_cache->context;
478 *ret_entry = id_cache;
480 if (client && registered &&
481 !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
484 SILC_LOG_DEBUG(("Found"));
489 /* Replaces old Client ID with new one */
492 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
493 SilcClientID *new_id)
495 SilcIDCacheEntry id_cache = NULL;
496 SilcClientEntry client;
498 if (!old_id || !new_id)
501 SILC_LOG_DEBUG(("Replacing Client ID"));
503 /* Do extended search since the normal ID comparison function for
504 Client ID's compares only the hash from the Client ID and not the
505 entire ID. The silc_hash_client_id_compare compares the entire
506 Client ID as we want to find one specific Client ID. */
507 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
509 silc_hash_client_id_compare, NULL,
513 client = (SilcClientEntry)id_cache->context;
515 /* Remove the old entry and add a new one */
517 silc_idcache_del_by_context(id_list->clients, client);
519 silc_free(client->id);
522 silc_idcache_add(id_list->clients, NULL, client->id, client, FALSE);
524 SILC_LOG_DEBUG(("Replaced"));
529 /* Client cache entry destructor that is called when the cache is purged. */
531 void silc_idlist_client_destructor(SilcIDCache cache,
532 SilcIDCacheEntry entry)
534 SilcClientEntry client;
536 SILC_LOG_DEBUG(("Start"));
538 client = (SilcClientEntry)entry->context;
540 if (client->nickname)
541 silc_free(client->nickname);
542 if (client->username)
543 silc_free(client->username);
544 if (client->userinfo)
545 silc_free(client->userinfo);
547 silc_free(client->id);
549 memset(client, 'F', sizeof(*client));
554 /******************************************************************************
556 Channel entry functions
558 ******************************************************************************/
560 /* Add new channel entry. This add the new channel entry to the ID cache
561 system and returns the allocated entry or NULL on error. */
564 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
565 SilcChannelID *id, SilcServerEntry router,
566 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, FALSE)) {
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 /* Remove the context from the client's channel hash table as that
609 table and channel's user_list hash table share this same context. */
610 silc_hash_table_del(chl->client->channels, chl->channel);
614 /* Free channel entry. This free's everything. */
616 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
618 SILC_LOG_DEBUG(("Start"));
621 /* Remove from cache */
623 if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
627 silc_free(entry->channel_name);
628 silc_free(entry->id);
629 silc_free(entry->topic);
630 if (entry->channel_key)
631 silc_cipher_free(entry->channel_key);
633 memset(entry->key, 0, entry->key_len / 8);
634 silc_free(entry->key);
636 silc_free(entry->cipher);
637 silc_free(entry->hmac_name);
638 silc_free(entry->rekey);
640 /* Free all client entrys from the users list. The silc_hash_table_free
641 will free all the entries so they are not freed at the foreach
643 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
645 silc_hash_table_free(entry->user_list);
647 memset(entry, 'F', sizeof(*entry));
655 /* Finds channel by channel name. Channel names are unique and they
656 are not case-sensitive. */
659 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
660 SilcIDCacheEntry *ret_entry)
662 SilcIDCacheEntry id_cache = NULL;
664 SILC_LOG_DEBUG(("Channel by name"));
666 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
670 *ret_entry = id_cache;
672 SILC_LOG_DEBUG(("Found"));
674 return id_cache->context;
677 /* Finds channel by Channel ID. */
680 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
681 SilcIDCacheEntry *ret_entry)
683 SilcIDCacheEntry id_cache = NULL;
684 SilcChannelEntry channel;
689 SILC_LOG_DEBUG(("Channel ID (%s)",
690 silc_id_render(id, SILC_ID_CHANNEL)));
692 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
695 channel = (SilcChannelEntry)id_cache->context;
698 *ret_entry = id_cache;
700 SILC_LOG_DEBUG(("Found"));
705 /* Replaces old Channel ID with new one. This is done when router forces
706 normal server to change Channel ID. */
709 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
710 SilcChannelID *new_id)
712 SilcIDCacheEntry id_cache = NULL;
713 SilcChannelEntry channel;
715 if (!old_id || !new_id)
718 SILC_LOG_DEBUG(("Replacing Channel ID"));
720 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
724 channel = (SilcChannelEntry)id_cache->context;
726 /* Remove the old entry and add a new one */
728 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
730 silc_free(channel->id);
731 channel->id = new_id;
733 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
736 SILC_LOG_DEBUG(("Replaced"));
741 /* Returns channels from the ID list. If the `channel_id' is NULL then
742 all channels are returned. */
745 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
746 uint32 *channels_count)
748 SilcIDCacheList list = NULL;
749 SilcIDCacheEntry id_cache = NULL;
750 SilcChannelEntry *channels = NULL;
753 SILC_LOG_DEBUG(("Start"));
756 if (!silc_idcache_get_all(id_list->channels, &list))
759 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
762 silc_idcache_list_first(list, &id_cache);
763 channels[i++] = (SilcChannelEntry)id_cache->context;
765 while (silc_idcache_list_next(list, &id_cache))
766 channels[i++] = (SilcChannelEntry)id_cache->context;
768 silc_idcache_list_free(list);
770 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
774 channels = silc_calloc(1, sizeof(*channels));
775 channels[0] = (SilcChannelEntry)id_cache->context;