Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2004, 2006 Pekka Riikonen
+ Copyright (C) 1997 - 2007 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
SILC_LOG_DEBUG(("Sending channel message"));
- if (!client || !conn || !channel)
+ if (silc_unlikely(!client || !conn || !channel))
+ return FALSE;
+ if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
return FALSE;
- if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+ if (silc_unlikely(conn->internal->disconnected))
return FALSE;
chu = silc_client_on_channel(channel, conn->local_entry);
- if (!chu) {
+ if (silc_unlikely(!chu)) {
client->internal->ops->say(conn->client, conn,
SILC_CLIENT_MESSAGE_AUDIT,
"Cannot talk to channel: not joined");
}
/* Check if it is allowed to send messages to this channel by us. */
- if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chu->mode)
+ if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS &&
+ !chu->mode))
return FALSE;
- if (channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
- chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
- !(chu->mode & SILC_CHANNEL_UMODE_CHANFO))
+ if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
+ chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
+ !(chu->mode & SILC_CHANNEL_UMODE_CHANFO)))
return FALSE;
- if (chu->mode & SILC_CHANNEL_UMODE_QUIET)
+ if (silc_unlikely(chu->mode & SILC_CHANNEL_UMODE_QUIET))
return FALSE;
/* Take the key to be used */
channel->internal.curr_key = key;
} else {
/* Use normal channel key generated by the server */
- cipher = channel->internal.channel_key;
+ cipher = channel->internal.send_key;
hmac = channel->internal.hmac;
}
} else {
/* Use normal channel key generated by the server */
- cipher = channel->internal.channel_key;
+ cipher = channel->internal.send_key;
hmac = channel->internal.hmac;
}
- if (!cipher || !hmac) {
+ if (silc_unlikely(!cipher || !hmac)) {
SILC_LOG_ERROR(("No cipher and HMAC for channel"));
return FALSE;
}
buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
cipher, hmac, client->rng, NULL,
conn->private_key, hash, NULL);
- if (!buffer) {
+ if (silc_unlikely(!buffer)) {
SILC_LOG_ERROR(("Error encoding channel message"));
return FALSE;
}
SILC_LOG_DEBUG(("Received channel message"));
- if (packet->dst_id_type != SILC_ID_CHANNEL) {
+ SILC_LOG_HEXDUMP(("Channel message"), silc_buffer_data(buffer),
+ silc_buffer_len(buffer));
+
+ if (silc_unlikely(packet->dst_id_type != SILC_ID_CHANNEL)) {
/** Invalid packet */
silc_fsm_next(fsm, silc_client_channel_message_error);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
- if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
- &remote_id, sizeof(remote_id))) {
+ if (silc_unlikely(!silc_id_str2id(packet->src_id,
+ packet->src_id_len, SILC_ID_CLIENT,
+ &remote_id, sizeof(remote_id)))) {
/** Invalid source ID */
silc_fsm_next(fsm, silc_client_channel_message_error);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/* Get sender client entry */
/* NOT REACHED */
}
- if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
- &channel_id, sizeof(channel_id))) {
+ if (silc_unlikely(!silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL, &channel_id,
+ sizeof(channel_id)))) {
/** Invalid destination ID */
silc_fsm_next(fsm, silc_client_channel_message_error);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/* Find the channel */
channel = silc_client_get_channel_by_id(client, conn, &channel_id);
- if (!channel) {
+ if (silc_unlikely(!channel)) {
/** Unknown channel */
silc_fsm_next(fsm, silc_client_channel_message_error);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
+ }
+
+ /* Check that user is on channel */
+ if (silc_unlikely(!silc_client_on_channel(channel, client_entry))) {
+ /** User not on channel */
+ SILC_LOG_WARNING(("Message from user not on channel, client or "
+ "server bug"));
+ silc_fsm_next(fsm, silc_client_channel_message_error);
+ SILC_FSM_CONTINUE;
}
/* If there is no channel private key then just decrypt the message
/* Parse the channel message payload. This also decrypts the payload */
payload = silc_message_payload_parse(silc_buffer_data(buffer),
silc_buffer_len(buffer), FALSE,
- FALSE, channel->internal.channel_key,
+ FALSE, channel->internal.receive_key,
channel->internal.hmac, NULL,
FALSE, NULL);
/* If decryption failed and we have just performed channel key rekey
we will use the old key in decryption. If that fails too then we
cannot do more and will drop the packet. */
- if (!payload) {
+ if (silc_unlikely(!payload)) {
SilcCipher cipher;
SilcHmac hmac;
payload = silc_message_payload_parse(silc_buffer_data(buffer),
silc_buffer_len(buffer),
FALSE, FALSE,
- channel->internal.channel_key,
+ channel->internal.receive_key,
channel->internal.hmac, NULL,
FALSE, NULL);
silc_client_unref_channel(client, conn, channel);
if (payload)
silc_message_payload_free(payload);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Channel message error. */
{
SilcPacket packet = state_context;
silc_packet_free(packet);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/******************************* Channel Key ********************************/
SilcChannelID id;
SilcChannelKeyPayload payload;
+ SILC_LOG_DEBUG(("New channel key"));
+
payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
silc_buffer_len(key_payload));
if (!payload)
if (!channel) {
channel = silc_client_get_channel_by_id(client, conn, &id);
if (!channel) {
+ SILC_LOG_DEBUG(("Key for unknown channel"));
silc_channel_key_payload_free(payload);
return FALSE;
}
channel->internal.old_hmacs = silc_dlist_init();
if (channel->internal.old_channel_keys && channel->internal.old_hmacs) {
silc_dlist_add(channel->internal.old_channel_keys,
- channel->internal.channel_key);
+ channel->internal.receive_key);
silc_dlist_add(channel->internal.old_hmacs, channel->internal.hmac);
silc_schedule_task_add_timeout(client->schedule,
silc_client_save_channel_key_rekey,
/* Get channel cipher */
cipher = silc_channel_key_get_cipher(payload, NULL);
- if (!silc_cipher_alloc(cipher, &channel->internal.channel_key)) {
+ if (!silc_cipher_alloc(cipher, &channel->internal.send_key)) {
+ client->internal->ops->say(
+ conn->client, conn,
+ SILC_CLIENT_MESSAGE_AUDIT,
+ "Cannot talk to channel: unsupported cipher %s",
+ cipher);
+ silc_client_unref_channel(client, conn, channel);
+ silc_channel_key_payload_free(payload);
+ return FALSE;
+ }
+ if (!silc_cipher_alloc(cipher, &channel->internal.receive_key)) {
client->internal->ops->say(
conn->client, conn,
SILC_CLIENT_MESSAGE_AUDIT,
return FALSE;
}
- /* Set the cipher key */
+ /* Set the cipher key. Both sending and receiving keys are same */
key = silc_channel_key_get_key(payload, &tmp_len);
- silc_cipher_set_key(channel->internal.channel_key, key, tmp_len * 8);
+ silc_cipher_set_key(channel->internal.send_key, key, tmp_len * 8, TRUE);
+ silc_cipher_set_key(channel->internal.receive_key, key, tmp_len * 8, FALSE);
/* Get channel HMAC */
hmac = (channel->internal.hmac ?
silc_client_save_channel_key(client, conn, &packet->buffer, NULL);
silc_packet_free(packet);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/**************************** Channel Private Key ***************************/
/* Produce the key material */
keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
- client->sha1hash);
+ conn->internal->sha1hash);
if (!keymat)
return FALSE;
}
entry->name = name ? strdup(name) : NULL;
- /* Allocate the cipher and set the key*/
+ /* Allocate the cipher and set the key */
if (!silc_cipher_alloc(cipher, &entry->cipher)) {
silc_free(entry);
silc_free(entry->name);
return FALSE;
}
silc_cipher_set_key(entry->cipher, keymat->send_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, TRUE);
/* Generate HMAC key from the channel key data and set it */
if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) {
return NULL;
}
-/* Adds client to channel */
+/* Adds client to channel. Returns TRUE if user was added or is already
+ added to the channel, FALSE on error. */
-SilcBool silc_client_add_to_channel(SilcChannelEntry channel,
+SilcBool silc_client_add_to_channel(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
SilcClientEntry client_entry,
SilcUInt32 cumode)
{
SilcChannelUser chu;
if (silc_client_on_channel(channel, client_entry))
- return FALSE;
+ return TRUE;
+
+ SILC_LOG_DEBUG(("Add client %s to channel", client_entry->nickname));
chu = silc_calloc(1, sizeof(*chu));
if (!chu)
chu->client = client_entry;
chu->channel = channel;
chu->mode = cumode;
+
+ silc_client_ref_client(client, conn, client_entry);
+ silc_client_ref_channel(client, conn, channel);
+
silc_hash_table_add(channel->user_list, client_entry, chu);
silc_hash_table_add(client_entry->channels, channel, chu);
/* Removes client from a channel */
-SilcBool silc_client_remove_from_channel(SilcChannelEntry channel,
+SilcBool silc_client_remove_from_channel(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel,
SilcClientEntry client_entry)
{
SilcChannelUser chu;
if (!chu)
return FALSE;
+ SILC_LOG_DEBUG(("Remove client %s from channel", client_entry->nickname));
+
silc_hash_table_del(chu->client->channels, chu->channel);
silc_hash_table_del(chu->channel->user_list, chu->client);
silc_free(chu);
+ /* If channel became empty, delete it */
+ if (!silc_hash_table_count(channel->user_list))
+ silc_client_del_channel(client, conn, channel);
+
+ silc_client_unref_client(client, conn, client_entry);
+ silc_client_unref_channel(client, conn, channel);
+
return TRUE;
}
SilcHashTableList htl;
SilcChannelUser chu;
+ if (!silc_hash_table_count(client_entry->channels))
+ return;
+
+ SILC_LOG_DEBUG(("Remove client from all joined channels"));
+
silc_hash_table_list(client_entry->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
silc_hash_table_del(chu->client->channels, chu->channel);
silc_hash_table_del(chu->channel->user_list, chu->client);
+
+ /* If channel became empty, delete it */
+ if (!silc_hash_table_count(chu->channel->user_list))
+ silc_client_del_channel(client, conn, chu->channel);
+
+ silc_client_unref_client(client, conn, chu->client);
+ silc_client_unref_channel(client, conn, chu->channel);
silc_free(chu);
}
silc_hash_table_list_reset(&htl);
}
+
+/* Empties channel from users. */
+
+void silc_client_empty_channel(SilcClient client,
+ SilcClientConnection conn,
+ SilcChannelEntry channel)
+{
+ SilcHashTableList htl;
+ SilcChannelUser chu;
+
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ silc_hash_table_del(chu->client->channels, chu->channel);
+ silc_hash_table_del(chu->channel->user_list, chu->client);
+ silc_client_unref_client(client, conn, chu->client);
+ silc_client_unref_channel(client, conn, chu->channel);
+ silc_free(chu);
+ }
+ silc_hash_table_list_reset(&htl);
+}