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->rekey = idata->rekey;
40 data->hash = idata->hash;
41 data->hmac_send = idata->hmac_send;
42 data->hmac_receive = idata->hmac_receive;
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 if (idata->rekey->send_enc_key) {
60 memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
61 silc_free(idata->rekey->send_enc_key);
63 silc_free(idata->rekey);
65 if (idata->hmac_send) /* Same as idata->hmac_receive */
66 silc_hmac_free(idata->hmac_send);
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 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
256 SILC_LOG_DEBUG(("Start"));
259 /* Remove from cache */
261 if (!silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER,
266 if (entry->server_name)
267 silc_free(entry->server_name);
269 silc_free(entry->id);
271 memset(entry, 'F', sizeof(*entry));
279 /******************************************************************************
281 Client entry functions
283 ******************************************************************************/
285 /* Add new client entry. This adds the client entry to ID cache system
286 and returns the allocated client entry or NULL on error. This is
287 called when new client connection is accepted to the server. If The
288 `router' is provided then the all server routines assume that the client
289 is not directly connected local client but it has router set and is
290 remote. If this is the case then `connection' must be NULL. If, on the
291 other hand, the `connection' is provided then the client is assumed
292 to be directly connected local client and `router' must be NULL. */
295 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname,
296 uint32 nickname_len, char *username,
297 char *userinfo, SilcClientID *id,
298 SilcServerEntry router, void *connection)
300 SilcClientEntry client;
302 SILC_LOG_DEBUG(("Adding new client entry"));
304 client = silc_calloc(1, sizeof(*client));
305 client->nickname = nickname;
306 client->username = username;
307 client->userinfo = userinfo;
309 client->router = router;
310 client->connection = connection;
311 silc_list_init(client->channels, struct SilcChannelClientEntryStruct,
314 if (!silc_idcache_add(id_list->clients, nickname, nickname_len,
315 SILC_ID_CLIENT, (void *)client->id,
316 (void *)client, TRUE, FALSE)) {
324 /* Free client entry. This free's everything and removes the entry
325 from ID cache. Call silc_idlist_del_data before calling this one. */
327 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
329 SILC_LOG_DEBUG(("Start"));
332 /* Remove from cache */
334 if (!silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT,
340 silc_free(entry->nickname);
342 silc_free(entry->username);
344 silc_free(entry->userinfo);
346 silc_free(entry->id);
348 memset(entry, 'F', sizeof(*entry));
357 /* Returns all clients matching requested nickname. Number of clients is
358 returned to `clients_count'. Caller must free the returned table. */
360 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
362 SilcClientEntry **clients,
363 uint32 *clients_count)
365 SilcIDCacheList list = NULL;
366 SilcIDCacheEntry id_cache = NULL;
369 SILC_LOG_DEBUG(("Start"));
371 if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
374 *clients = silc_realloc(*clients,
375 (silc_idcache_list_count(list) + *clients_count) *
379 silc_idcache_list_first(list, &id_cache);
380 (*clients)[i++] = (SilcClientEntry)id_cache->context;
382 while (silc_idcache_list_next(list, &id_cache))
383 (*clients)[i++] = (SilcClientEntry)id_cache->context;
385 silc_idcache_list_free(list);
392 /* Returns all clients matching requested nickname. Number of clients is
393 returned to `clients_count'. Caller must free the returned table. */
394 /* XXX This actually checks the data, which can be hash of the nickname
395 but is not if the client is local client. Global client on global
396 list may have hash. Thus, this is not fully reliable function.
397 Instead this should probably check the hash from the list of client ID's. */
399 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
401 SilcClientEntry **clients,
402 uint32 *clients_count)
404 SilcIDCacheList list = NULL;
405 SilcIDCacheEntry id_cache = NULL;
406 unsigned char hash[32];
409 SILC_LOG_DEBUG(("Start"));
411 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
413 if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
416 *clients = silc_realloc(*clients,
417 (silc_idcache_list_count(list) + *clients_count) *
421 silc_idcache_list_first(list, &id_cache);
422 (*clients)[i++] = (SilcClientEntry)id_cache->context;
424 while (silc_idcache_list_next(list, &id_cache))
425 (*clients)[i++] = (SilcClientEntry)id_cache->context;
427 silc_idcache_list_free(list);
434 /* Finds client by nickname hash. */
437 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
438 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
440 SilcIDCacheList list = NULL;
441 SilcIDCacheEntry id_cache = NULL;
442 SilcClientEntry client = NULL;
443 unsigned char hash[32];
445 SILC_LOG_DEBUG(("Client by hash"));
447 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
449 if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY,
450 SILC_ID_CLIENT, &list))
453 if (!silc_idcache_list_first(list, &id_cache)) {
454 silc_idcache_list_free(list);
459 client = (SilcClientEntry)id_cache->context;
461 if (client && SILC_ID_COMPARE_HASH(client->id, hash))
467 if (!silc_idcache_list_next(list, &id_cache))
471 silc_idcache_list_free(list);
474 *ret_entry = id_cache;
476 SILC_LOG_DEBUG(("Found"));
481 /* Finds client by Client ID */
484 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
485 SilcIDCacheEntry *ret_entry)
487 SilcIDCacheEntry id_cache = NULL;
488 SilcClientEntry client;
493 SILC_LOG_DEBUG(("Client ID (%s)",
494 silc_id_render(id, SILC_ID_CLIENT)));
496 if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id,
497 SILC_ID_CLIENT, &id_cache))
500 client = (SilcClientEntry)id_cache->context;
503 *ret_entry = id_cache;
505 SILC_LOG_DEBUG(("Found"));
510 /* Replaces old Client ID with new one */
513 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
514 SilcClientID *new_id)
516 SilcIDCacheEntry id_cache = NULL;
517 SilcClientEntry client;
519 if (!old_id || !new_id)
522 SILC_LOG_DEBUG(("Replacing Client ID"));
524 if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id,
525 SILC_ID_CLIENT, &id_cache))
528 client = (SilcClientEntry)id_cache->context;
529 silc_free(client->id);
531 id_cache->id = (void *)new_id;
533 /* If the old ID Cache data was the hash value of the old Client ID
534 replace it with the hash of new Client ID */
535 if (id_cache->data && SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
536 silc_free(id_cache->data);
537 id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
538 memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
539 silc_idcache_sort_by_data(id_list->clients);
542 SILC_LOG_DEBUG(("Replaced"));
547 /* Client cache entry destructor that is called when the cache is purged. */
549 void silc_idlist_client_destructor(SilcIDCache cache,
550 SilcIDCacheEntry entry)
552 SilcClientEntry client;
554 SILC_LOG_DEBUG(("Start"));
556 client = (SilcClientEntry)entry->context;
558 if (client->nickname)
559 silc_free(client->nickname);
560 if (client->username)
561 silc_free(client->username);
562 if (client->userinfo)
563 silc_free(client->userinfo);
565 silc_free(client->id);
567 memset(client, 'F', sizeof(*client));
572 /******************************************************************************
574 Channel entry functions
576 ******************************************************************************/
578 /* Add new channel entry. This add the new channel entry to the ID cache
579 system and returns the allocated entry or NULL on error. */
582 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
583 SilcChannelID *id, SilcServerEntry router,
584 SilcCipher channel_key, SilcHmac hmac)
586 SilcChannelEntry channel;
588 channel = silc_calloc(1, sizeof(*channel));
589 channel->channel_name = channel_name;
590 channel->mode = mode;
592 channel->router = router;
593 channel->channel_key = channel_key;
594 channel->hmac = hmac;
596 if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
601 silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct,
604 if (!silc_idcache_add(id_list->channels, channel->channel_name,
605 channel->channel_name ? strlen(channel->channel_name) :
607 (void *)channel->id, (void *)channel, TRUE, FALSE)) {
615 /* Free channel entry. This free's everything. */
617 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
619 SILC_LOG_DEBUG(("Start"));
622 SilcChannelClientEntry chl;
624 /* Remove from cache */
626 if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL,
631 if (entry->channel_name)
632 silc_free(entry->channel_name);
634 silc_free(entry->id);
636 silc_free(entry->topic);
637 if (entry->channel_key)
638 silc_cipher_free(entry->channel_key);
640 memset(entry->key, 0, entry->key_len / 8);
641 silc_free(entry->key);
644 silc_free(entry->cipher);
645 if (entry->hmac_name)
646 silc_free(entry->hmac_name);
648 silc_free(entry->rekey);
650 /* Free all data, free also any reference from the client's channel
651 list since they share the same memory. */
652 silc_list_start(entry->user_list);
653 while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
654 silc_list_del(chl->client->channels, chl);
655 silc_list_del(entry->user_list, chl);
659 memset(entry, 'F', sizeof(*entry));
667 /* Finds channel by channel name. Channel names are unique and they
668 are not case-sensitive. */
671 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
672 SilcIDCacheEntry *ret_entry)
674 SilcIDCacheList list = NULL;
675 SilcIDCacheEntry id_cache = NULL;
676 SilcChannelEntry channel;
678 SILC_LOG_DEBUG(("Channel by name"));
680 if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
683 if (!silc_idcache_list_first(list, &id_cache)) {
684 silc_idcache_list_free(list);
688 channel = (SilcChannelEntry)id_cache->context;
691 *ret_entry = id_cache;
693 silc_idcache_list_free(list);
695 SILC_LOG_DEBUG(("Found"));
700 /* Finds channel by Channel ID. */
703 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
704 SilcIDCacheEntry *ret_entry)
706 SilcIDCacheEntry id_cache = NULL;
707 SilcChannelEntry channel;
712 SILC_LOG_DEBUG(("Channel ID (%s)",
713 silc_id_render(id, SILC_ID_CHANNEL)));
715 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id,
716 SILC_ID_CHANNEL, &id_cache))
719 channel = (SilcChannelEntry)id_cache->context;
722 *ret_entry = id_cache;
724 SILC_LOG_DEBUG(("Found"));
729 /* Replaces old Channel ID with new one. This is done when router forces
730 normal server to change Channel ID. */
733 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
734 SilcChannelID *new_id)
736 SilcIDCacheEntry id_cache = NULL;
737 SilcChannelEntry channel;
739 if (!old_id || !new_id)
742 SILC_LOG_DEBUG(("Replacing Channel ID"));
744 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
745 SILC_ID_CHANNEL, &id_cache))
748 channel = (SilcChannelEntry)id_cache->context;
749 silc_free(channel->id);
750 channel->id = new_id;
751 id_cache->id = (void *)new_id;
753 SILC_LOG_DEBUG(("Replaced"));
758 /* Returns channels from the ID list. If the `channel_id' is NULL then
759 all channels are returned. */
762 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
763 uint32 *channels_count)
765 SilcIDCacheList list = NULL;
766 SilcIDCacheEntry id_cache = NULL;
767 SilcChannelEntry *channels;
770 SILC_LOG_DEBUG(("Start"));
772 if (!silc_idcache_find_by_id(id_list->channels, channel_id ? channel_id :
773 SILC_ID_CACHE_ANY, SILC_ID_CHANNEL, &list))
776 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
779 silc_idcache_list_first(list, &id_cache);
780 channels[i++] = (SilcChannelEntry)id_cache->context;
782 while (silc_idcache_list_next(list, &id_cache))
783 channels[i++] = (SilcChannelEntry)id_cache->context;
785 silc_idcache_list_free(list);