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->pkcs = idata->pkcs;
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);
60 silc_hmac_free(idata->hmac);
61 if (idata->hmac_key) {
62 memset(idata->hmac_key, 0, idata->hmac_key_len);
63 silc_free(idata->hmac_key);
66 silc_pkcs_free(idata->pkcs);
67 if (idata->public_key)
68 silc_pkcs_public_key_free(idata->public_key);
73 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
75 SilcIDListPurge i = (SilcIDListPurge)context;
77 SILC_LOG_DEBUG(("Start"));
79 silc_idcache_purge(i->cache);
80 silc_task_register(i->timeout_queue, 0,
83 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
86 /******************************************************************************
88 Server entry functions
90 ******************************************************************************/
92 /* Add new server entry. This adds the new server entry to ID cache and
93 returns the allocated entry object or NULL on error. This is called
94 when new server connects to us. We also add ourselves to cache with
98 silc_idlist_add_server(SilcIDList id_list,
99 char *server_name, int server_type,
100 SilcServerID *id, SilcServerEntry router,
103 SilcServerEntry server;
105 SILC_LOG_DEBUG(("Adding new server entry"));
107 server = silc_calloc(1, sizeof(*server));
108 server->server_name = server_name;
109 server->server_type = server_type;
111 server->router = router;
112 server->connection = connection;
114 if (!silc_idcache_add(id_list->servers, server->server_name,
115 server->server_name ? strlen(server->server_name) : 0,
116 SILC_ID_SERVER, (void *)server->id,
117 (void *)server, TRUE, FALSE)) {
125 /* Finds server by Server ID */
128 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
129 SilcIDCacheEntry *ret_entry)
131 SilcIDCacheEntry id_cache = NULL;
132 SilcServerEntry server;
137 SILC_LOG_DEBUG(("Server ID (%s)",
138 silc_id_render(id, SILC_ID_SERVER)));
140 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
141 SILC_ID_SERVER, &id_cache))
144 server = (SilcServerEntry)id_cache->context;
147 *ret_entry = id_cache;
152 /* Find server by name */
155 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
156 SilcIDCacheEntry *ret_entry)
158 SilcIDCacheEntry id_cache = NULL;
159 SilcServerEntry server;
161 SILC_LOG_DEBUG(("Server by name `%s'", name));
163 if (!silc_idcache_find_by_data_one(id_list->servers, name, &id_cache))
166 server = (SilcServerEntry)id_cache->context;
169 *ret_entry = id_cache;
171 SILC_LOG_DEBUG(("Found"));
176 /* Find server by connection parameters, hostname and port */
179 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
180 int port, SilcIDCacheEntry *ret_entry)
182 SilcIDCacheList list = NULL;
183 SilcIDCacheEntry id_cache = NULL;
184 SilcServerEntry server = NULL;
185 SilcSocketConnection sock;
187 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
189 if (!silc_idcache_find_by_id(id_list->servers, SILC_ID_CACHE_ANY,
190 SILC_ID_SERVER, &list))
193 if (!silc_idcache_list_first(list, &id_cache)) {
194 silc_idcache_list_free(list);
199 server = (SilcServerEntry)id_cache->context;
200 sock = (SilcSocketConnection)server->connection;
202 if (sock && ((sock->hostname && !strcmp(sock->hostname, hostname)) ||
203 (sock->ip && !strcmp(sock->ip, hostname)))
204 && sock->port == port)
210 if (!silc_idcache_list_next(list, &id_cache))
214 silc_idcache_list_free(list);
217 *ret_entry = id_cache;
219 SILC_LOG_DEBUG(("Found"));
224 /* Replaces old Server ID with new one */
227 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
228 SilcServerID *new_id)
230 SilcIDCacheEntry id_cache = NULL;
231 SilcServerEntry server;
233 if (!old_id || !new_id)
236 SILC_LOG_DEBUG(("Replacing Server ID"));
238 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
239 SILC_ID_SERVER, &id_cache))
242 server = (SilcServerEntry)id_cache->context;
243 silc_free(server->id);
245 id_cache->id = (void *)new_id;
247 SILC_LOG_DEBUG(("Found"));
252 /* Removes and free's server entry from ID list */
254 void silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
257 /* Remove from cache */
259 silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER,
263 if (entry->server_name)
264 silc_free(entry->server_name);
266 silc_free(entry->id);
268 memset(entry, 'F', sizeof(*entry));
273 /******************************************************************************
275 Client entry functions
277 ******************************************************************************/
279 /* Add new client entry. This adds the client entry to ID cache system
280 and returns the allocated client entry or NULL on error. This is
281 called when new client connection is accepted to the server. If The
282 `router' is provided then the all server routines assume that the client
283 is not directly connected local client but it has router set and is
284 remote. If this is the case then `connection' must be NULL. If, on the
285 other hand, the `connection' is provided then the client is assumed
286 to be directly connected local client and `router' must be NULL. */
289 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname,
290 unsigned int nickname_len, char *username,
291 char *userinfo, SilcClientID *id,
292 SilcServerEntry router, void *connection)
294 SilcClientEntry client;
296 SILC_LOG_DEBUG(("Adding new client entry"));
298 client = silc_calloc(1, sizeof(*client));
299 client->nickname = nickname;
300 client->username = username;
301 client->userinfo = userinfo;
303 client->router = router;
304 client->connection = connection;
305 silc_list_init(client->channels, struct SilcChannelClientEntryStruct,
308 if (!silc_idcache_add(id_list->clients, nickname, nickname_len,
309 SILC_ID_CLIENT, (void *)client->id,
310 (void *)client, TRUE, FALSE)) {
318 /* Free client entry. This free's everything and removes the entry
319 from ID cache. Call silc_idlist_del_data before calling this one. */
321 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
324 /* Remove from cache */
326 if (!silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT,
332 silc_free(entry->nickname);
334 silc_free(entry->username);
336 silc_free(entry->userinfo);
338 silc_free(entry->id);
340 memset(entry, 'F', sizeof(*entry));
349 /* Returns all clients matching requested nickname. Number of clients is
350 returned to `clients_count'. Caller must free the returned table. */
353 silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
354 char *server, unsigned int *clients_count)
356 SilcIDCacheList list = NULL;
357 SilcIDCacheEntry id_cache = NULL;
358 SilcClientEntry *clients;
361 SILC_LOG_DEBUG(("Start"));
363 if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
366 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
369 silc_idcache_list_first(list, &id_cache);
370 clients[i++] = (SilcClientEntry)id_cache->context;
372 while (silc_idcache_list_next(list, &id_cache))
373 clients[i++] = (SilcClientEntry)id_cache->context;
375 silc_idcache_list_free(list);
383 /* Returns all clients matching requested nickname. Number of clients is
384 returned to `clients_count'. Caller must free the returned table. */
385 /* XXX This actually checks the data, which can be hash of the nickname
386 but is not if the client is local client. Global client on global
387 list may have hash. Thus, this is not fully reliable function.
388 Instead this should probably check the hash from the lists client ID's. */
391 silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
393 unsigned int *clients_count)
395 SilcIDCacheList list = NULL;
396 SilcIDCacheEntry id_cache = NULL;
397 SilcClientEntry *clients;
398 unsigned char hash[32];
401 SILC_LOG_DEBUG(("Start"));
403 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
405 if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
408 clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
411 silc_idcache_list_first(list, &id_cache);
412 clients[i++] = (SilcClientEntry)id_cache->context;
414 while (silc_idcache_list_next(list, &id_cache))
415 clients[i++] = (SilcClientEntry)id_cache->context;
417 silc_idcache_list_free(list);
425 /* Finds client entry by nickname. */
428 silc_idlist_find_client_by_nickname(SilcIDList id_list, char *nickname,
429 char *server, SilcIDCacheEntry *ret_entry)
431 SilcIDCacheList list = NULL;
432 SilcIDCacheEntry id_cache = NULL;
433 SilcClientEntry client = NULL;
435 SILC_LOG_DEBUG(("Client by nickname"));
438 if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
442 while (silc_idcache_list_next(list, &id_cache)) {
443 client = (SilcClientEntry)id_cache->context;
445 if (!strcmp(server, XXX, strlen(server)))
452 silc_idcache_list_free(list);
457 if (!silc_idcache_find_by_data_one(id_list->clients, nickname, &id_cache))
460 client = (SilcClientEntry)id_cache->context;
463 *ret_entry = id_cache;
466 SILC_LOG_DEBUG(("Found"));
471 /* Finds client by nickname hash. */
474 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
475 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
477 SilcIDCacheList list = NULL;
478 SilcIDCacheEntry id_cache = NULL;
479 SilcClientEntry client = NULL;
480 unsigned char hash[32];
482 SILC_LOG_DEBUG(("Client by hash"));
484 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
486 if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY,
487 SILC_ID_CLIENT, &list))
490 if (!silc_idcache_list_first(list, &id_cache)) {
491 silc_idcache_list_free(list);
496 client = (SilcClientEntry)id_cache->context;
498 if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
504 if (!silc_idcache_list_next(list, &id_cache))
508 silc_idcache_list_free(list);
511 *ret_entry = id_cache;
513 SILC_LOG_DEBUG(("Found"));
518 /* Finds client by Client ID */
521 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
522 SilcIDCacheEntry *ret_entry)
524 SilcIDCacheEntry id_cache = NULL;
525 SilcClientEntry client;
530 SILC_LOG_DEBUG(("Client ID (%s)",
531 silc_id_render(id, SILC_ID_CLIENT)));
533 if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id,
534 SILC_ID_CLIENT, &id_cache))
537 client = (SilcClientEntry)id_cache->context;
540 *ret_entry = id_cache;
542 SILC_LOG_DEBUG(("Found"));
547 /* Replaces old Client ID with new one */
550 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
551 SilcClientID *new_id)
553 SilcIDCacheEntry id_cache = NULL;
554 SilcClientEntry client;
556 if (!old_id || !new_id)
559 SILC_LOG_DEBUG(("Replacing Client ID"));
561 if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id,
562 SILC_ID_CLIENT, &id_cache))
565 client = (SilcClientEntry)id_cache->context;
566 silc_free(client->id);
568 id_cache->id = (void *)new_id;
570 /* If the old ID Cache data was the hash value of the old Client ID
571 replace it with the hash of new Client ID */
572 if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
573 silc_free(id_cache->data);
574 id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
575 memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
576 silc_idcache_sort_by_data(id_list->clients);
579 SILC_LOG_DEBUG(("Replaced"));
584 /* Client cache entry destructor that is called when the cache is purged. */
586 void silc_idlist_client_destructor(SilcIDCache cache,
587 SilcIDCacheEntry entry)
589 SilcClientEntry client;
591 SILC_LOG_DEBUG(("Start"));
593 client = (SilcClientEntry)entry->context;
595 if (client->nickname)
596 silc_free(client->nickname);
597 if (client->username)
598 silc_free(client->username);
599 if (client->userinfo)
600 silc_free(client->userinfo);
602 silc_free(client->id);
604 memset(client, 'F', sizeof(*client));
609 /******************************************************************************
611 Channel entry functions
613 ******************************************************************************/
615 /* Add new channel entry. This add the new channel entry to the ID cache
616 system and returns the allocated entry or NULL on error. */
619 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
620 SilcChannelID *id, SilcServerEntry router,
621 SilcCipher channel_key, SilcHmac hmac)
623 SilcChannelEntry channel;
625 channel = silc_calloc(1, sizeof(*channel));
626 channel->channel_name = channel_name;
627 channel->mode = mode;
629 channel->router = router;
630 channel->channel_key = channel_key;
631 channel->hmac = hmac;
633 if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
638 silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct,
641 if (!silc_idcache_add(id_list->channels, channel->channel_name,
642 channel->channel_name ? strlen(channel->channel_name) :
644 (void *)channel->id, (void *)channel, TRUE, FALSE)) {
652 /* Free channel entry. This free's everything. */
654 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
657 SilcChannelClientEntry chl;
659 /* Remove from cache */
661 if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL,
666 if (entry->channel_name)
667 silc_free(entry->channel_name);
669 silc_free(entry->id);
671 silc_free(entry->topic);
672 if (entry->channel_key)
673 silc_cipher_free(entry->channel_key);
675 memset(entry->key, 0, entry->key_len / 8);
676 silc_free(entry->key);
679 /* Free all data, free also any reference from the client's channel
680 list since they share the same memory. */
681 silc_list_start(entry->user_list);
682 while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
683 silc_list_del(chl->client->channels, chl);
684 silc_list_del(entry->user_list, chl);
688 memset(entry, 'F', sizeof(*entry));
696 /* Finds channel by channel name. Channel names are unique and they
697 are not case-sensitive. */
700 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
701 SilcIDCacheEntry *ret_entry)
703 SilcIDCacheList list = NULL;
704 SilcIDCacheEntry id_cache = NULL;
705 SilcChannelEntry channel;
707 SILC_LOG_DEBUG(("Channel by name"));
709 if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
712 if (!silc_idcache_list_first(list, &id_cache)) {
713 silc_idcache_list_free(list);
717 channel = (SilcChannelEntry)id_cache->context;
720 *ret_entry = id_cache;
722 silc_idcache_list_free(list);
724 SILC_LOG_DEBUG(("Found"));
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,
745 SILC_ID_CHANNEL, &id_cache))
748 channel = (SilcChannelEntry)id_cache->context;
751 *ret_entry = id_cache;
753 SILC_LOG_DEBUG(("Found"));
758 /* Replaces old Channel ID with new one. This is done when router forces
759 normal server to change Channel ID. */
762 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
763 SilcChannelID *new_id)
765 SilcIDCacheEntry id_cache = NULL;
766 SilcChannelEntry channel;
768 if (!old_id || !new_id)
771 SILC_LOG_DEBUG(("Replacing Channel ID"));
773 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
774 SILC_ID_CHANNEL, &id_cache))
777 channel = (SilcChannelEntry)id_cache->context;
778 silc_free(channel->id);
779 channel->id = new_id;
780 id_cache->id = (void *)new_id;
782 SILC_LOG_DEBUG(("Replaced"));
787 /* Returns channels from the ID list. If the `channel_id' is NULL then
788 all channels are returned. */
791 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
792 unsigned int *channels_count)
794 SilcIDCacheList list = NULL;
795 SilcIDCacheEntry id_cache = NULL;
796 SilcChannelEntry *channels;
799 SILC_LOG_DEBUG(("Start"));
801 if (!silc_idcache_find_by_id(id_list->channels, channel_id ? channel_id :
802 SILC_ID_CACHE_ANY, SILC_ID_CHANNEL, &list))
805 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
808 silc_idcache_list_first(list, &id_cache);
809 channels[i++] = (SilcChannelEntry)id_cache->context;
811 while (silc_idcache_list_next(list, &id_cache))
812 channels[i++] = (SilcChannelEntry)id_cache->context;
814 silc_idcache_list_free(list);