}
/* Initialize ID caches */
- server->local_list->clients = silc_idcache_alloc(0);
- server->local_list->servers = silc_idcache_alloc(0);
- server->local_list->channels = silc_idcache_alloc(0);
+ server->local_list->clients =
+ silc_idcache_alloc(0, silc_idlist_client_destructor);
+ server->local_list->servers = silc_idcache_alloc(0, NULL);
+ server->local_list->channels = silc_idcache_alloc(0, NULL);
/* These are allocated for normal server as well as these hold some
global information that the server has fetched from its router. For
router these are used as they are supposed to be used on router. */
- server->global_list->clients = silc_idcache_alloc(0);
- server->global_list->servers = silc_idcache_alloc(0);
- server->global_list->channels = silc_idcache_alloc(0);
+ server->global_list->clients =
+ silc_idcache_alloc(0, silc_idlist_client_destructor);
+ server->global_list->servers = silc_idcache_alloc(0, NULL);
+ server->global_list->channels = silc_idcache_alloc(0, NULL);
/* Allocate the entire socket list that is used in server. Eventually
all connections will have entry in this table (it is a table of
SILC_LOG_DEBUG(("Start"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_protocol_free(protocol);
silc_ske_free_key_material(ctx->keymat);
SILC_LOG_DEBUG(("Start"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_protocol_free(protocol);
if (ctx->packet)
if (ret == SILC_PACKET_NONE)
goto out;
+ /* Check that the the current client ID is same as in the client's packet. */
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ SilcClientEntry client = (SilcClientEntry)sock->user_data;
+ if (client && client->id) {
+ void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (SILC_ID_CLIENT_COMPARE(client->id, id)) {
+ silc_free(id);
+ goto out;
+ }
+ silc_free(id);
+ }
+ }
+
if (server->server_type == SILC_ROUTER) {
/* Route the packet if it is not destined to us. Other ID types but
server are handled separately after processing them. */
silc_server_close_connection(server, sock);
}
+typedef struct {
+ SilcServer server;
+ SilcClientEntry client;
+} *FreeClientInternal;
+
+SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
+{
+ FreeClientInternal i = (FreeClientInternal)context;
+
+ silc_idlist_del_data(i->client);
+ silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
+ silc_free(i);
+}
+
/* Frees client data and notifies about client's signoff. */
void silc_server_free_client_data(SilcServer server,
SilcSocketConnection sock,
- SilcClientEntry user_data, char *signoff)
+ SilcClientEntry client, char *signoff)
{
- /* Send REMOVE_ID packet to routers. */
+ FreeClientInternal i = silc_calloc(1, sizeof(*i));
+
+ /* Send SIGNOFF notify to routers. */
if (!server->standalone && server->router)
silc_server_send_notify_signoff(server, server->router->connection,
server->server_type == SILC_SERVER ?
- FALSE : TRUE, user_data->id,
+ FALSE : TRUE, client->id,
SILC_ID_CLIENT_LEN, signoff);
/* Remove client from all channels */
- silc_server_remove_from_channels(server, sock, user_data, signoff);
+ silc_server_remove_from_channels(server, sock, client, TRUE, signoff, TRUE);
- /* XXX must take some info to history before freeing */
+ /* We will not delete the client entry right away. We will take it
+ into history (for WHOWAS command) for 5 minutes */
+ i->server = server;
+ i->client = client;
+ silc_task_register(server->timeout_queue, 0,
+ silc_server_free_client_data_timeout,
+ (void *)i, 300, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ client->data.registered = FALSE;
/* Free the client entry and everything in it */
- silc_idlist_del_data(user_data);
- silc_idlist_del_client(server->local_list, user_data);
server->stat.my_clients--;
server->stat.clients--;
if (server->server_type == SILC_ROUTER)
/* If this was our primary router connection then we're lost to
the outside world. */
- if (server->server_type == SILC_SERVER && server->router == user_data) {
+ if (server->router == user_data) {
server->id_entry->router = NULL;
server->router = NULL;
server->standalone = TRUE;
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, NULL);
+ silc_server_remove_from_channels(server, NULL, client, TRUE,
+ NULL, TRUE);
silc_idlist_del_client(server->local_list, client);
if (!silc_idcache_list_next(list, &id_cache))
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, NULL);
+ silc_server_remove_from_channels(server, NULL, client, TRUE,
+ NULL, TRUE);
silc_idlist_del_client(server->global_list, client);
if (!silc_idcache_list_next(list, &id_cache))
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
SilcClientEntry client,
- char *signoff_message)
+ int notify,
+ char *signoff_message,
+ int keygen)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
if (server->server_type == SILC_SERVER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
- if (channel->global_users)
+ if (notify && channel->global_users)
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message ? 2 : 1,
/* Send notify to channel about client leaving SILC and thus
the entire channel. */
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_SIGNOFF,
- signoff_message ? 2 : 1,
- clidp->data, clidp->len,
- signoff_message, signoff_message ?
- strlen(signoff_message) : 0);
+ if (notify)
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ SILC_NOTIFY_TYPE_SIGNOFF,
+ signoff_message ? 2 : 1,
+ clidp->data, clidp->len,
+ signoff_message, signoff_message ?
+ strlen(signoff_message) : 0);
+
+ if (keygen) {
+ /* Re-generate channel key */
+ silc_server_create_channel_key(server, channel, 0);
+
+ /* Send the channel key to the channel. The key of course is not sent
+ to the client who was removed f rom the channel. */
+ silc_server_send_channel_key(server, client->connection, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
}
silc_buffer_free(clidp);
unsigned char channel_key[32], hash[32];
unsigned int len;
+ SILC_LOG_DEBUG(("Generating channel key"));
+
if (!channel->channel_key)
if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
return;
silc_free(f);
}
+
+/* Assembles user list and users mode list from the `channel'. */
+
+void silc_server_get_users_on_channel(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *user_list,
+ SilcBuffer *mode_list,
+ unsigned int *user_count)
+{
+ SilcChannelClientEntry chl;
+ SilcBuffer client_id_list;
+ SilcBuffer client_mode_list;
+ SilcBuffer idp;
+ unsigned int list_count = 0;
+
+ client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) *
+ silc_list_count(channel->user_list));
+ client_mode_list = silc_buffer_alloc(4 *
+ silc_list_count(channel->user_list));
+ silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
+ silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ /* Client ID */
+ idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+ silc_buffer_put(client_id_list, idp->data, idp->len);
+ silc_buffer_pull(client_id_list, idp->len);
+ silc_buffer_free(idp);
+
+ /* Client's mode on channel */
+ SILC_PUT32_MSB(chl->mode, client_mode_list->data);
+ silc_buffer_pull(client_mode_list, 4);
+
+ list_count++;
+ }
+ silc_buffer_push(client_id_list,
+ client_id_list->data - client_id_list->head);
+ silc_buffer_push(client_mode_list,
+ client_mode_list->data - client_mode_list->head);
+
+ *user_list = client_id_list;
+ *mode_list = client_mode_list;
+ *user_count = list_count;
+}
+
+/* Saves users and their modes to the `channel'. */
+
+void silc_server_save_users_on_channel(SilcServer server,
+ SilcSocketConnection sock,
+ SilcChannelEntry channel,
+ SilcClientID *noadd,
+ SilcBuffer user_list,
+ SilcBuffer mode_list,
+ unsigned int user_count)
+{
+ int i;
+
+ /* Cache the received Client ID's and modes. This cache expires
+ whenever server sends notify message to channel. It means two things;
+ some user has joined or leaved the channel. XXX TODO! */
+ for (i = 0; i < user_count; i++) {
+ unsigned short idp_len;
+ unsigned int mode;
+ SilcClientID *client_id;
+ SilcClientEntry client;
+
+ /* Client ID */
+ SILC_GET16_MSB(idp_len, user_list->data + 2);
+ idp_len += 4;
+ client_id = silc_id_payload_parse_id(user_list->data, idp_len);
+ silc_buffer_pull(user_list, idp_len);
+ if (!client_id)
+ continue;
+
+ /* Mode */
+ SILC_GET32_MSB(mode, mode_list->data);
+ silc_buffer_pull(mode_list, 4);
+
+ if (noadd && !SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+ silc_free(client_id);
+ continue;
+ }
+
+ /* Check if we have this client cached already. */
+ client = silc_idlist_find_client_by_id(server->local_list, client_id,
+ NULL);
+ if (!client)
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
+ if (!client) {
+ /* If router did not find such Client ID in its lists then this must
+ be bogus client or some router in the net is buggy. */
+ if (server->server_type == SILC_ROUTER) {
+ silc_free(client_id);
+ continue;
+ }
+
+ /* We don't have that client anywhere, add it. The client is added
+ to global list since server didn't have it in the lists so it must be
+ global. */
+ client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+ silc_id_dup(client_id, SILC_ID_CLIENT),
+ sock->user_data, NULL);
+ if (!client) {
+ silc_free(client_id);
+ continue;
+ }
+ }
+
+ silc_free(client_id);
+
+ if (!silc_server_client_on_channel(client, channel)) {
+ /* Client was not on the channel, add it. */
+ SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+ chl->client = client;
+ chl->mode = mode;
+ chl->channel = channel;
+ silc_list_add(channel->user_list, chl);
+ silc_list_add(client->channels, chl);
+ }
+ }
+}