has been either created or resolved from ID lists. This joins the sent
client to the channel. */
-static void
-silc_server_command_join_channel(SilcServer server,
- SilcServerCommandContext cmd,
- SilcChannelEntry channel,
- unsigned int umode)
+static void silc_server_command_join_channel(SilcServer server,
+ SilcServerCommandContext cmd,
+ SilcChannelEntry channel,
+ int created,
+ unsigned int umode)
{
SilcSocketConnection sock = cmd->sock;
unsigned char *tmp;
unsigned char *passphrase = NULL, mode[4];
SilcClientEntry client;
SilcChannelClientEntry chl;
- SilcBuffer packet, idp;
+ SilcBuffer reply, chidp, clidp;
if (!channel)
return;
/* If the JOIN request was forwarded to us we will make a bit slower
query to get the client pointer. Otherwise, we get the client pointer
real easy. */
- if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
- client = (SilcClientEntry)sock->user_data;
- } else {
+ if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
client = silc_idlist_find_client_by_id(server->local_list, id);
if (!client) {
goto out;
}
silc_free(id);
+ } else {
+ client = (SilcClientEntry)sock->user_data;
}
/* Check whether the client already is on the channel */
goto out;
}
+ /* Generate new channel key as protocol dictates */
+ if (!created)
+ silc_server_create_channel_key(server, channel, 0);
+
/* Join the client to the channel by adding it to channel's user list.
Add also the channel to client entry's channels list for fast cross-
referencing. */
silc_list_add(channel->user_list, chl);
silc_list_add(client->channels, chl);
- /* Notify router about new user on channel. If we are normal server
- we send it to our router, if we are router we send it to our
- primary route. */
- if (!server->standalone) {
-
+ /* Encode Client ID Payload of the original client who wants to join */
+ clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+ /* Encode command reply packet */
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ SILC_PUT32_MSB(channel->mode, mode);
+ if (!channel->topic) {
+ reply =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+ SILC_STATUS_OK, 0, 3,
+ 2, channel->channel_name,
+ strlen(channel->channel_name),
+ 3, chidp->data, chidp->len,
+ 4, mode, 4);
+ } else {
+ reply =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+ SILC_STATUS_OK, 0, 4,
+ 2, channel->channel_name,
+ strlen(channel->channel_name),
+ 3, chidp->data, chidp->len,
+ 4, mode, 4,
+ 5, channel->topic,
+ strlen(channel->topic));
}
+
+ if (server->server_type == SILC_ROUTER &&
+ cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
+ /* We are router and server has forwarded this command to us. Send
+ all replys to the server. */
+ void *tmpid;
+
+ /* Send command reply destined to the original client */
+ tmpid = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
+ silc_server_packet_send_dest(cmd->server, sock,
+ SILC_PACKET_COMMAND_REPLY, 0,
+ tmpid, cmd->packet->src_id_type,
+ reply->data, reply->len, FALSE);
- /* Send command reply to the client. Client receives the Channe ID,
- channel mode and possibly other information in this reply packet. */
- if (!cmd->pending) {
- idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
- SILC_PUT32_MSB(channel->mode, mode);
+ /* Distribute new channel key to local cell and local clients. */
+ silc_server_send_channel_key(server, sock, channel, TRUE);
+
+ /* Distribute JOIN notify into the cell for everbody on the channel */
+ silc_server_send_notify_to_channel(server, channel, FALSE,
+ SILC_NOTIFY_TYPE_JOIN, 1,
+ clidp->data, clidp->len);
- if (!channel->topic)
- packet =
- silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
- SILC_STATUS_OK, 0, 3,
- 2, channel->channel_name,
- strlen(channel->channel_name),
- 3, idp->data, idp->len,
- 4, mode, 4);
- else
- packet =
- silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
- SILC_STATUS_OK, 0, 4,
- 2, channel->channel_name,
- strlen(channel->channel_name),
- 3, idp->data, idp->len,
- 4, mode, 4,
- 5, channel->topic,
- strlen(channel->topic));
-
- if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
- void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
- silc_server_packet_send_dest(cmd->server, cmd->sock,
- SILC_PACKET_COMMAND_REPLY, 0,
- id, cmd->packet->src_id_type,
- packet->data, packet->len, FALSE);
- silc_free(id);
- } else
- silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
- packet->data, packet->len, FALSE);
- silc_buffer_free(packet);
+ /* Broadcast NEW_CHANNEL_USER packet to primary route */
+ silc_server_send_new_channel_user(server, server->router->connection,
+ TRUE, channel->id, SILC_ID_CHANNEL_LEN,
+ client->id, SILC_ID_CLIENT_LEN);
- /* Send channel key to the client. Client cannot start transmitting
- to the channel until we have sent the key. */
- tmp_len = strlen(channel->channel_key->cipher->name);
- packet =
- silc_channel_key_payload_encode(idp->len, idp->data,
- strlen(channel->channel_key->
- cipher->name),
- channel->channel_key->cipher->name,
- channel->key_len / 8, channel->key);
-
- silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
- packet->data, packet->len, FALSE);
+ silc_free(tmpid);
+ } else {
+ /* Client sent the command. Send all replies directly to the client. */
- silc_buffer_free(packet);
- silc_buffer_free(idp);
- }
+ /* Send command reply */
+ silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
+ reply->data, reply->len, FALSE);
- /* Finally, send notify message to all clients on the channel about
- new user on the channel. */
- if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
- if (!cmd->pending) {
- /* Send JOIN notify to clients */
- SilcBuffer clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- silc_server_send_notify_to_channel(server, channel, FALSE,
- SILC_NOTIFY_TYPE_JOIN, 1,
- clidp->data, clidp->len);
- silc_buffer_free(clidp);
-
- /* Send NEW_CHANNEL_USER packet to primary route */
- if (!server->standalone)
- silc_server_send_new_channel_user(server, server->router->connection,
- server->server_type == SILC_SERVER ?
- FALSE : TRUE,
- channel->id, SILC_ID_CHANNEL_LEN,
- client->id, SILC_ID_CLIENT_LEN);
- } else {
- /* This is pending command request. Send the notify after we have
- received the key for the channel from the router. */
- JoinInternalContext *ctx = silc_calloc(1, sizeof(*ctx));
- ctx->channel_name = channel->channel_name;
- ctx->nickname = client->nickname;
- ctx->username = client->username;
- ctx->hostname = sock->hostname ? sock->hostname : sock->ip;
- ctx->channel = channel;
- ctx->server = server;
- ctx->client = client;
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_command_join_notify, ctx,
- 0, 10000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
- goto out;
- }
+ /* Send the channel key. Channel key is sent before any other packet
+ to the channel. */
+ silc_server_send_channel_key(server, sock, channel, server->standalone ?
+ FALSE : TRUE);
+
+ /* Send JOIN notify to locally connected clients on the channel */
+ silc_server_send_notify_to_channel(server, channel, FALSE,
+ SILC_NOTIFY_TYPE_JOIN, 1,
+ clidp->data, clidp->len);
+
+ /* Send NEW_CHANNEL_USER packet to our primary router */
+ if (!server->standalone)
+ silc_server_send_new_channel_user(server, server->router->connection,
+ FALSE,
+ channel->id, SILC_ID_CHANNEL_LEN,
+ client->id, SILC_ID_CLIENT_LEN);
/* Send NAMES command reply to the joined channel so the user sees who
is currently on the channel. */
silc_server_command_send_names(server, sock, channel);
}
+ silc_buffer_free(reply);
+ silc_buffer_free(clidp);
+ silc_buffer_free(chidp);
+
out:
if (passphrase)
silc_free(passphrase);
int argc, tmp_len;
char *tmp, *channel_name = NULL, *cipher = NULL;
SilcChannelEntry channel;
- unsigned int umode = SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO;
+ unsigned int umode = 0;
+ int created = FALSE;
SILC_LOG_DEBUG(("Start"));
if (server->standalone) {
channel = silc_server_create_new_channel(server, server->id, cipher,
channel_name);
+ umode |= SILC_CHANNEL_UMODE_CHANOP;
+ umode |= SILC_CHANNEL_UMODE_CHANFO;
+ created = TRUE;
} else {
- /* The channel does not exist on our server. We send JOIN command to
- our router which will handle the joining procedure (either creates
- the channel if it doesn't exist or joins the client to it) - if we
- are normal server. */
+ /* The channel does not exist on our server. If we are normal server
+ we will send JOIN command to our router which will handle the joining
+ procedure (either creates the channel if it doesn't exist or joins
+ the client to it). */
if (server->server_type == SILC_SERVER) {
- SilcBuffer buffer = cmd->packet->buffer;
-
/* Forward the original JOIN command to the router */
- silc_buffer_push(buffer, buffer->data - buffer->head);
+ silc_buffer_push(cmd->packet->buffer,
+ cmd->packet->buffer->data -
+ cmd->packet->buffer->head);
silc_server_packet_forward(server, (SilcSocketConnection)
server->router->connection,
- buffer->data, buffer->len, TRUE);
-
- /* Add the command to be pending. It will be re-executed after
- router has replied back to us. */
- cmd->pending = TRUE;
- silc_server_command_pending(server, SILC_COMMAND_JOIN, 0,
- silc_server_command_join, context);
- return;
+ cmd->packet->buffer->data,
+ cmd->packet->buffer->len, TRUE);
+ goto out;
}
/* We are router and the channel does not seem exist so we will check
channel_name);
umode |= SILC_CHANNEL_UMODE_CHANOP;
umode |= SILC_CHANNEL_UMODE_CHANFO;
+ created = TRUE;
}
}
}
/* Join to the channel */
- silc_server_command_join_channel(server, cmd, channel, umode);
+ silc_server_command_join_channel(server, cmd, channel, created, umode);
out:
silc_server_command_free(cmd);
/* The mode is removed and we need to generate and distribute
new channel key. Clients are not using private channel keys
anymore after this. */
- unsigned int key_len;
- unsigned char channel_key[32];
/* XXX Duplicated code, make own function for this!! LEAVE uses this
as well */
/* Re-generate channel key */
- key_len = channel->key_len / 8;
- for (i = 0; i < key_len; i++)
- channel_key[i] = silc_rng_get_byte(server->rng);
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- channel_key, key_len);
- memset(channel->key, 0, key_len);
- silc_free(channel->key);
- channel->key = silc_calloc(key_len, sizeof(*channel->key));
- memcpy(channel->key, channel_key, key_len);
- memset(channel_key, 0, sizeof(channel_key));
+ silc_server_create_channel_key(server, channel, 0);
/* Encode channel key payload to be distributed on the channel */
packet =
strlen(channel->channel_key->
cipher->name),
channel->channel_key->cipher->name,
- key_len, channel->key);
+ channel->key_len / 8, channel->key);
/* If we are normal server then we will send it to our router. If we
are router we will send it to all local servers that has clients on
if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
/* Cipher to use protect the traffic */
unsigned int key_len = 128;
- unsigned char channel_key[32];
char *cp;
/* Get cipher */
silc_cipher_free(channel->channel_key);
silc_cipher_alloc(tmp, &channel->channel_key);
- /* Re-generate channel key */
key_len /= 8;
- if (key_len > sizeof(channel_key))
- key_len = sizeof(channel_key);
-
- for (i = 0; i < key_len; i++)
- channel_key[i] = silc_rng_get_byte(server->rng);
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- channel_key, key_len);
- memset(channel->key, 0, key_len);
- silc_free(channel->key);
- channel->key = silc_calloc(key_len, sizeof(*channel->key));
- memcpy(channel->key, channel_key, key_len);
- memset(channel_key, 0, sizeof(channel_key));
+ if (key_len > 32)
+ key_len = 32;
+
+ /* Re-generate channel key */
+ silc_server_create_channel_key(server, channel, key_len);
/* Encode channel key payload to be distributed on the channel */
packet =
strlen(channel->channel_key->
cipher->name),
channel->channel_key->cipher->name,
- key_len, channel->key);
+ channel->key_len / 8, channel->key);
/* If we are normal server then we will send it to our router. If we
are router we will send it to all local servers that has clients on
if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
/* Cipher mode is unset. Remove the cipher and revert back to
default cipher */
- unsigned int key_len;
- unsigned char channel_key[32];
if (channel->mode_data.cipher) {
silc_free(channel->mode_data.cipher);
silc_cipher_alloc(channel->cipher, &channel->channel_key);
/* Re-generate channel key */
- key_len = channel->key_len / 8;
- for (i = 0; i < key_len; i++)
- channel_key[i] = silc_rng_get_byte(server->rng);
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- channel_key, key_len);
- memset(channel->key, 0, key_len);
- silc_free(channel->key);
- channel->key = silc_calloc(key_len, sizeof(*channel->key));
- memcpy(channel->key, channel_key, key_len);
- memset(channel_key, 0, sizeof(channel_key));
+ silc_server_create_channel_key(server, channel, 0);
/* Encode channel key payload to be distributed on the channel */
packet =
strlen(channel->channel_key->
cipher->name),
channel->channel_key->cipher->name,
- key_len, channel->key);
+ channel->key_len / 8, channel->key);
/* If we are normal server then we will send it to our router. If we
are router we will send it to all local servers that has clients on
SilcChannelID *id;
SilcChannelEntry channel;
SilcBuffer packet;
- unsigned int i, argc, key_len, len;
- unsigned char *tmp, channel_key[32];
+ unsigned int i, argc, len;
+ unsigned char *tmp;
SILC_LOG_DEBUG(("Start"));
goto out;
/* Re-generate channel key */
- key_len = channel->key_len / 8;
- for (i = 0; i < key_len; i++)
- channel_key[i] = silc_rng_get_byte(server->rng);
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- channel_key, key_len);
- memset(channel->key, 0, key_len);
- silc_free(channel->key);
- channel->key = silc_calloc(key_len, sizeof(*channel->key));
- memcpy(channel->key, channel_key, key_len);
- memset(channel_key, 0, sizeof(channel_key));
+ silc_server_create_channel_key(server, channel, 0);
/* Encode channel key payload to be distributed on the channel */
packet =
silc_channel_key_payload_encode(len, tmp,
strlen(channel->channel_key->cipher->name),
channel->channel_key->cipher->name,
- key_len, channel->key);
+ channel->key_len / 8, channel->key);
/* If we are normal server then we will send it to our router. If we
are router we will send it to all local servers that has clients on
/* Received channel key packet. We distribute the key to all of our locally
connected clients on the channel. */
-/* XXX Router must accept this packet and distribute the key to all its
- server that has clients on the channel */
void silc_server_channel_key(SilcServer server,
SilcSocketConnection sock,
unsigned char *tmp;
unsigned int tmp_len;
char *cipher;
+ int exist = FALSE;
- if (packet->src_id_type != SILC_ID_SERVER &&
- sock->type != SILC_SOCKET_TYPE_ROUTER)
+ if (packet->src_id_type != SILC_ID_SERVER)
goto out;
/* Decode channel key payload */
goto out;
}
- /* Save the key for us as well */
tmp = silc_channel_key_get_key(payload, &tmp_len);
if (!tmp)
goto out;
+
cipher = silc_channel_key_get_cipher(payload, NULL);;
if (!cipher)
goto out;
- if (!silc_cipher_alloc(cipher, &channel->channel_key))
- goto out;
- /* Distribute the key to all clients on the channel */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- silc_server_packet_send(server, chl->client->connection,
- SILC_PACKET_CHANNEL_KEY, 0,
- buffer->data, buffer->len, TRUE);
+ /* Remove old key if exists */
+ if (channel->key) {
+ memset(channel->key, 0, channel->key_len);
+ silc_free(channel_key);
+ silc_cipher_free(channel->channel_key);
+ exist = TRUE;
}
+ /* Create new cipher */
+ if (!silc_cipher_alloc(cipher, &channel->channel_key))
+ goto out;
+
+ /* Save the key */
channel->key_len = tmp_len * 8;
channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
memcpy(channel->key, tmp, tmp_len);
channel->channel_key->cipher->set_key(channel->channel_key->context,
tmp, tmp_len);
+
+ /* Distribute the key to everybody who is on the channel. If we are router
+ we will also send it to locally connected servers. If we are normal
+ server and old key did not exist then we don't use this function
+ as the client is not in our channel user list just yet. We send the
+ key after receiveing JOIN notify from router. */
+ if (server->server_type == SILC_SERVER && exist)
+ silc_server_send_channel_key(server, channel, FALSE);
+ if (server->server_type == SILC_ROUTER)
+ silc_server_send_channel_key(server, channel, FALSE);
+
out:
if (id)
silc_free(id);
}
/* Processes incoming New ID packet. New ID Payload is used to distribute
- information about newly registered clients, servers and created
- channels. */
+ information about newly registered clients and servers. */
void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
SilcPacketContext *packet)
SilcNotifyPayload payload;
SilcNotifyType type;
SilcArgumentPayload args;
+ SilcClientID *client_id;
+ SilcChannelID *channel_id;
+ SilcClientEntry client;
+ SilcChannelEntry channel;
+ unsigned char *tmp;
+ unsigned int tmp_len;
int i;
+ SILC_LOG_DEBUG(("Start"));
+
if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
packet->src_id_type != SILC_ID_SERVER)
return;
- /* For now we expect that the we are normal server and that the
+ /* XXX: For now we expect that the we are normal server and that the
sender is router. Server could send (protocol allows it) notify to
- router but we don't support it yet. XXX! */
+ router but we don't support it yet. */
if (server->server_type != SILC_SERVER &&
sock->type != SILC_SOCKET_TYPE_ROUTER)
return;
switch(type) {
case SILC_NOTIFY_TYPE_JOIN:
+ {
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
+ goto out;
+
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->local_list, client_id);
+ if (!client) {
+ silc_free(client_id);
+ goto out;
+ }
+
+ // channel_id = ;
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+ if (!channel) {
+ silc_free(client_id);
+ goto out;
+ }
+
+ silc_free(client_id);
+ }
break;
case SILC_NOTIFY_TYPE_LEAVE: