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->hash = idata->hash;
40 data->hmac = idata->hmac;
41 data->hmac_key = idata->hmac_key;
42 data->hmac_key_len = idata->hmac_key_len;
43 data->public_key = idata->public_key;
44 data->last_receive = idata->last_receive;
45 data->last_sent = idata->last_sent;
46 data->registered = idata->registered;
49 /* Free's all data in the common ID entry data structure. */
51 void silc_idlist_del_data(void *entry)
53 SilcIDListData idata = (SilcIDListData)entry;
55 silc_cipher_free(idata->send_key);
56 if (idata->receive_key)
57 silc_cipher_free(idata->receive_key);
59 silc_hmac_free(idata->hmac);
60 if (idata->hmac_key) {
61 memset(idata->hmac_key, 0, idata->hmac_key_len);
62 silc_free(idata->hmac_key);
64 if (idata->public_key)
65 silc_pkcs_public_key_free(idata->public_key);
70 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
72 SilcIDListPurge i = (SilcIDListPurge)context;
74 SILC_LOG_DEBUG(("Start"));
76 silc_idcache_purge(i->cache);
77 silc_task_register(i->timeout_queue, 0,
80 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
83 /******************************************************************************
85 Server entry functions
87 ******************************************************************************/
89 /* Add new server entry. This adds the new server entry to ID cache and
90 returns the allocated entry object or NULL on error. This is called
91 when new server connects to us. We also add ourselves to cache with
95 silc_idlist_add_server(SilcIDList id_list,
96 char *server_name, int server_type,
97 SilcServerID *id, SilcServerEntry router,
100 SilcServerEntry server;
102 SILC_LOG_DEBUG(("Adding new server entry"));
104 server = silc_calloc(1, sizeof(*server));
105 server->server_name = server_name;
106 server->server_type = server_type;
108 server->router = router;
109 server->connection = connection;
111 if (!silc_idcache_add(id_list->servers, server->server_name,
112 server->server_name ? strlen(server->server_name) : 0,
113 SILC_ID_SERVER, (void *)server->id,
114 (void *)server, TRUE, FALSE)) {
122 /* Finds server by Server ID */
125 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
126 SilcIDCacheEntry *ret_entry)
128 SilcIDCacheEntry id_cache = NULL;
129 SilcServerEntry server;
134 SILC_LOG_DEBUG(("Server ID (%s)",
135 silc_id_render(id, SILC_ID_SERVER)));
137 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
138 SILC_ID_SERVER, &id_cache))
141 server = (SilcServerEntry)id_cache->context;
144 *ret_entry = id_cache;
149 /* Find server by name */
152 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
153 SilcIDCacheEntry *ret_entry)
155 SilcIDCacheEntry id_cache = NULL;
156 SilcServerEntry server;
158 SILC_LOG_DEBUG(("Server by name `%s'", name));
160 if (!silc_idcache_find_by_data_one(id_list->servers, name, &id_cache))
163 server = (SilcServerEntry)id_cache->context;
166 *ret_entry = id_cache;
168 SILC_LOG_DEBUG(("Found"));
173 /* Find server by connection parameters, hostname and port */
176 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
177 int port, SilcIDCacheEntry *ret_entry)
179 SilcIDCacheList list = NULL;
180 SilcIDCacheEntry id_cache = NULL;
181 SilcServerEntry server = NULL;
182 SilcSocketConnection sock;
184 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
186 if (!silc_idcache_find_by_id(id_list->servers, SILC_ID_CACHE_ANY,
187 SILC_ID_SERVER, &list))
190 if (!silc_idcache_list_first(list, &id_cache)) {
191 silc_idcache_list_free(list);
196 server = (SilcServerEntry)id_cache->context;
197 sock = (SilcSocketConnection)server->connection;
199 if (sock && ((sock->hostname && !strcmp(sock->hostname, hostname)) ||
200 (sock->ip && !strcmp(sock->ip, hostname)))
201 && sock->port == port)
207 if (!silc_idcache_list_next(list, &id_cache))
211 silc_idcache_list_free(list);
214 *ret_entry = id_cache;
216 SILC_LOG_DEBUG(("Found"));
221 /* Replaces old Server ID with new one */
224 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
225 SilcServerID *new_id)
227 SilcIDCacheEntry id_cache = NULL;
228 SilcServerEntry server;
230 if (!old_id || !new_id)
233 SILC_LOG_DEBUG(("Replacing Server ID"));
235 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
236 SILC_ID_SERVER, &id_cache))
239 server = (SilcServerEntry)id_cache->context;
240 silc_free(server->id);
242 id_cache->id = (void *)new_id;
244 SILC_LOG_DEBUG(("Found"));
249 /* Removes and free's server entry from ID list */
251 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
254 /* Remove from cache */
256 if (!silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER,
261 if (entry->server_name)
262 silc_free(entry->server_name);
264 silc_free(entry->id);
266 memset(entry, 'F', sizeof(*entry));
274 /******************************************************************************
276 Client entry functions
278 ******************************************************************************/
280 /* Add new client entry. This adds the client entry to ID cache system
281 and returns the allocated client entry or NULL on error. This is
282 called when new client connection is accepted to the server. If The
283 `router' is provided then the all server routines assume that the client
284 is not directly connected local client but it has router set and is
285 remote. If this is the case then `connection' must be NULL. If, on the
286 other hand, the `connection' is provided then the client is assumed
287 to be directly connected local client and `router' must be NULL. */
290 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname,
291 unsigned int nickname_len, char *username,
292 char *userinfo, SilcClientID *id,
293 SilcServerEntry router, void *connection)
295 SilcClientEntry client;
297 SILC_LOG_DEBUG(("Adding new client entry"));
299 client = silc_calloc(1, sizeof(*client));
300 client->nickname = nickname;
301 client->username = username;
302 client->userinfo = userinfo;
304 client->router = router;
305 client->connection = connection;
306 silc_list_init(client->channels, struct SilcChannelClientEntryStruct,
309 if (!silc_idcache_add(id_list->clients, nickname, nickname_len,
310 SILC_ID_CLIENT, (void *)client->id,
311 (void *)client, TRUE, FALSE)) {
319 /* Free client entry. This free's everything and removes the entry
320 from ID cache. Call silc_idlist_del_data before calling this one. */
322 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
325 /* Remove from cache */
327 if (!silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT,
333 silc_free(entry->nickname);
335 silc_free(entry->username);
337 silc_free(entry->userinfo);
339 silc_free(entry->id);
341 memset(entry, 'F', sizeof(*entry));
350 /* Returns all clients matching requested nickname. Number of clients is
351 returned to `clients_count'. Caller must free the returned table. */
353 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
355 SilcClientEntry **clients,
356 unsigned int *clients_count)
358 SilcIDCacheList list = NULL;
359 SilcIDCacheEntry id_cache = NULL;
362 SILC_LOG_DEBUG(("Start"));
364 if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
367 *clients = silc_realloc(*clients,
368 (silc_idcache_list_count(list) + *clients_count) *
372 silc_idcache_list_first(list, &id_cache);
373 (*clients)[i++] = (SilcClientEntry)id_cache->context;
375 while (silc_idcache_list_next(list, &id_cache))
376 (*clients)[i++] = (SilcClientEntry)id_cache->context;
378 silc_idcache_list_free(list);
385 /* Returns all clients matching requested nickname. Number of clients is
386 returned to `clients_count'. Caller must free the returned table. */
387 /* XXX This actually checks the data, which can be hash of the nickname
388 but is not if the client is local client. Global client on global
389 list may have hash. Thus, this is not fully reliable function.
390 Instead this should probably check the hash from the list of client ID's. */
392 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
394 SilcClientEntry **clients,
395 unsigned int *clients_count)
397 SilcIDCacheList list = NULL;
398 SilcIDCacheEntry id_cache = NULL;
399 unsigned char hash[32];
402 SILC_LOG_DEBUG(("Start"));
404 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
406 if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
409 *clients = silc_realloc(*clients,
410 (silc_idcache_list_count(list) + *clients_count) *
414 silc_idcache_list_first(list, &id_cache);
415 (*clients)[i++] = (SilcClientEntry)id_cache->context;
417 while (silc_idcache_list_next(list, &id_cache))
418 (*clients)[i++] = (SilcClientEntry)id_cache->context;
420 silc_idcache_list_free(list);
427 /* Finds client by nickname hash. */
430 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
431 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
433 SilcIDCacheList list = NULL;
434 SilcIDCacheEntry id_cache = NULL;
435 SilcClientEntry client = NULL;
436 unsigned char hash[32];
438 SILC_LOG_DEBUG(("Client by hash"));
440 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
442 if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY,
443 SILC_ID_CLIENT, &list))
446 if (!silc_idcache_list_first(list, &id_cache)) {
447 silc_idcache_list_free(list);
452 client = (SilcClientEntry)id_cache->context;
454 if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
460 if (!silc_idcache_list_next(list, &id_cache))
464 silc_idcache_list_free(list);
467 *ret_entry = id_cache;
469 SILC_LOG_DEBUG(("Found"));
474 /* Finds client by Client ID */
477 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
478 SilcIDCacheEntry *ret_entry)
480 SilcIDCacheEntry id_cache = NULL;
481 SilcClientEntry client;
486 SILC_LOG_DEBUG(("Client ID (%s)",
487 silc_id_render(id, SILC_ID_CLIENT)));
489 if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id,
490 SILC_ID_CLIENT, &id_cache))
493 client = (SilcClientEntry)id_cache->context;
496 *ret_entry = id_cache;
498 SILC_LOG_DEBUG(("Found"));
503 /* Replaces old Client ID with new one */
506 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
507 SilcClientID *new_id)
509 SilcIDCacheEntry id_cache = NULL;
510 SilcClientEntry client;
512 if (!old_id || !new_id)
515 SILC_LOG_DEBUG(("Replacing Client ID"));
517 if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id,
518 SILC_ID_CLIENT, &id_cache))
521 client = (SilcClientEntry)id_cache->context;
522 silc_free(client->id);
524 id_cache->id = (void *)new_id;
526 /* If the old ID Cache data was the hash value of the old Client ID
527 replace it with the hash of new Client ID */
528 if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
529 silc_free(id_cache->data);
530 id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
531 memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
532 silc_idcache_sort_by_data(id_list->clients);
535 SILC_LOG_DEBUG(("Replaced"));
540 /* Client cache entry destructor that is called when the cache is purged. */
542 void silc_idlist_client_destructor(SilcIDCache cache,
543 SilcIDCacheEntry entry)
545 SilcClientEntry client;
547 SILC_LOG_DEBUG(("Start"));
549 client = (SilcClientEntry)entry->context;
551 if (client->nickname)
552 silc_free(client->nickname);
553 if (client->username)
554 silc_free(client->username);
555 if (client->userinfo)
556 silc_free(client->userinfo);
558 silc_free(client->id);
560 memset(client, 'F', sizeof(*client));
565 /******************************************************************************
567 Channel entry functions
569 ******************************************************************************/
571 /* Add new channel entry. This add the new channel entry to the ID cache
572 system and returns the allocated entry or NULL on error. */
575 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
576 SilcChannelID *id, SilcServerEntry router,
577 SilcCipher channel_key, SilcHmac hmac)
579 SilcChannelEntry channel;
581 channel = silc_calloc(1, sizeof(*channel));
582 channel->channel_name = channel_name;
583 channel->mode = mode;
585 channel->router = router;
586 channel->channel_key = channel_key;
587 channel->hmac = hmac;
589 if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
594 silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct,
597 if (!silc_idcache_add(id_list->channels, channel->channel_name,
598 channel->channel_name ? strlen(channel->channel_name) :
600 (void *)channel->id, (void *)channel, TRUE, FALSE)) {
608 /* Free channel entry. This free's everything. */
610 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
613 SilcChannelClientEntry chl;
615 /* Remove from cache */
617 if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL,
622 if (entry->channel_name)
623 silc_free(entry->channel_name);
625 silc_free(entry->id);
627 silc_free(entry->topic);
628 if (entry->channel_key)
629 silc_cipher_free(entry->channel_key);
631 memset(entry->key, 0, entry->key_len / 8);
632 silc_free(entry->key);
635 silc_free(entry->cipher);
636 if (entry->hmac_name)
637 silc_free(entry->hmac_name);
639 silc_free(entry->rekey);
641 /* Free all data, free also any reference from the client's channel
642 list since they share the same memory. */
643 silc_list_start(entry->user_list);
644 while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
645 silc_list_del(chl->client->channels, chl);
646 silc_list_del(entry->user_list, chl);
650 memset(entry, 'F', sizeof(*entry));
658 /* Finds channel by channel name. Channel names are unique and they
659 are not case-sensitive. */
662 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
663 SilcIDCacheEntry *ret_entry)
665 SilcIDCacheList list = NULL;
666 SilcIDCacheEntry id_cache = NULL;
667 SilcChannelEntry channel;
669 SILC_LOG_DEBUG(("Channel by name"));
671 if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
674 if (!silc_idcache_list_first(list, &id_cache)) {
675 silc_idcache_list_free(list);
679 channel = (SilcChannelEntry)id_cache->context;
682 *ret_entry = id_cache;
684 silc_idcache_list_free(list);
686 SILC_LOG_DEBUG(("Found"));
691 /* Finds channel by Channel ID. */
694 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
695 SilcIDCacheEntry *ret_entry)
697 SilcIDCacheEntry id_cache = NULL;
698 SilcChannelEntry channel;
703 SILC_LOG_DEBUG(("Channel ID (%s)",
704 silc_id_render(id, SILC_ID_CHANNEL)));
706 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id,
707 SILC_ID_CHANNEL, &id_cache))
710 channel = (SilcChannelEntry)id_cache->context;
713 *ret_entry = id_cache;
715 SILC_LOG_DEBUG(("Found"));
720 /* Replaces old Channel ID with new one. This is done when router forces
721 normal server to change Channel ID. */
724 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
725 SilcChannelID *new_id)
727 SilcIDCacheEntry id_cache = NULL;
728 SilcChannelEntry channel;
730 if (!old_id || !new_id)
733 SILC_LOG_DEBUG(("Replacing Channel ID"));
735 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
736 SILC_ID_CHANNEL, &id_cache))
739 channel = (SilcChannelEntry)id_cache->context;
740 silc_free(channel->id);
741 channel->id = new_id;
742 id_cache->id = (void *)new_id;
744 SILC_LOG_DEBUG(("Replaced"));
749 /* Returns channels from the ID list. If the `channel_id' is NULL then
750 all channels are returned. */
753 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
754 unsigned int *channels_count)
756 SilcIDCacheList list = NULL;
757 SilcIDCacheEntry id_cache = NULL;
758 SilcChannelEntry *channels;
761 SILC_LOG_DEBUG(("Start"));
763 if (!silc_idcache_find_by_id(id_list->channels, channel_id ? channel_id :
764 SILC_ID_CACHE_ANY, SILC_ID_CHANNEL, &list))
767 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
770 silc_idcache_list_first(list, &id_cache);
771 channels[i++] = (SilcChannelEntry)id_cache->context;
773 while (silc_idcache_list_next(list, &id_cache))
774 channels[i++] = (SilcChannelEntry)id_cache->context;
776 silc_idcache_list_free(list);