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 (void *)server->id, (void *)server, 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,
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_name_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_get_all(id_list->servers, &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 && !strcasecmp(sock->hostname, hostname)) ||
200 (sock->ip && !strcasecmp(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,
239 server = (SilcServerEntry)id_cache->context;
240 silc_free(server->id);
243 /* Remove the old entry and add a new one */
244 silc_idcache_del_by_context(id_list->servers, server);
245 silc_idcache_add(id_list->servers, server->server_name, server->id,
248 SILC_LOG_DEBUG(("Found"));
253 /* Removes and free's server entry from ID list */
255 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
257 SILC_LOG_DEBUG(("Start"));
260 /* Remove from cache */
262 if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
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, char *nickname, char *username,
296 char *userinfo, SilcClientID *id,
297 SilcServerEntry router, void *connection)
299 SilcClientEntry client;
301 SILC_LOG_DEBUG(("Adding new client entry"));
303 client = silc_calloc(1, sizeof(*client));
304 client->nickname = nickname;
305 client->username = username;
306 client->userinfo = userinfo;
308 client->router = router;
309 client->connection = connection;
310 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
311 NULL, NULL, NULL, NULL, TRUE);
313 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
314 (void *)client, FALSE)) {
315 silc_hash_table_free(client->channels);
323 /* Free client entry. This free's everything and removes the entry
324 from ID cache. Call silc_idlist_del_data before calling this one. */
326 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
328 SILC_LOG_DEBUG(("Start"));
331 /* Remove from cache */
333 if (!silc_idcache_del_by_context(id_list->clients, entry))
338 silc_free(entry->nickname);
340 silc_free(entry->username);
342 silc_free(entry->userinfo);
344 silc_free(entry->id);
346 memset(entry, 'F', sizeof(*entry));
355 /* Returns all clients matching requested nickname. Number of clients is
356 returned to `clients_count'. Caller must free the returned table. */
358 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
360 SilcClientEntry **clients,
361 uint32 *clients_count)
363 SilcIDCacheList list = NULL;
364 SilcIDCacheEntry id_cache = NULL;
367 SILC_LOG_DEBUG(("Start"));
369 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
372 *clients = silc_realloc(*clients,
373 (silc_idcache_list_count(list) + *clients_count) *
377 silc_idcache_list_first(list, &id_cache);
378 (*clients)[i++] = (SilcClientEntry)id_cache->context;
380 while (silc_idcache_list_next(list, &id_cache))
381 (*clients)[i++] = (SilcClientEntry)id_cache->context;
383 silc_idcache_list_free(list);
387 SILC_LOG_DEBUG(("Found %d clients", *clients_count));
392 /* Returns all clients matching requested nickname hash. Number of clients
393 is returned to `clients_count'. Caller must free the returned table. */
395 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
397 SilcClientEntry **clients,
398 uint32 *clients_count)
400 SilcIDCacheList list = NULL;
401 SilcIDCacheEntry id_cache = NULL;
402 unsigned char hash[32];
404 SilcClientID client_id;
406 SILC_LOG_DEBUG(("Start"));
408 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
410 /* As the Client ID is hashed in the ID cache by hashing only the hash
411 from the Client ID, we can do a lookup with only the hash not the
412 other parts of the ID and get all the clients with that hash, ie.
413 with that nickname, as the hash is from the nickname. */
414 memset(&client_id, 0, sizeof(client_id));
415 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
416 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
419 *clients = silc_realloc(*clients,
420 (silc_idcache_list_count(list) + *clients_count) *
424 silc_idcache_list_first(list, &id_cache);
425 (*clients)[i++] = (SilcClientEntry)id_cache->context;
427 while (silc_idcache_list_next(list, &id_cache))
428 (*clients)[i++] = (SilcClientEntry)id_cache->context;
430 silc_idcache_list_free(list);
434 SILC_LOG_DEBUG(("Found %d clients", *clients_count));
439 /* Finds client by Client ID */
442 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
443 SilcIDCacheEntry *ret_entry)
445 SilcIDCacheEntry id_cache = NULL;
446 SilcClientEntry client;
451 SILC_LOG_DEBUG(("Client ID (%s)",
452 silc_id_render(id, SILC_ID_CLIENT)));
454 /* Do extended search since the normal ID comparison function for
455 Client ID's compares only the hash from the Client ID and not the
456 entire ID. The silc_hash_client_id_compare compares the entire
457 Client ID as we want to find one specific Client ID. */
458 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
460 silc_hash_client_id_compare, NULL,
464 client = (SilcClientEntry)id_cache->context;
467 *ret_entry = id_cache;
469 SILC_LOG_DEBUG(("Found"));
474 /* Replaces old Client ID with new one */
477 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
478 SilcClientID *new_id)
480 SilcIDCacheEntry id_cache = NULL;
481 SilcClientEntry client;
483 if (!old_id || !new_id)
486 SILC_LOG_DEBUG(("Replacing Client ID"));
488 /* Do extended search since the normal ID comparison function for
489 Client ID's compares only the hash from the Client ID and not the
490 entire ID. The silc_hash_client_id_compare compares the entire
491 Client ID as we want to find one specific Client ID. */
492 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
494 silc_hash_client_id_compare, NULL,
498 client = (SilcClientEntry)id_cache->context;
499 silc_free(client->id);
502 /* Remove the old entry and add a new one */
503 silc_idcache_del_by_context(id_list->clients, client);
504 silc_idcache_add(id_list->clients, client->nickname, client->id,
507 SILC_LOG_DEBUG(("Replaced"));
512 /* Client cache entry destructor that is called when the cache is purged. */
514 void silc_idlist_client_destructor(SilcIDCache cache,
515 SilcIDCacheEntry entry)
517 SilcClientEntry client;
519 SILC_LOG_DEBUG(("Start"));
521 client = (SilcClientEntry)entry->context;
523 if (client->nickname)
524 silc_free(client->nickname);
525 if (client->username)
526 silc_free(client->username);
527 if (client->userinfo)
528 silc_free(client->userinfo);
530 silc_free(client->id);
532 memset(client, 'F', sizeof(*client));
537 /******************************************************************************
539 Channel entry functions
541 ******************************************************************************/
543 /* Add new channel entry. This add the new channel entry to the ID cache
544 system and returns the allocated entry or NULL on error. */
547 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
548 SilcChannelID *id, SilcServerEntry router,
549 SilcCipher channel_key, SilcHmac hmac)
551 SilcChannelEntry channel;
553 channel = silc_calloc(1, sizeof(*channel));
554 channel->channel_name = channel_name;
555 channel->mode = mode;
557 channel->router = router;
558 channel->channel_key = channel_key;
559 channel->hmac = hmac;
561 if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
566 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
567 NULL, NULL, NULL, TRUE);
569 if (!silc_idcache_add(id_list->channels, channel->channel_name,
570 (void *)channel->id, (void *)channel, FALSE)) {
571 silc_hmac_free(channel->hmac);
572 silc_hash_table_free(channel->user_list);
580 /* Foreach callbcak to free all users from the channel when deleting a
583 static void silc_idlist_del_channel_foreach(void *key, void *context,
586 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
588 /* Remove the context from the client's channel hash table as that
589 table and channel's user_list hash table share this same context. */
590 silc_hash_table_del_by_context(chl->client->channels, chl->channel, chl);
594 /* Free channel entry. This free's everything. */
596 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
598 SILC_LOG_DEBUG(("Start"));
601 /* Remove from cache */
603 if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
607 if (entry->channel_name)
608 silc_free(entry->channel_name);
610 silc_free(entry->id);
612 silc_free(entry->topic);
613 if (entry->channel_key)
614 silc_cipher_free(entry->channel_key);
616 memset(entry->key, 0, entry->key_len / 8);
617 silc_free(entry->key);
620 silc_free(entry->cipher);
621 if (entry->hmac_name)
622 silc_free(entry->hmac_name);
624 silc_free(entry->rekey);
626 /* Free all client entrys from the users list. The silc_hash_table_free
627 will free all the entries so they are not freed at the foreach
629 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
631 silc_hash_table_free(entry->user_list);
633 memset(entry, 'F', sizeof(*entry));
641 /* Finds channel by channel name. Channel names are unique and they
642 are not case-sensitive. */
645 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
646 SilcIDCacheEntry *ret_entry)
648 SilcIDCacheEntry id_cache = NULL;
650 SILC_LOG_DEBUG(("Channel by name"));
652 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
656 *ret_entry = id_cache;
658 SILC_LOG_DEBUG(("Found"));
660 return id_cache->context;
663 /* Finds channel by Channel ID. */
666 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
667 SilcIDCacheEntry *ret_entry)
669 SilcIDCacheEntry id_cache = NULL;
670 SilcChannelEntry channel;
675 SILC_LOG_DEBUG(("Channel ID (%s)",
676 silc_id_render(id, SILC_ID_CHANNEL)));
678 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
681 channel = (SilcChannelEntry)id_cache->context;
684 *ret_entry = id_cache;
686 SILC_LOG_DEBUG(("Found"));
691 /* Replaces old Channel ID with new one. This is done when router forces
692 normal server to change Channel ID. */
695 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
696 SilcChannelID *new_id)
698 SilcIDCacheEntry id_cache = NULL;
699 SilcChannelEntry channel;
701 if (!old_id || !new_id)
704 SILC_LOG_DEBUG(("Replacing Channel ID"));
706 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
710 channel = (SilcChannelEntry)id_cache->context;
711 silc_free(channel->id);
712 channel->id = new_id;
714 /* Remove the old entry and add a new one */
715 silc_idcache_del_by_context(id_list->channels, channel);
716 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
719 SILC_LOG_DEBUG(("Replaced"));
724 /* Returns channels from the ID list. If the `channel_id' is NULL then
725 all channels are returned. */
728 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
729 uint32 *channels_count)
731 SilcIDCacheList list = NULL;
732 SilcIDCacheEntry id_cache = NULL;
733 SilcChannelEntry *channels = NULL;
736 SILC_LOG_DEBUG(("Start"));
739 if (!silc_idcache_get_all(id_list->channels, &list))
742 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
745 silc_idcache_list_first(list, &id_cache);
746 channels[i++] = (SilcChannelEntry)id_cache->context;
748 while (silc_idcache_list_next(list, &id_cache))
749 channels[i++] = (SilcChannelEntry)id_cache->context;
751 silc_idcache_list_free(list);
753 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
757 channels = silc_calloc(1, sizeof(*channels));
758 channels[0] = (SilcChannelEntry)id_cache->context;