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->send_enc_key = idata->send_enc_key;
40 data->enc_key_len = idata->enc_key_len;
41 data->pfs = idata->pfs;
42 data->hash = idata->hash;
43 data->hmac = idata->hmac;
44 data->public_key = idata->public_key;
45 data->last_receive = idata->last_receive;
46 data->last_sent = idata->last_sent;
47 data->registered = idata->registered;
50 /* Free's all data in the common ID entry data structure. */
52 void silc_idlist_del_data(void *entry)
54 SilcIDListData idata = (SilcIDListData)entry;
56 silc_cipher_free(idata->send_key);
57 if (idata->receive_key)
58 silc_cipher_free(idata->receive_key);
59 if (idata->send_enc_key) {
60 memset(idata->send_enc_key, 0, idata->enc_key_len);
61 silc_free(idata->send_enc_key);
64 silc_hmac_free(idata->hmac);
65 if (idata->public_key)
66 silc_pkcs_public_key_free(idata->public_key);
71 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
73 SilcIDListPurge i = (SilcIDListPurge)context;
75 SILC_LOG_DEBUG(("Start"));
77 silc_idcache_purge(i->cache);
78 silc_task_register(i->timeout_queue, 0,
81 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
84 /******************************************************************************
86 Server entry functions
88 ******************************************************************************/
90 /* Add new server entry. This adds the new server entry to ID cache and
91 returns the allocated entry object or NULL on error. This is called
92 when new server connects to us. We also add ourselves to cache with
96 silc_idlist_add_server(SilcIDList id_list,
97 char *server_name, int server_type,
98 SilcServerID *id, SilcServerEntry router,
101 SilcServerEntry server;
103 SILC_LOG_DEBUG(("Adding new server entry"));
105 server = silc_calloc(1, sizeof(*server));
106 server->server_name = server_name;
107 server->server_type = server_type;
109 server->router = router;
110 server->connection = connection;
112 if (!silc_idcache_add(id_list->servers, server->server_name,
113 server->server_name ? strlen(server->server_name) : 0,
114 SILC_ID_SERVER, (void *)server->id,
115 (void *)server, TRUE, FALSE)) {
123 /* Finds server by Server ID */
126 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
127 SilcIDCacheEntry *ret_entry)
129 SilcIDCacheEntry id_cache = NULL;
130 SilcServerEntry server;
135 SILC_LOG_DEBUG(("Server ID (%s)",
136 silc_id_render(id, SILC_ID_SERVER)));
138 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
139 SILC_ID_SERVER, &id_cache))
142 server = (SilcServerEntry)id_cache->context;
145 *ret_entry = id_cache;
150 /* Find server by name */
153 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
154 SilcIDCacheEntry *ret_entry)
156 SilcIDCacheEntry id_cache = NULL;
157 SilcServerEntry server;
159 SILC_LOG_DEBUG(("Server by name `%s'", name));
161 if (!silc_idcache_find_by_data_one(id_list->servers, name, &id_cache))
164 server = (SilcServerEntry)id_cache->context;
167 *ret_entry = id_cache;
169 SILC_LOG_DEBUG(("Found"));
174 /* Find server by connection parameters, hostname and port */
177 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
178 int port, SilcIDCacheEntry *ret_entry)
180 SilcIDCacheList list = NULL;
181 SilcIDCacheEntry id_cache = NULL;
182 SilcServerEntry server = NULL;
183 SilcSocketConnection sock;
185 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
187 if (!silc_idcache_find_by_id(id_list->servers, SILC_ID_CACHE_ANY,
188 SILC_ID_SERVER, &list))
191 if (!silc_idcache_list_first(list, &id_cache)) {
192 silc_idcache_list_free(list);
197 server = (SilcServerEntry)id_cache->context;
198 sock = (SilcSocketConnection)server->connection;
200 if (sock && ((sock->hostname && !strcmp(sock->hostname, hostname)) ||
201 (sock->ip && !strcmp(sock->ip, hostname)))
202 && sock->port == port)
208 if (!silc_idcache_list_next(list, &id_cache))
212 silc_idcache_list_free(list);
215 *ret_entry = id_cache;
217 SILC_LOG_DEBUG(("Found"));
222 /* Replaces old Server ID with new one */
225 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
226 SilcServerID *new_id)
228 SilcIDCacheEntry id_cache = NULL;
229 SilcServerEntry server;
231 if (!old_id || !new_id)
234 SILC_LOG_DEBUG(("Replacing Server ID"));
236 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
237 SILC_ID_SERVER, &id_cache))
240 server = (SilcServerEntry)id_cache->context;
241 silc_free(server->id);
243 id_cache->id = (void *)new_id;
245 SILC_LOG_DEBUG(("Found"));
250 /* Removes and free's server entry from ID list */
252 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
255 /* Remove from cache */
257 if (!silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER,
262 if (entry->server_name)
263 silc_free(entry->server_name);
265 silc_free(entry->id);
267 memset(entry, 'F', sizeof(*entry));
275 /******************************************************************************
277 Client entry functions
279 ******************************************************************************/
281 /* Add new client entry. This adds the client entry to ID cache system
282 and returns the allocated client entry or NULL on error. This is
283 called when new client connection is accepted to the server. If The
284 `router' is provided then the all server routines assume that the client
285 is not directly connected local client but it has router set and is
286 remote. If this is the case then `connection' must be NULL. If, on the
287 other hand, the `connection' is provided then the client is assumed
288 to be directly connected local client and `router' must be NULL. */
291 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname,
292 uint32 nickname_len, char *username,
293 char *userinfo, SilcClientID *id,
294 SilcServerEntry router, void *connection)
296 SilcClientEntry client;
298 SILC_LOG_DEBUG(("Adding new client entry"));
300 client = silc_calloc(1, sizeof(*client));
301 client->nickname = nickname;
302 client->username = username;
303 client->userinfo = userinfo;
305 client->router = router;
306 client->connection = connection;
307 silc_list_init(client->channels, struct SilcChannelClientEntryStruct,
310 if (!silc_idcache_add(id_list->clients, nickname, nickname_len,
311 SILC_ID_CLIENT, (void *)client->id,
312 (void *)client, TRUE, FALSE)) {
320 /* Free client entry. This free's everything and removes the entry
321 from ID cache. Call silc_idlist_del_data before calling this one. */
323 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
326 /* Remove from cache */
328 if (!silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT,
334 silc_free(entry->nickname);
336 silc_free(entry->username);
338 silc_free(entry->userinfo);
340 silc_free(entry->id);
342 memset(entry, 'F', sizeof(*entry));
351 /* Returns all clients matching requested nickname. Number of clients is
352 returned to `clients_count'. Caller must free the returned table. */
354 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
356 SilcClientEntry **clients,
357 uint32 *clients_count)
359 SilcIDCacheList list = NULL;
360 SilcIDCacheEntry id_cache = NULL;
363 SILC_LOG_DEBUG(("Start"));
365 if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
368 *clients = silc_realloc(*clients,
369 (silc_idcache_list_count(list) + *clients_count) *
373 silc_idcache_list_first(list, &id_cache);
374 (*clients)[i++] = (SilcClientEntry)id_cache->context;
376 while (silc_idcache_list_next(list, &id_cache))
377 (*clients)[i++] = (SilcClientEntry)id_cache->context;
379 silc_idcache_list_free(list);
386 /* Returns all clients matching requested nickname. Number of clients is
387 returned to `clients_count'. Caller must free the returned table. */
388 /* XXX This actually checks the data, which can be hash of the nickname
389 but is not if the client is local client. Global client on global
390 list may have hash. Thus, this is not fully reliable function.
391 Instead this should probably check the hash from the list of client ID's. */
393 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
395 SilcClientEntry **clients,
396 uint32 *clients_count)
398 SilcIDCacheList list = NULL;
399 SilcIDCacheEntry id_cache = NULL;
400 unsigned char hash[32];
403 SILC_LOG_DEBUG(("Start"));
405 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
407 if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
410 *clients = silc_realloc(*clients,
411 (silc_idcache_list_count(list) + *clients_count) *
415 silc_idcache_list_first(list, &id_cache);
416 (*clients)[i++] = (SilcClientEntry)id_cache->context;
418 while (silc_idcache_list_next(list, &id_cache))
419 (*clients)[i++] = (SilcClientEntry)id_cache->context;
421 silc_idcache_list_free(list);
428 /* Finds client by nickname hash. */
431 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
432 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
434 SilcIDCacheList list = NULL;
435 SilcIDCacheEntry id_cache = NULL;
436 SilcClientEntry client = NULL;
437 unsigned char hash[32];
439 SILC_LOG_DEBUG(("Client by hash"));
441 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
443 if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY,
444 SILC_ID_CLIENT, &list))
447 if (!silc_idcache_list_first(list, &id_cache)) {
448 silc_idcache_list_free(list);
453 client = (SilcClientEntry)id_cache->context;
455 if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
461 if (!silc_idcache_list_next(list, &id_cache))
465 silc_idcache_list_free(list);
468 *ret_entry = id_cache;
470 SILC_LOG_DEBUG(("Found"));
475 /* Finds client by Client ID */
478 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
479 SilcIDCacheEntry *ret_entry)
481 SilcIDCacheEntry id_cache = NULL;
482 SilcClientEntry client;
487 SILC_LOG_DEBUG(("Client ID (%s)",
488 silc_id_render(id, SILC_ID_CLIENT)));
490 if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id,
491 SILC_ID_CLIENT, &id_cache))
494 client = (SilcClientEntry)id_cache->context;
497 *ret_entry = id_cache;
499 SILC_LOG_DEBUG(("Found"));
504 /* Replaces old Client ID with new one */
507 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
508 SilcClientID *new_id)
510 SilcIDCacheEntry id_cache = NULL;
511 SilcClientEntry client;
513 if (!old_id || !new_id)
516 SILC_LOG_DEBUG(("Replacing Client ID"));
518 if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id,
519 SILC_ID_CLIENT, &id_cache))
522 client = (SilcClientEntry)id_cache->context;
523 silc_free(client->id);
525 id_cache->id = (void *)new_id;
527 /* If the old ID Cache data was the hash value of the old Client ID
528 replace it with the hash of new Client ID */
529 if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
530 silc_free(id_cache->data);
531 id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
532 memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
533 silc_idcache_sort_by_data(id_list->clients);
536 SILC_LOG_DEBUG(("Replaced"));
541 /* Client cache entry destructor that is called when the cache is purged. */
543 void silc_idlist_client_destructor(SilcIDCache cache,
544 SilcIDCacheEntry entry)
546 SilcClientEntry client;
548 SILC_LOG_DEBUG(("Start"));
550 client = (SilcClientEntry)entry->context;
552 if (client->nickname)
553 silc_free(client->nickname);
554 if (client->username)
555 silc_free(client->username);
556 if (client->userinfo)
557 silc_free(client->userinfo);
559 silc_free(client->id);
561 memset(client, 'F', sizeof(*client));
566 /******************************************************************************
568 Channel entry functions
570 ******************************************************************************/
572 /* Add new channel entry. This add the new channel entry to the ID cache
573 system and returns the allocated entry or NULL on error. */
576 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
577 SilcChannelID *id, SilcServerEntry router,
578 SilcCipher channel_key, SilcHmac hmac)
580 SilcChannelEntry channel;
582 channel = silc_calloc(1, sizeof(*channel));
583 channel->channel_name = channel_name;
584 channel->mode = mode;
586 channel->router = router;
587 channel->channel_key = channel_key;
588 channel->hmac = hmac;
590 if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
595 silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct,
598 if (!silc_idcache_add(id_list->channels, channel->channel_name,
599 channel->channel_name ? strlen(channel->channel_name) :
601 (void *)channel->id, (void *)channel, TRUE, FALSE)) {
609 /* Free channel entry. This free's everything. */
611 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
614 SilcChannelClientEntry chl;
616 /* Remove from cache */
618 if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL,
623 if (entry->channel_name)
624 silc_free(entry->channel_name);
626 silc_free(entry->id);
628 silc_free(entry->topic);
629 if (entry->channel_key)
630 silc_cipher_free(entry->channel_key);
632 memset(entry->key, 0, entry->key_len / 8);
633 silc_free(entry->key);
636 silc_free(entry->cipher);
637 if (entry->hmac_name)
638 silc_free(entry->hmac_name);
640 silc_free(entry->rekey);
642 /* Free all data, free also any reference from the client's channel
643 list since they share the same memory. */
644 silc_list_start(entry->user_list);
645 while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
646 silc_list_del(chl->client->channels, chl);
647 silc_list_del(entry->user_list, chl);
651 memset(entry, 'F', sizeof(*entry));
659 /* Finds channel by channel name. Channel names are unique and they
660 are not case-sensitive. */
663 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
664 SilcIDCacheEntry *ret_entry)
666 SilcIDCacheList list = NULL;
667 SilcIDCacheEntry id_cache = NULL;
668 SilcChannelEntry channel;
670 SILC_LOG_DEBUG(("Channel by name"));
672 if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
675 if (!silc_idcache_list_first(list, &id_cache)) {
676 silc_idcache_list_free(list);
680 channel = (SilcChannelEntry)id_cache->context;
683 *ret_entry = id_cache;
685 silc_idcache_list_free(list);
687 SILC_LOG_DEBUG(("Found"));
692 /* Finds channel by Channel ID. */
695 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
696 SilcIDCacheEntry *ret_entry)
698 SilcIDCacheEntry id_cache = NULL;
699 SilcChannelEntry channel;
704 SILC_LOG_DEBUG(("Channel ID (%s)",
705 silc_id_render(id, SILC_ID_CHANNEL)));
707 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id,
708 SILC_ID_CHANNEL, &id_cache))
711 channel = (SilcChannelEntry)id_cache->context;
714 *ret_entry = id_cache;
716 SILC_LOG_DEBUG(("Found"));
721 /* Replaces old Channel ID with new one. This is done when router forces
722 normal server to change Channel ID. */
725 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
726 SilcChannelID *new_id)
728 SilcIDCacheEntry id_cache = NULL;
729 SilcChannelEntry channel;
731 if (!old_id || !new_id)
734 SILC_LOG_DEBUG(("Replacing Channel ID"));
736 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
737 SILC_ID_CHANNEL, &id_cache))
740 channel = (SilcChannelEntry)id_cache->context;
741 silc_free(channel->id);
742 channel->id = new_id;
743 id_cache->id = (void *)new_id;
745 SILC_LOG_DEBUG(("Replaced"));
750 /* Returns channels from the ID list. If the `channel_id' is NULL then
751 all channels are returned. */
754 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
755 uint32 *channels_count)
757 SilcIDCacheList list = NULL;
758 SilcIDCacheEntry id_cache = NULL;
759 SilcChannelEntry *channels;
762 SILC_LOG_DEBUG(("Start"));
764 if (!silc_idcache_find_by_id(id_list->channels, channel_id ? channel_id :
765 SILC_ID_CACHE_ANY, SILC_ID_CHANNEL, &list))
768 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
771 silc_idcache_list_first(list, &id_cache);
772 channels[i++] = (SilcChannelEntry)id_cache->context;
774 while (silc_idcache_list_next(list, &id_cache))
775 channels[i++] = (SilcChannelEntry)id_cache->context;
777 silc_idcache_list_free(list);