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 && server->id->port == SILC_SWAB_16(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 id %s", entry->server_name ?
299 entry->server_name : "",
301 silc_id_render(entry->id, SILC_ID_SERVER) : ""));
304 silc_free(entry->server_name);
305 silc_free(entry->id);
306 silc_free(entry->server_info);
308 memset(entry, 'F', sizeof(*entry));
316 /******************************************************************************
318 Client entry functions
320 ******************************************************************************/
322 /* Add new client entry. This adds the client entry to ID cache system
323 and returns the allocated client entry or NULL on error. This is
324 called when new client connection is accepted to the server. If The
325 `router' is provided then the all server routines assume that the client
326 is not directly connected local client but it has router set and is
327 remote. If this is the case then `connection' must be NULL. If, on the
328 other hand, the `connection' is provided then the client is assumed
329 to be directly connected local client and `router' must be NULL. */
332 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
333 char *userinfo, SilcClientID *id,
334 SilcServerEntry router, void *connection,
337 SilcClientEntry client;
339 SILC_LOG_DEBUG(("Adding new client entry"));
341 client = silc_calloc(1, sizeof(*client));
342 client->nickname = nickname;
343 client->username = username;
344 client->userinfo = userinfo;
346 client->router = router;
347 client->connection = connection;
348 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
349 NULL, NULL, NULL, NULL, TRUE);
351 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
352 (void *)client, expire, NULL)) {
353 silc_hash_table_free(client->channels);
361 /* Free client entry. This free's everything and removes the entry
362 from ID cache. Call silc_idlist_del_data before calling this one. */
364 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
366 SILC_LOG_DEBUG(("Start"));
369 /* Remove from cache */
370 if (!silc_idcache_del_by_context(id_list->clients, entry))
373 assert(!silc_hash_table_count(entry->channels));
376 silc_free(entry->nickname);
377 silc_free(entry->servername);
378 silc_free(entry->username);
379 silc_free(entry->userinfo);
380 silc_free(entry->id);
381 silc_free(entry->attrs);
382 silc_hash_table_free(entry->channels);
384 memset(entry, 'F', sizeof(*entry));
393 /* Returns all clients matching requested nickname. Number of clients is
394 returned to `clients_count'. Caller must free the returned table. */
396 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
398 SilcClientEntry **clients,
399 SilcUInt32 *clients_count)
401 SilcIDCacheList list = NULL;
402 SilcIDCacheEntry id_cache = NULL;
404 SILC_LOG_DEBUG(("Start"));
406 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
409 *clients = silc_realloc(*clients,
410 (silc_idcache_list_count(list) + *clients_count) *
413 silc_idcache_list_first(list, &id_cache);
414 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
416 while (silc_idcache_list_next(list, &id_cache))
417 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
419 silc_idcache_list_free(list);
421 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
426 /* Returns all clients matching requested nickname hash. Number of clients
427 is returned to `clients_count'. Caller must free the returned table. */
429 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
431 SilcClientEntry **clients,
432 SilcUInt32 *clients_count)
434 SilcIDCacheList list = NULL;
435 SilcIDCacheEntry id_cache = NULL;
436 unsigned char hash[32];
437 SilcClientID client_id;
440 SILC_LOG_DEBUG(("Start"));
442 memset(nick, 0, sizeof(nick));
443 silc_to_lower(nickname, nick, sizeof(nick) - 1);
444 silc_hash_make(md5hash, nick, strlen(nick), hash);
446 /* As the Client ID is hashed in the ID cache by hashing only the hash
447 from the Client ID, we can do a lookup with only the hash not the
448 other parts of the ID and get all the clients with that hash, ie.
449 with that nickname, as the hash is from the nickname. */
450 memset(&client_id, 0, sizeof(client_id));
451 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
452 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
455 *clients = silc_realloc(*clients,
456 (silc_idcache_list_count(list) + *clients_count) *
459 silc_idcache_list_first(list, &id_cache);
460 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
462 while (silc_idcache_list_next(list, &id_cache))
463 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
465 silc_idcache_list_free(list);
467 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
472 /* Finds client by Client ID */
475 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
476 bool registered, SilcIDCacheEntry *ret_entry)
478 SilcIDCacheEntry id_cache = NULL;
479 SilcClientEntry client;
484 SILC_LOG_DEBUG(("Client ID (%s)",
485 silc_id_render(id, SILC_ID_CLIENT)));
487 /* Do extended search since the normal ID comparison function for
488 Client ID's compares only the hash from the Client ID and not the
489 entire ID. The silc_hash_client_id_compare compares the entire
490 Client ID as we want to find one specific Client ID. */
491 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
493 silc_hash_client_id_compare, NULL,
497 client = (SilcClientEntry)id_cache->context;
499 if (client && registered &&
500 !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
504 *ret_entry = id_cache;
506 SILC_LOG_DEBUG(("Found"));
511 /* Replaces old Client ID with new one */
514 silc_idlist_replace_client_id(SilcServer server,
515 SilcIDList id_list, SilcClientID *old_id,
516 SilcClientID *new_id, const char *nickname)
518 SilcIDCacheEntry id_cache = NULL;
519 SilcClientEntry client;
521 if (!old_id || !new_id)
524 SILC_LOG_DEBUG(("Replacing Client ID"));
526 /* Do extended search since the normal ID comparison function for
527 Client ID's compares only the hash from the Client ID and not the
528 entire ID. The silc_hash_client_id_compare compares the entire
529 Client ID as we want to find one specific Client ID. */
530 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
532 silc_hash_client_id_compare, NULL,
536 client = (SilcClientEntry)id_cache->context;
538 /* Remove the old entry and add a new one */
540 if (!silc_idcache_del_by_context(id_list->clients, client))
543 /* Check if anyone is watching old nickname */
544 if (server->server_type == SILC_ROUTER)
545 silc_server_check_watcher_list(server, client, nickname,
546 SILC_NOTIFY_TYPE_NICK_CHANGE);
548 silc_free(client->id);
549 silc_free(client->nickname);
551 client->nickname = nickname ? strdup(nickname) : NULL;
553 /* Check if anyone is watching new nickname */
554 if (server->server_type == SILC_ROUTER)
555 silc_server_check_watcher_list(server, client, nickname,
556 SILC_NOTIFY_TYPE_NICK_CHANGE);
558 if (!silc_idcache_add(id_list->clients, client->nickname, client->id,
562 SILC_LOG_DEBUG(("Replaced"));
567 /* Client cache entry destructor that is called when the cache is purged. */
569 void silc_idlist_client_destructor(SilcIDCache cache,
570 SilcIDCacheEntry entry)
572 SilcClientEntry client;
574 client = (SilcClientEntry)entry->context;
576 assert(!silc_hash_table_count(client->channels));
577 silc_free(client->nickname);
578 silc_free(client->username);
579 silc_free(client->userinfo);
580 silc_free(client->id);
581 silc_free(client->attrs);
582 silc_hash_table_free(client->channels);
584 memset(client, 'A', sizeof(*client));
589 /******************************************************************************
591 Channel entry functions
593 ******************************************************************************/
595 /* Add new channel entry. This add the new channel entry to the ID cache
596 system and returns the allocated entry or NULL on error. */
599 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
600 SilcChannelID *id, SilcServerEntry router,
601 SilcCipher channel_key, SilcHmac hmac,
604 SilcChannelEntry channel;
606 SILC_LOG_DEBUG(("Adding new channel %s", channel_name));
608 channel = silc_calloc(1, sizeof(*channel));
609 channel->channel_name = channel_name;
610 channel->mode = mode;
612 channel->router = router;
613 channel->channel_key = channel_key;
614 channel->hmac = hmac;
615 channel->created = channel->updated = time(0);
617 if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
622 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
623 NULL, NULL, NULL, TRUE);
625 if (!silc_idcache_add(id_list->channels, channel->channel_name,
626 (void *)channel->id, (void *)channel, expire, NULL)) {
627 silc_hmac_free(channel->hmac);
628 silc_hash_table_free(channel->user_list);
636 /* Foreach callbcak to free all users from the channel when deleting a
639 static void silc_idlist_del_channel_foreach(void *key, void *context,
642 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
644 SILC_LOG_DEBUG(("Removing client %s from channel %s",
645 chl->client->nickname ? chl->client->nickname :
646 (unsigned char *)"", chl->channel->channel_name));
648 /* Remove the context from the client's channel hash table as that
649 table and channel's user_list hash table share this same context. */
650 silc_hash_table_del(chl->client->channels, chl->channel);
654 /* Free channel entry. This free's everything. */
656 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
659 /* Remove from cache */
660 if (!silc_idcache_del_by_context(id_list->channels, entry))
663 SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
665 /* Free all client entrys from the users list. The silc_hash_table_free
666 will free all the entries so they are not freed at the foreach
668 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
670 silc_hash_table_free(entry->user_list);
673 silc_free(entry->channel_name);
674 silc_free(entry->id);
675 silc_free(entry->topic);
677 if (entry->invite_list)
678 silc_hash_table_free(entry->invite_list);
680 silc_hash_table_free(entry->ban_list);
682 if (entry->channel_key)
683 silc_cipher_free(entry->channel_key);
685 memset(entry->key, 0, entry->key_len / 8);
686 silc_free(entry->key);
688 silc_free(entry->cipher);
690 silc_hmac_free(entry->hmac);
691 silc_free(entry->hmac_name);
692 silc_free(entry->rekey);
693 if (entry->founder_key)
694 silc_pkcs_public_key_free(entry->founder_key);
696 memset(entry, 'F', sizeof(*entry));
704 /* Finds channel by channel name. Channel names are unique and they
705 are not case-sensitive. */
708 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
709 SilcIDCacheEntry *ret_entry)
711 SilcIDCacheEntry id_cache = NULL;
713 SILC_LOG_DEBUG(("Channel by name %s", name));
715 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
719 *ret_entry = id_cache;
721 SILC_LOG_DEBUG(("Found"));
724 ((SilcChannelEntry)id_cache->context)->updated = time(NULL);
726 return id_cache->context;
729 /* Finds channel by Channel ID. */
732 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
733 SilcIDCacheEntry *ret_entry)
735 SilcIDCacheEntry id_cache = NULL;
736 SilcChannelEntry channel;
741 SILC_LOG_DEBUG(("Channel ID (%s)",
742 silc_id_render(id, SILC_ID_CHANNEL)));
744 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
747 channel = (SilcChannelEntry)id_cache->context;
750 *ret_entry = id_cache;
752 SILC_LOG_DEBUG(("Found"));
755 channel->updated = time(NULL);
760 /* Replaces old Channel ID with new one. This is done when router forces
761 normal server to change Channel ID. */
764 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
765 SilcChannelID *new_id)
767 SilcIDCacheEntry id_cache = NULL;
768 SilcChannelEntry channel;
770 if (!old_id || !new_id)
773 SILC_LOG_DEBUG(("Replacing Channel ID"));
775 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
779 channel = (SilcChannelEntry)id_cache->context;
781 /* Remove the old entry and add a new one */
783 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
785 silc_free(channel->id);
786 channel->id = new_id;
788 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
791 SILC_LOG_DEBUG(("Replaced"));
794 channel->updated = time(NULL);
799 /* Returns channels from the ID list. If the `channel_id' is NULL then
800 all channels are returned. */
803 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
804 SilcUInt32 *channels_count)
806 SilcIDCacheList list = NULL;
807 SilcIDCacheEntry id_cache = NULL;
808 SilcChannelEntry *channels = NULL;
811 SILC_LOG_DEBUG(("Start"));
814 if (!silc_idcache_get_all(id_list->channels, &list))
817 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
820 silc_idcache_list_first(list, &id_cache);
821 channels[i++] = (SilcChannelEntry)id_cache->context;
823 while (silc_idcache_list_next(list, &id_cache))
824 channels[i++] = (SilcChannelEntry)id_cache->context;
826 silc_idcache_list_free(list);
828 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
832 channels = silc_calloc(1, sizeof(*channels));
833 channels[0] = (SilcChannelEntry)id_cache->context;