should now summat work in router environment.
+Sat Jan 27 22:34:56 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * silc_server_remove_channel_user checks now also global list
+ for channel and client.
+
+ * silc_server_new_channel_user checks now both local and global
+ list for channel and client. Fixed a bug in client id decoding.
+ Used to decode wrong buffer.
+
+ * silc_server_channel_message checks now both local and global
+ list for channel entry.
+
+ * Tested channel joining (hence JOIN) in router environment
+ successfully. Tested with two routers, two servers and two
+ clients.
+
+ * Tested channel message sending in router environment successfully.
+
+Thu Jan 11 03:22:57 EET 2001 Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ * Added silc_server_save_channel_key into server.[ch] to save the
+ received channel key in Channel Key payload processing. It is
+ also used in JOIN command reply handling.
+
+ Equivalent function silc_client_save_channel_key added into
+ client.[ch] into client library.
+
+ * Changed JOIN command reply to send information whether the channel
+ was created or not (is existing already) and the channel key
+ payload. Changed protocol specs accordingly.
+
+ * Fixed bugs in WHOIS and IDENTIFY command reply sending when
+ the request was sent by ID and not by nickname. Crashed on
+ NULL dereference.
+
Sat Dec 23 21:55:07 EET 2000 Pekka Riikonen <priikone@poseidon.pspt.fi>
* Fixed a bug in Client library. IDENTIFY and WHOIS reply functions
if (!clients) {
/* Such a client really does not exist in the SILC network. */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, nick, strlen(nick));
+ if (!client_id) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+ SILC_STATUS_ERR_NO_SUCH_NICK,
+ 3, nick, strlen(nick));
+ } else {
+ SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 2, idp->data, idp->len);
+ silc_buffer_free(idp);
+ }
goto out;
}
if (!clients) {
/* Such a client really does not exist in the SILC network. */
- silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
- SILC_STATUS_ERR_NO_SUCH_NICK,
- 3, nick, strlen(nick));
+ if (!client_id) {
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_NICK,
+ 3, nick, strlen(nick));
+ } else {
+ SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 2, idp->data, idp->len);
+ silc_buffer_free(idp);
+ }
goto out;
}
static void silc_server_command_join_channel(SilcServer server,
SilcServerCommandContext cmd,
SilcChannelEntry channel,
+ SilcClientID *client_id,
int created,
unsigned int umode)
{
SilcSocketConnection sock = cmd->sock;
unsigned char *tmp;
unsigned int tmp_len;
- unsigned char *passphrase = NULL, mode[4];
+ unsigned char *passphrase = NULL, mode[4], tmp2[4];
SilcClientEntry client;
SilcChannelClientEntry chl;
- SilcBuffer reply, chidp, clidp;
+ SilcBuffer reply, chidp, clidp, keyp;
+ unsigned short ident = silc_command_get_ident(cmd->payload);
SILC_LOG_DEBUG(("Start"));
return;
/* Get passphrase */
- tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
if (tmp) {
passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
memcpy(passphrase, tmp, tmp_len);
* Client is allowed to join to the channel. Make it happen.
*/
- /* 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) {
- 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, NULL);
+ /* Get the client entry */
+ if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ client = (SilcClientEntry)sock->user_data;
+ } else {
+ client = silc_idlist_find_client_by_id(server->local_list, client_id,
+ NULL);
if (!client) {
- /* XXX */
- SILC_LOG_ERROR(("Forwarded join command did not find the client who "
- "wanted to join the channel"));
- goto out;
+ /* XXX actually this is useless since router finds always cell's
+ local clients from its local lists. */
+ client = silc_idlist_find_client_by_id(server->global_list, client_id,
+ NULL);
+ if (!client)
+ goto out;
}
- silc_free(id);
- } else {
- client = (SilcClientEntry)sock->user_data;
}
/* Check whether the client already is on the channel */
}
/* Generate new channel key as protocol dictates */
- if (!created)
+ if (!created || !channel->channel_key)
silc_server_create_channel_key(server, channel, 0);
+ /* Send the channel key. This is broadcasted to the channel but is not
+ sent to the client who is joining to the channel. */
+ silc_server_send_channel_key(server, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : server->standalone);
+
/* 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. */
/* Encode command reply packet */
chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
SILC_PUT32_MSB(channel->mode, mode);
+ SILC_PUT32_MSB(created, tmp2);
+ tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp,
+ SILC_ID_CHANNEL_LEN,
+ channel->channel_key->cipher->name,
+ channel->key_len / 8, channel->key);
+ silc_free(tmp);
if (!channel->topic) {
reply =
silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
- SILC_STATUS_OK, 0, 3,
+ SILC_STATUS_OK, ident, 5,
2, channel->channel_name,
strlen(channel->channel_name),
3, chidp->data, chidp->len,
- 4, mode, 4);
+ 4, mode, 4,
+ 5, tmp2, 4,
+ 6, keyp->data, keyp->len);
} else {
reply =
silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
- SILC_STATUS_OK, 0, 4,
+ SILC_STATUS_OK, ident, 6,
2, channel->channel_name,
strlen(channel->channel_name),
3, chidp->data, chidp->len,
4, mode, 4,
- 5, channel->topic,
+ 5, tmp2, 4,
+ 6, keyp->data, keyp->len,
+ 8, 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);
-
- /* Distribute new channel key to local cell and local clients. */
- silc_server_send_channel_key(server, channel, FALSE);
-
- /* Distribute JOIN notify into the cell for everbody on the channel */
+
+ /* Send command reply */
+ silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
+ reply->data, reply->len, FALSE);
+
+ if (!cmd->pending)
+ /* 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);
- /* Broadcast NEW_CHANNEL_USER packet to primary route */
+ /* Send NEW_CHANNEL_USER packet to our primary router */
+ if (!cmd->pending && !server->standalone)
silc_server_send_new_channel_user(server, server->router->connection,
- TRUE, channel->id, SILC_ID_CHANNEL_LEN,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE,
+ channel->id, SILC_ID_CHANNEL_LEN,
client->id, SILC_ID_CLIENT_LEN);
- silc_free(tmpid);
- } else {
- /* Client sent the command. Send all replies directly to the client. */
+ /* 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);
- /* Send command reply */
- silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
- reply->data, reply->len, FALSE);
+ /*
- /* Send the channel key. Channel key is sent before any other packet
- to the channel. */
- silc_server_send_channel_key(server, 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);
+ FAQ:
- /* 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);
+ * Kuinka NAMES komento händlätään serverissä kun router lähettää sen
+ serverille joka on lähettäny sille clientin puolesta JOIN komennon?
+
+ R: Serverin pitää ymmärtää NAMES comman replyjä.
- /* 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);
+ silc_buffer_free(keyp);
out:
if (passphrase)
SilcChannelEntry channel;
unsigned int umode = 0;
int created = FALSE;
+ SilcClientID *client_id;
- SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 3);
+ SILC_SERVER_COMMAND_CHECK_ARGC(SILC_COMMAND_JOIN, cmd, 1, 4);
/* Get channel name */
tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
goto out;
}
+ /* Get Client ID of the client who is joining to the channel */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+
/* Get cipher name */
- cipher = silc_argument_get_arg_type(cmd->args, 3, NULL);
+ cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
/* See if the channel exists */
channel = silc_idlist_find_channel_by_name(server->local_list,
channel_name, NULL);
- if (!channel) {
- /* Channel not found */
-
- /* If we are standalone server we don't have a router, we just create
- the channel by ourselves. */
- 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. 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) {
- /* Forward the original JOIN command to the router */
- silc_buffer_push(cmd->packet->buffer,
- cmd->packet->buffer->data -
- cmd->packet->buffer->head);
- silc_server_packet_forward(server, (SilcSocketConnection)
- server->router->connection,
- cmd->packet->buffer->data,
- cmd->packet->buffer->len, TRUE);
-
- /* Register handler that will be called after the router has replied
- to us. We will add the client to the new channel in this callback
- function. Will be called from JOIN command reply. */
- silc_server_command_pending(server, SILC_COMMAND_JOIN,
- ++server->router->data.cmd_ident,
- silc_server_command_add_to_channel,
- context);
- return;
+ if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ /* If this is coming from client the Client ID in the command packet must
+ be same as the client's ID. */
+ if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+ if (SILC_ID_CLIENT_COMPARE(entry->id, client_id)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
}
+ }
+
+ if (!channel) {
+ /* Channel not found */
+
+ /* If we are standalone server we don't have a router, we just create
+ the channel by ourselves. */
+ if (server->standalone) {
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ channel_name);
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+
+ } else {
+
+ /* 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 tmpbuf;
+ unsigned short old_ident;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ /* Send JOIN command to our router */
+ silc_server_packet_send(server, (SilcSocketConnection)
+ server->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, tmpbuf->len, TRUE);
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_JOIN,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_join, context);
+ cmd->pending = TRUE;
+ return;
+ }
+
+ /* We are router and the channel does not seem exist so we will check
+ our global list as well for the channel. */
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_name, NULL);
+ if (!channel) {
+ /* Channel really does not exist, create it */
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ channel_name);
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ }
+ }
+ }
+ } else {
+ if (!channel) {
+ /* Channel not found */
+
+ /* If the command came from router and/or we are normal server then
+ something went wrong with the joining as the channel was not found.
+ We can't do anything else but ignore this. */
+ if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
+ server->server_type == SILC_SERVER)
+ goto out;
/* We are router and the channel does not seem exist so we will check
our global list as well for the channel. */
/* Channel really does not exist, create it */
channel = silc_server_create_new_channel(server, server->id, cipher,
channel_name);
- umode |= SILC_CHANNEL_UMODE_CHANOP;
- umode |= SILC_CHANNEL_UMODE_CHANFO;
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
created = TRUE;
}
}
}
+ /* If the channel does not have global users and is also empty it means the
+ channel was created globally (by our router) and the client will be the
+ channel founder and operator. */
+ if (!channel->global_users && silc_list_count(channel->user_list) == 0) {
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE; /* Created globally by our router */
+ }
+
/* Join to the channel */
- silc_server_command_join_channel(server, cmd, channel, created, umode);
+ silc_server_command_join_channel(server, cmd, channel, client_id,
+ created, umode);
+
+ silc_free(client_id);
out:
silc_server_command_free(cmd);
if (cmd->cmd == command)
break;
- if (cmd == NULL) {
+ if (cmd == NULL || !cmd->cb) {
silc_free(ctx);
return;
}
unsigned int len;
unsigned char *id_string;
char *channel_name, *tmp;
+ unsigned int mode, created;
+ SilcBuffer keyp;
SILC_LOG_DEBUG(("Start"));
COMMAND_CHECK_STATUS;
/* Get channel name */
- tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
- if (!tmp)
+ channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
+ if (!channel_name)
goto out;
/* Get channel ID */
if (!id_string)
goto out;
- channel_name = strdup(tmp);
- id = silc_id_payload_parse_id(id_string, len);
+ /* Get mode mask */
+ tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+ if (!tmp)
+ goto out;
+ SILC_GET32_MSB(mode, tmp);
- /* XXX We should check that we have sent JOIN command to the router
- in the first place. Also should check that we don't have the channel
- already in the cache. These checks must be made because of possible
- buggy routers. */
+ /* Get created boolean value */
+ tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ if (!tmp)
+ goto out;
+ SILC_GET32_MSB(created, tmp);
- SILC_LOG_DEBUG(("Adding new channel %s id(%s)", channel_name,
- silc_id_render(id, SILC_ID_CHANNEL)));
+ /* Get channel key */
+ tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+ if (!tmp)
+ goto out;
+ keyp = silc_buffer_alloc(len);
+ silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
+ silc_buffer_put(keyp, tmp, len);
- /* Add the channel to our local list. */
- entry = silc_idlist_add_channel(server->local_list, channel_name,
- SILC_CHANNEL_MODE_NONE, id,
- server->router, NULL);
+ id = silc_id_payload_parse_id(id_string, len);
+
+ /* See whether we already have the channel. */
+ entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
if (!entry) {
- silc_free(channel_name);
+ /* Add new channel */
+
+ SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
+ (created == 0 ? "created" : "existing"), channel_name,
+ silc_id_render(id, SILC_ID_CHANNEL)));
+
+ /* Add the channel to our local list. */
+ entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
+ SILC_CHANNEL_MODE_NONE, id,
+ server->router, NULL);
+ if (!entry) {
+ silc_free(id);
+ goto out;
+ }
+ } else {
silc_free(id);
- goto out;
}
- //entry->global_users = TRUE;
+ /* If channel was not created we know there is global users on the
+ channel. */
+ entry->global_users = (created == 0 ? TRUE : FALSE);
+
+ /* If channel was just created the mask must be zero */
+ if (!entry->global_users && mode) {
+ SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
+ "new channel, forcing it to zero", cmd->sock->hostname));
+ mode = 0;
+ }
+
+ /* Save channel mode */
+ entry->mode = mode;
+
+ /* Save channel key */
+ silc_server_save_channel_key(server, keyp, entry);
+ silc_buffer_free(keyp);
/* Execute any pending commands */
SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
*ret_entry = id_cache;
}
+ SILC_LOG_DEBUG(("Found"));
+
return client;
}
if (ret_entry)
*ret_entry = id_cache;
+ SILC_LOG_DEBUG(("Found"));
+
return client;
}
if (ret_entry)
*ret_entry = id_cache;
+ SILC_LOG_DEBUG(("Found"));
+
return client;
}
silc_idcache_list_free(list);
+ SILC_LOG_DEBUG(("Found"));
+
return channel;
}
if (ret_entry)
*ret_entry = id_cache;
+ SILC_LOG_DEBUG(("Found"));
+
return channel;
}
/* Sanity checks */
if (packet->dst_id_type != SILC_ID_CHANNEL) {
- SILC_LOG_ERROR(("Received bad message for channel, dropped"));
SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
goto out;
}
id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Could not find channel"));
- goto out;
+ channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+ if (!channel) {
+ SILC_LOG_DEBUG(("Could not find channel"));
+ goto out;
+ }
}
/* See that this client is on the channel. If the message is coming
SilcPacketContext *packet)
{
SilcBuffer buffer = packet->buffer;
- SilcChannelKeyPayload payload = NULL;
- SilcChannelID *id = NULL;
SilcChannelEntry channel;
- SilcChannelClientEntry chl;
- unsigned char *tmp;
- unsigned int tmp_len;
- char *cipher;
- int exist = FALSE;
if (packet->src_id_type != SILC_ID_SERVER)
- goto out;
-
- /* Decode channel key payload */
- payload = silc_channel_key_payload_parse(buffer);
- if (!payload) {
- SILC_LOG_ERROR(("Bad channel key payload, dropped"));
- goto out;
- }
-
- /* Get channel ID */
- tmp = silc_channel_key_get_id(payload, &tmp_len);
- id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!id)
- goto out;
-
- /* Get the channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
- if (!channel) {
- SILC_LOG_ERROR(("Received key for non-existent channel"));
- goto out;
- }
-
- 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;
-
- /* Remove old key if exists */
- if (channel->key) {
- memset(channel->key, 0, channel->key_len / 8);
- 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;
+ return;
- /* 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);
+ /* Save the channel key */
+ channel = silc_server_save_channel_key(server, buffer, NULL);
+ if (!channel)
+ return;
/* Distribute the key to everybody who is on the channel. If we are router
we will also send it to locally connected servers. */
silc_server_send_channel_key(server, channel, FALSE);
-
- out:
- if (id)
- silc_free(id);
- if (payload)
- silc_channel_key_payload_free(payload);
}
/* Received packet to replace a ID. This checks that the requested ID
/* Replace the old ID */
switch(old_id_type) {
case SILC_ID_CLIENT:
+ SILC_LOG_DEBUG(("Old Client ID id(%s)",
+ silc_id_render(id, SILC_ID_CLIENT)));
+ SILC_LOG_DEBUG(("New Client ID id(%s)",
+ silc_id_render(id2, SILC_ID_CLIENT)));
if (silc_idlist_replace_client_id(server->local_list, id, id2) == NULL)
if (server->server_type == SILC_ROUTER)
silc_idlist_replace_client_id(server->global_list, id, id2);
break;
case SILC_ID_SERVER:
+ SILC_LOG_DEBUG(("Old Server ID id(%s)",
+ silc_id_render(id, SILC_ID_CLIENT)));
+ SILC_LOG_DEBUG(("New Server ID id(%s)",
+ silc_id_render(id2, SILC_ID_CLIENT)));
if (silc_idlist_replace_server_id(server->local_list, id, id2) == NULL)
if (server->server_type == SILC_ROUTER)
silc_idlist_replace_server_id(server->global_list, id, id2);
buffer->data, buffer->len, FALSE);
}
- /* XXX routers should check server->global_list as well */
/* Get channel entry */
channel = silc_idlist_find_channel_by_id(server->local_list,
channel_id, NULL);
- if (!channel)
- goto out;
-
- /* XXX routers should check server->global_list as well */
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel)
+ goto out;
+ }
+
/* Get client entry */
client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
- if (!client)
- goto out;
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
+ if (!client)
+ goto out;
+ }
/* Remove from channel */
silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
goto out;
}
+ channel->global_users = TRUE;
+
/* Send to channel */
silc_server_packet_send_to_channel(server, channel, packet->type, FALSE,
packet->buffer->data,
goto out;
/* Decode the client ID */
- client_id = silc_id_str2id(tmpid1, SILC_ID_CLIENT);
+ client_id = silc_id_str2id(tmpid2, SILC_ID_CLIENT);
if (!client_id)
goto out;
+#if 0
/* If the packet is originated from the one who sent it to us we know
that the ID belongs to our cell, unless the sender was router. */
tmpid = silc_id_str2id(packet->src_id, SILC_ID_SERVER);
router = server->router;
}
silc_free(tmpid);
+#endif
+
+ router_sock = sock;
+ router = sock->user_data;
/* Find the channel */
- channel = silc_idlist_find_channel_by_id(id_list, channel_id, NULL);
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
if (!channel) {
- SILC_LOG_ERROR(("Received channel user for non-existent channel"));
- goto out;
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel)
+ goto out;
}
/* If we are router and this packet is not already broadcast packet
packet->buffer->data, packet->buffer->len, FALSE);
}
+ SILC_LOG_DEBUG(("Client ID: %s", silc_id_render(client_id, SILC_ID_CLIENT)));
+
/* Get client entry */
- client = silc_idlist_find_client_by_id(id_list, client_id, NULL);
+ client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
if (!client) {
- /* This is new client to us, add entry to ID list */
- client = silc_idlist_add_client(id_list, NULL, NULL, NULL,
- client_id, router, router_sock);
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
if (!client)
goto out;
}
continue;
}
+ if (server->server_type == SILC_ROUTER && !route)
+ continue;
+
/* Send to locally connected client */
if (client) {
SILC_LOG_DEBUG(("Start"));
- clid = silc_id_id2str(client_id, SILC_ID_CLIENT);
- if (!clid)
- return;
-
chid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
if (!chid)
return;
+ clid = silc_id_id2str(client_id, SILC_ID_CLIENT);
+ if (!clid)
+ return;
+
packet = silc_buffer_alloc(2 + 2 + channel_id_len + client_id_len);
silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
silc_buffer_format(packet,
/* Send Channel Key payload to distribute the new channel key. Normal server
sends this to router when new client joins to existing channel. Router
- sends this to the local server who forwarded join command in case where
- the channel did not exist yet. Both normal and router servers uses this
+ sends this to the local server who sent the join command in case where
+ the channel did not exist yet. Both normal and router servers uses this
also to send this to locally connected clients on the channel. This
- must not be broadcasted packet. */
+ must not be broadcasted packet. Routers do not send this to each other. */
void silc_server_send_channel_key(SilcServer server,
SilcChannelEntry channel,
unsigned int len;
if (!channel->channel_key)
- return;
+ silc_cipher_alloc("twofish", &channel->channel_key);
if (key_len)
len = key_len;
memcpy(channel->key, channel_key, len);
memset(channel_key, 0, sizeof(channel_key));
}
+
+/* Saves the channel key found in the encoded `key_payload' buffer. This
+ function is used when we receive Channel Key Payload and also when we're
+ processing JOIN command reply. Returns entry to the channel. */
+
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+ SilcBuffer key_payload,
+ SilcChannelEntry channel)
+{
+ SilcChannelKeyPayload payload = NULL;
+ SilcChannelID *id = NULL;
+ unsigned char *tmp;
+ unsigned int tmp_len;
+ char *cipher;
+
+ /* Decode channel key payload */
+ payload = silc_channel_key_payload_parse(key_payload);
+ if (!payload) {
+ SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+ channel = NULL;
+ goto out;
+ }
+
+ /* Get the channel entry */
+ if (!channel) {
+
+ /* Get channel ID */
+ tmp = silc_channel_key_get_id(payload, &tmp_len);
+ id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
+ if (!id) {
+ channel = NULL;
+ goto out;
+ }
+
+ channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+ if (!channel) {
+ SILC_LOG_ERROR(("Received key for non-existent channel"));
+ goto out;
+ }
+ }
+
+ tmp = silc_channel_key_get_key(payload, &tmp_len);
+ if (!tmp) {
+ channel = NULL;
+ goto out;
+ }
+
+ cipher = silc_channel_key_get_cipher(payload, NULL);;
+ if (!cipher) {
+ channel = NULL;
+ goto out;
+ }
+
+ /* Remove old key if exists */
+ if (channel->key) {
+ memset(channel->key, 0, channel->key_len / 8);
+ silc_free(channel->key);
+ silc_cipher_free(channel->channel_key);
+ }
+
+ /* Create new cipher */
+ if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+ channel = NULL;
+ 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);
+
+ out:
+ if (id)
+ silc_free(id);
+ if (payload)
+ silc_channel_key_payload_free(payload);
+
+ return channel;
+}
void silc_server_create_channel_key(SilcServer server,
SilcChannelEntry channel,
unsigned int key_len);
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+ SilcBuffer key_payload,
+ SilcChannelEntry channel);
#endif
the client. After that, client starts using the key received
in this payload to protect the traffic on the channel.
+The client who is joining to the channel receives its key in the
+SILC_COMMAND_JOIN command reply message thus it is not necessary to
+send this payload to the entity who sent the SILC_COMMAND_JOIN command.
+
Channel keys are cell specific thus every router in cell have
to create a channel key and distribute it if any client in the
cell has joined to a channel. Channel traffic between cell's
or not. This way the new client on the channel is not able to decrypt
any of the old traffic on the channel. Client who receives the reply to
the join command must start using the received Channel ID in the channel
-message communication thereafter. However, client must not start
-communicating on the channel before it has received the packet
-SILC_PACKET_CHANNEL_KEY.
+message communication thereafter. Client also receives the key for the
+channel in the command reply.
If client wants to know the other clients currently on the channel
the client must send SILC_COMMAND_NAMES command to receive a list of
SILC_STATUS_LIST_START
SILC_STATUS_LIST_END
SILC_STATUS_ERR_NO_SUCH_NICK
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
SILC_STATUS_ERR_WILDCARDS
SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
SILC_STATUS_ERR_TOO_MANY_PARAMS
SILC_STATUS_LIST_START
SILC_STATUS_LIST_END
SILC_STATUS_ERR_NO_SUCH_NICK
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
SILC_STATUS_ERR_WILDCARDS
SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
SILC_STATUS_ERR_TOO_MANY_PARAMS
14 SILC_COMMAND_JOIN
- Max Arguments: 3
- Arguments: (1) <channel> (2) [<passphrase>]
- (3) [<cipher>]
+ Max Arguments: 4
+ Arguments: (1) <channel> (2) <Client ID>
+ (3) [<passphrase>] (4) [<cipher>]
Join to channel/create new channel. This command is used to
join to a channel. If the channel does not exist the channel is
The name of the <channel> must not include any spaces (` '),
non-printable characters, commas (`,') or any wildcard characters.
+ The second argument <Client ID> is the Client ID of the client who
+ is joining to the client. When client sends this command to the
+ server the <Client ID> must be the client's own ID.
+
Cipher to be used to secure the traffic on the channel may be
requested by sending the name of the requested <cipher>. This
is used only if the channel does not exist and is created. If
Reply messages to the command:
- Max Arguments: 6
+ Max Arguments: 9
Arguments: (1) <Status Payload> (2) <channel>
(3) <Channel ID> (4) <channel mode mask>
- (5) [<ban mask>] (6) [<invite list>]
- (6) [<topic>]
+ (5) <created> (6) <Channel Key Payload>
+ (7) [<ban mask>] (8) [<invite list>]
+ (9) [<topic>]
This command replies with the channel name requested by the
client, channel ID of the channel and topic of the channel
channel is created the mode mask is zero (0). If ban mask
and/or invite list is set they are sent as well.
- Client must not start transmitting to the channel even after
- server has replied to this command. Client is permitted to
- start transmitting on channel after server has sent packet
- SILC_PACKET_CHANNEL_KEY to the client.
+ Client receives the channel key in the reply message as well
+ inside <Channel Key Payload>.
Status messages:
(void *)channel->id, (void *)channel, TRUE);
}
-/* Processes received key for channel. The received key will be used
- to protect the traffic on the channel for now on. Client must receive
- the key to the channel before talking on the channel is possible.
- This is the key that server has generated, this is not the channel
- private key, it is entirely local setting. */
+/* Saves channel key from encoded `key_payload'. This is used when we
+ receive Channel Key Payload and when we are processing JOIN command
+ reply. */
-void silc_client_receive_channel_key(SilcClient client,
- SilcSocketConnection sock,
- SilcBuffer packet)
+void silc_client_save_channel_key(SilcClientConnection conn,
+ SilcBuffer key_payload,
+ SilcChannelEntry channel)
{
unsigned char *id_string, *key, *cipher;
unsigned int tmp_len;
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
SilcChannelID *id;
SilcIDCacheEntry id_cache = NULL;
- SilcChannelEntry channel;
SilcChannelKeyPayload payload;
- SILC_LOG_DEBUG(("Received key for channel"));
-
- payload = silc_channel_key_payload_parse(packet);
+ payload = silc_channel_key_payload_parse(key_payload);
if (!payload)
return;
id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
/* Find channel. */
- if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
- SILC_ID_CHANNEL, &id_cache))
- goto out;
+ if (!channel) {
+ if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
+ SILC_ID_CHANNEL, &id_cache))
+ goto out;
+ /* Get channel entry */
+ channel = (SilcChannelEntry)id_cache->context;
+ }
+
/* Save the key */
key = silc_channel_key_get_key(payload, &tmp_len);
cipher = silc_channel_key_get_cipher(payload, NULL);
-
- channel = (SilcChannelEntry)id_cache->context;
channel->key_len = tmp_len;
channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
memcpy(channel->key, key, tmp_len);
if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
- client->ops->say(client, conn,
+ conn->client->ops->say(conn->client, conn,
"Cannot talk to channel: unsupported cipher %s", cipher);
goto out;
}
silc_channel_key_payload_free(payload);
}
+/* Processes received key for channel. The received key will be used
+ to protect the traffic on the channel for now on. Client must receive
+ the key to the channel before talking on the channel is possible.
+ This is the key that server has generated, this is not the channel
+ private key, it is entirely local setting. */
+
+void silc_client_receive_channel_key(SilcClient client,
+ SilcSocketConnection sock,
+ SilcBuffer packet)
+{
+ SILC_LOG_DEBUG(("Received key for channel"));
+
+ /* Save the key */
+ silc_client_save_channel_key(sock->user_data, packet, NULL);
+}
+
/* Process received message to a channel (or from a channel, really). This
decrypts the channel message with channel specific key and parses the
channel payload. Finally it displays the message on the screen. */
SilcSocketConnection sock,
char *channel_name,
unsigned int mode, SilcIDPayload idp);
+void silc_client_save_channel_key(SilcClientConnection conn,
+ SilcBuffer key_payload,
+ SilcChannelEntry channel);
void silc_client_receive_channel_key(SilcClient client,
SilcSocketConnection sock,
SilcBuffer packet);
SilcClientCommandContext cmd = (SilcClientCommandContext)context;
SilcClientConnection conn = cmd->conn;
SilcIDCacheEntry id_cache = NULL;
- SilcBuffer buffer;
+ SilcBuffer buffer, idp;
if (!cmd->conn) {
SILC_NOT_CONNECTED(cmd->client, cmd->conn);
goto out;
}
+ idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
+
/* Send JOIN command to the server */
- if (cmd->argc == 2)
+ if (cmd->argc == 3)
buffer =
- silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 1,
- 1, cmd->argv[1], cmd->argv_lens[1]);
- else if (cmd->argc == 3)
+ silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
+ 1, cmd->argv[1], cmd->argv_lens[1],
+ 2, idp->data, idp->len);
+ else if (cmd->argc == 4)
/* XXX Buggy */
buffer =
- silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
+ silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
1, cmd->argv[1], cmd->argv_lens[1],
- 2, cmd->argv[2], cmd->argv_lens[2]);
+ 2, idp->data, idp->len,
+ 3, cmd->argv[2], cmd->argv_lens[2]);
else
buffer =
- silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
+ silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
1, cmd->argv[1], cmd->argv_lens[1],
- 2, cmd->argv[2], cmd->argv_lens[2],
- 3, cmd->argv[3], cmd->argv_lens[3]);
+ 2, idp->data, idp->len,
+ 3, cmd->argv[2], cmd->argv_lens[2],
+ 4, cmd->argv[3], cmd->argv_lens[3]);
silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
0, NULL, NULL, buffer->data, buffer->len, TRUE);
silc_buffer_free(buffer);
+ silc_buffer_free(idp);
/* Notify application */
COMMAND;
SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
SilcClient client = cmd->client;
SilcCommandStatus status;
- SilcIDPayload idp;
+ SilcIDPayload idp = NULL;
unsigned int argc, mode, len;
- char *topic, *tmp, *channel_name;
+ char *topic, *tmp, *channel_name = NULL;
+ SilcBuffer keyp;
SILC_LOG_DEBUG(("Start"));
}
argc = silc_argument_get_arg_num(cmd->args);
- if (argc < 3 || argc > 5) {
+ if (argc < 3 || argc > 9) {
cmd->client->ops->say(cmd->client, conn,
"Cannot join channel: Bad reply packet");
COMMAND_REPLY_ERROR;
cmd->client->ops->say(cmd->client, conn,
"Cannot join channel: Bad reply packet");
COMMAND_REPLY_ERROR;
+ silc_free(channel_name);
goto out;
}
idp = silc_id_payload_parse_data(tmp, len);
else
mode = 0;
+ /* Get channel key */
+ tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+ if (!tmp) {
+ silc_id_payload_free(idp);
+ silc_free(channel_name);
+ goto out;
+ }
+ keyp = silc_buffer_alloc(len);
+ silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
+ silc_buffer_put(keyp, tmp, len);
+
/* Get topic */
- topic = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
/* Save received Channel ID */
silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
mode, idp);
silc_id_payload_free(idp);
+ /* Save channel key */
+ silc_client_save_channel_key(conn, keyp, conn->current_channel);
+ silc_buffer_free(keyp);
+
if (topic)
client->ops->say(cmd->client, conn,
"Topic for %s: %s", channel_name, topic);