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->hmac_send = idata->hmac_send;
40 data->hmac_receive = idata->hmac_receive;
41 data->psn_send = idata->psn_send;
42 data->psn_receive = idata->psn_receive;
43 data->hash = idata->hash;
44 data->public_key = idata->public_key;
45 data->rekey = idata->rekey;
46 data->last_receive = idata->last_receive;
47 data->last_sent = idata->last_sent;
48 data->status = idata->status;
50 data->created = time(0); /* Update creation time */
53 /* Free's all data in the common ID entry data structure. */
55 void silc_idlist_del_data(void *entry)
57 SilcIDListData idata = (SilcIDListData)entry;
59 silc_cipher_free(idata->send_key);
60 if (idata->receive_key)
61 silc_cipher_free(idata->receive_key);
63 if (idata->rekey->send_enc_key) {
64 memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
65 silc_free(idata->rekey->send_enc_key);
67 silc_free(idata->rekey);
70 silc_hmac_free(idata->hmac_send);
71 if (idata->hmac_receive)
72 silc_hmac_free(idata->hmac_receive);
73 if (idata->public_key)
74 silc_pkcs_public_key_free(idata->public_key);
79 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
81 SilcIDListPurge i = (SilcIDListPurge)context;
83 SILC_LOG_DEBUG(("Start"));
85 silc_idcache_purge(i->cache);
86 silc_schedule_task_add(i->schedule, 0,
89 SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
92 /******************************************************************************
94 Server entry functions
96 ******************************************************************************/
98 /* Add new server entry. This adds the new server entry to ID cache and
99 returns the allocated entry object or NULL on error. This is called
100 when new server connects to us. We also add ourselves to cache with
104 silc_idlist_add_server(SilcIDList id_list,
105 char *server_name, int server_type,
106 SilcServerID *id, SilcServerEntry router,
109 SilcServerEntry server;
111 SILC_LOG_DEBUG(("Adding new server entry"));
113 server = silc_calloc(1, sizeof(*server));
114 server->server_name = server_name;
115 server->server_type = server_type;
117 server->router = router;
118 server->connection = connection;
120 if (!silc_idcache_add(id_list->servers, server->server_name,
121 (void *)server->id, (void *)server, FALSE)) {
129 /* Finds server by Server ID */
132 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
133 bool registered, SilcIDCacheEntry *ret_entry)
135 SilcIDCacheEntry id_cache = NULL;
136 SilcServerEntry server;
141 SILC_LOG_DEBUG(("Server ID (%s)",
142 silc_id_render(id, SILC_ID_SERVER)));
144 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id,
148 server = (SilcServerEntry)id_cache->context;
151 *ret_entry = id_cache;
153 if (server && registered &&
154 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
157 SILC_LOG_DEBUG(("Found"));
162 /* Find server by name */
165 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
166 bool registered, SilcIDCacheEntry *ret_entry)
168 SilcIDCacheEntry id_cache = NULL;
169 SilcServerEntry server;
171 SILC_LOG_DEBUG(("Server by name `%s'", name));
173 if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
176 server = (SilcServerEntry)id_cache->context;
179 *ret_entry = id_cache;
181 if (server && registered &&
182 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
185 SILC_LOG_DEBUG(("Found"));
190 /* Find server by connection parameters, hostname and port */
193 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
194 int port, bool registered,
195 SilcIDCacheEntry *ret_entry)
197 SilcIDCacheList list = NULL;
198 SilcIDCacheEntry id_cache = NULL;
199 SilcServerEntry server = NULL;
200 SilcSocketConnection sock;
202 SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
204 if (!silc_idcache_get_all(id_list->servers, &list))
207 if (!silc_idcache_list_first(list, &id_cache)) {
208 silc_idcache_list_free(list);
213 server = (SilcServerEntry)id_cache->context;
214 sock = (SilcSocketConnection)server->connection;
216 if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
217 (sock->ip && !strcasecmp(sock->ip, hostname)))
218 && sock->port == port)
224 if (!silc_idcache_list_next(list, &id_cache))
228 silc_idcache_list_free(list);
231 *ret_entry = id_cache;
233 if (server && registered &&
234 !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
237 SILC_LOG_DEBUG(("Found"));
242 /* Replaces old Server ID with new one */
245 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
246 SilcServerID *new_id)
248 SilcIDCacheEntry id_cache = NULL;
249 SilcServerEntry server;
251 if (!old_id || !new_id)
254 SILC_LOG_DEBUG(("Replacing Server ID"));
256 if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id,
260 server = (SilcServerEntry)id_cache->context;
262 /* Remove the old entry and add a new one */
264 silc_idcache_del_by_id(id_list->servers, (void *)server->id);
266 silc_free(server->id);
269 silc_idcache_add(id_list->servers, server->server_name, server->id,
272 SILC_LOG_DEBUG(("Found"));
277 /* Removes and free's server entry from ID list */
279 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
281 SILC_LOG_DEBUG(("Start"));
284 /* Remove from cache */
286 if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
290 silc_free(entry->server_name);
291 silc_free(entry->id);
293 memset(entry, 'F', sizeof(*entry));
301 /******************************************************************************
303 Client entry functions
305 ******************************************************************************/
307 /* Add new client entry. This adds the client entry to ID cache system
308 and returns the allocated client entry or NULL on error. This is
309 called when new client connection is accepted to the server. If The
310 `router' is provided then the all server routines assume that the client
311 is not directly connected local client but it has router set and is
312 remote. If this is the case then `connection' must be NULL. If, on the
313 other hand, the `connection' is provided then the client is assumed
314 to be directly connected local client and `router' must be NULL. */
317 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username,
318 char *userinfo, SilcClientID *id,
319 SilcServerEntry router, void *connection)
321 SilcClientEntry client;
323 SILC_LOG_DEBUG(("Adding new client entry"));
325 client = silc_calloc(1, sizeof(*client));
326 client->nickname = nickname;
327 client->username = username;
328 client->userinfo = userinfo;
330 client->router = router;
331 client->connection = connection;
332 client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
333 NULL, NULL, NULL, NULL, TRUE);
335 if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id,
336 (void *)client, FALSE)) {
337 silc_hash_table_free(client->channels);
345 /* Free client entry. This free's everything and removes the entry
346 from ID cache. Call silc_idlist_del_data before calling this one. */
348 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
350 SILC_LOG_DEBUG(("Start"));
353 /* Remove from cache */
355 if (!silc_idcache_del_by_context(id_list->clients, entry))
359 silc_free(entry->nickname);
360 silc_free(entry->username);
361 silc_free(entry->userinfo);
362 silc_free(entry->id);
364 memset(entry, 'F', sizeof(*entry));
373 /* Returns all clients matching requested nickname. Number of clients is
374 returned to `clients_count'. Caller must free the returned table. */
376 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
378 SilcClientEntry **clients,
379 uint32 *clients_count)
381 SilcIDCacheList list = NULL;
382 SilcIDCacheEntry id_cache = NULL;
384 SILC_LOG_DEBUG(("Start"));
386 if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
389 *clients = silc_realloc(*clients,
390 (silc_idcache_list_count(list) + *clients_count) *
393 silc_idcache_list_first(list, &id_cache);
394 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
396 while (silc_idcache_list_next(list, &id_cache))
397 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
399 silc_idcache_list_free(list);
401 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
406 /* Returns all clients matching requested nickname hash. Number of clients
407 is returned to `clients_count'. Caller must free the returned table. */
409 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
411 SilcClientEntry **clients,
412 uint32 *clients_count)
414 SilcIDCacheList list = NULL;
415 SilcIDCacheEntry id_cache = NULL;
416 unsigned char hash[32];
417 SilcClientID client_id;
419 SILC_LOG_DEBUG(("Start"));
421 silc_hash_make(md5hash, nickname, strlen(nickname), hash);
423 /* As the Client ID is hashed in the ID cache by hashing only the hash
424 from the Client ID, we can do a lookup with only the hash not the
425 other parts of the ID and get all the clients with that hash, ie.
426 with that nickname, as the hash is from the nickname. */
427 memset(&client_id, 0, sizeof(client_id));
428 memcpy(&client_id.hash, hash, sizeof(client_id.hash));
429 if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
432 *clients = silc_realloc(*clients,
433 (silc_idcache_list_count(list) + *clients_count) *
436 silc_idcache_list_first(list, &id_cache);
437 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
439 while (silc_idcache_list_next(list, &id_cache))
440 (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
442 silc_idcache_list_free(list);
444 SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
449 /* Finds client by Client ID */
452 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
453 bool registered, SilcIDCacheEntry *ret_entry)
455 SilcIDCacheEntry id_cache = NULL;
456 SilcClientEntry client;
461 SILC_LOG_DEBUG(("Client ID (%s)",
462 silc_id_render(id, SILC_ID_CLIENT)));
464 /* Do extended search since the normal ID comparison function for
465 Client ID's compares only the hash from the Client ID and not the
466 entire ID. The silc_hash_client_id_compare compares the entire
467 Client ID as we want to find one specific Client ID. */
468 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id,
470 silc_hash_client_id_compare, NULL,
474 client = (SilcClientEntry)id_cache->context;
477 *ret_entry = id_cache;
479 if (client && registered &&
480 !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
483 SILC_LOG_DEBUG(("Found"));
488 /* Replaces old Client ID with new one */
491 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
492 SilcClientID *new_id)
494 SilcIDCacheEntry id_cache = NULL;
495 SilcClientEntry client;
497 if (!old_id || !new_id)
500 SILC_LOG_DEBUG(("Replacing Client ID"));
502 /* Do extended search since the normal ID comparison function for
503 Client ID's compares only the hash from the Client ID and not the
504 entire ID. The silc_hash_client_id_compare compares the entire
505 Client ID as we want to find one specific Client ID. */
506 if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id,
508 silc_hash_client_id_compare, NULL,
512 client = (SilcClientEntry)id_cache->context;
514 /* Remove the old entry and add a new one */
516 silc_idcache_del_by_context(id_list->clients, client);
518 silc_free(client->id);
521 silc_idcache_add(id_list->clients, NULL, client->id, client, FALSE);
523 SILC_LOG_DEBUG(("Replaced"));
528 /* Client cache entry destructor that is called when the cache is purged. */
530 void silc_idlist_client_destructor(SilcIDCache cache,
531 SilcIDCacheEntry entry)
533 SilcClientEntry client;
535 SILC_LOG_DEBUG(("Start"));
537 client = (SilcClientEntry)entry->context;
539 if (client->nickname)
540 silc_free(client->nickname);
541 if (client->username)
542 silc_free(client->username);
543 if (client->userinfo)
544 silc_free(client->userinfo);
546 silc_free(client->id);
548 memset(client, 'F', sizeof(*client));
553 /******************************************************************************
555 Channel entry functions
557 ******************************************************************************/
559 /* Add new channel entry. This add the new channel entry to the ID cache
560 system and returns the allocated entry or NULL on error. */
563 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
564 SilcChannelID *id, SilcServerEntry router,
565 SilcCipher channel_key, SilcHmac hmac)
567 SilcChannelEntry channel;
569 SILC_LOG_DEBUG(("Adding new channel entry"));
571 channel = silc_calloc(1, sizeof(*channel));
572 channel->channel_name = channel_name;
573 channel->mode = mode;
575 channel->router = router;
576 channel->channel_key = channel_key;
577 channel->hmac = hmac;
578 channel->created = time(0);
580 if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
585 channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
586 NULL, NULL, NULL, TRUE);
588 if (!silc_idcache_add(id_list->channels, channel->channel_name,
589 (void *)channel->id, (void *)channel, FALSE)) {
590 silc_hmac_free(channel->hmac);
591 silc_hash_table_free(channel->user_list);
599 /* Foreach callbcak to free all users from the channel when deleting a
602 static void silc_idlist_del_channel_foreach(void *key, void *context,
605 SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
607 /* Remove the context from the client's channel hash table as that
608 table and channel's user_list hash table share this same context. */
609 silc_hash_table_del(chl->client->channels, chl->channel);
613 /* Free channel entry. This free's everything. */
615 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
617 SILC_LOG_DEBUG(("Start"));
620 /* Remove from cache */
622 if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
626 silc_free(entry->channel_name);
627 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);
635 silc_free(entry->cipher);
636 silc_free(entry->hmac_name);
637 silc_free(entry->rekey);
639 /* Free all client entrys from the users list. The silc_hash_table_free
640 will free all the entries so they are not freed at the foreach
642 silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
644 silc_hash_table_free(entry->user_list);
646 memset(entry, 'F', sizeof(*entry));
654 /* Finds channel by channel name. Channel names are unique and they
655 are not case-sensitive. */
658 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
659 SilcIDCacheEntry *ret_entry)
661 SilcIDCacheEntry id_cache = NULL;
663 SILC_LOG_DEBUG(("Channel by name"));
665 if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
669 *ret_entry = id_cache;
671 SILC_LOG_DEBUG(("Found"));
673 return id_cache->context;
676 /* Finds channel by Channel ID. */
679 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
680 SilcIDCacheEntry *ret_entry)
682 SilcIDCacheEntry id_cache = NULL;
683 SilcChannelEntry channel;
688 SILC_LOG_DEBUG(("Channel ID (%s)",
689 silc_id_render(id, SILC_ID_CHANNEL)));
691 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
694 channel = (SilcChannelEntry)id_cache->context;
697 *ret_entry = id_cache;
699 SILC_LOG_DEBUG(("Found"));
704 /* Replaces old Channel ID with new one. This is done when router forces
705 normal server to change Channel ID. */
708 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
709 SilcChannelID *new_id)
711 SilcIDCacheEntry id_cache = NULL;
712 SilcChannelEntry channel;
714 if (!old_id || !new_id)
717 SILC_LOG_DEBUG(("Replacing Channel ID"));
719 if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id,
723 channel = (SilcChannelEntry)id_cache->context;
725 /* Remove the old entry and add a new one */
727 silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
729 silc_free(channel->id);
730 channel->id = new_id;
732 silc_idcache_add(id_list->channels, channel->channel_name, channel->id,
735 SILC_LOG_DEBUG(("Replaced"));
740 /* Returns channels from the ID list. If the `channel_id' is NULL then
741 all channels are returned. */
744 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
745 uint32 *channels_count)
747 SilcIDCacheList list = NULL;
748 SilcIDCacheEntry id_cache = NULL;
749 SilcChannelEntry *channels = NULL;
752 SILC_LOG_DEBUG(("Start"));
755 if (!silc_idcache_get_all(id_list->channels, &list))
758 channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
761 silc_idcache_list_first(list, &id_cache);
762 channels[i++] = (SilcChannelEntry)id_cache->context;
764 while (silc_idcache_list_next(list, &id_cache))
765 channels[i++] = (SilcChannelEntry)id_cache->context;
767 silc_idcache_list_free(list);
769 if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
773 channels = silc_calloc(1, sizeof(*channels));
774 channels[0] = (SilcChannelEntry)id_cache->context;