extern char *server_version;
-/* Received private message. This resolves the destination of the message
- and sends the packet. This is used by both server and router. If the
- destination is our locally connected client this sends the packet to
- the client. This may also send the message for further routing if
- the destination is not in our server (or router). */
+/* Received notify packet. Server can receive notify packets from router.
+ Server then relays the notify messages to clients if needed. */
-void silc_server_private_message(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+void silc_server_notify(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- SilcClientID *id;
- SilcServerEntry router;
- SilcSocketConnection dst_sock;
+ SilcNotifyPayload payload;
+ SilcNotifyType type;
+ SilcArgumentPayload args;
+ SilcChannelID *channel_id;
+ SilcClientID *client_id, *client_id2;
+ SilcChannelEntry channel;
SilcClientEntry client;
- SilcIDListData idata;
+ SilcChannelClientEntry chl;
+ unsigned int mode;
+ unsigned char *tmp;
+ unsigned int tmp_len;
SILC_LOG_DEBUG(("Start"));
- if (!packet->dst_id)
- goto err;
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+ packet->src_id_type != SILC_ID_SERVER)
+ return;
- /* Decode destination Client ID */
- id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
- if (!id) {
- SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
- goto err;
+ /* If we are router and this packet is not already broadcast packet
+ we will broadcast it. The sending socket really cannot be router or
+ the router is buggy. If this packet is coming from router then it must
+ have the broadcast flag set already and we won't do anything. */
+ if (!server->standalone && server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_SERVER &&
+ !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+ SILC_LOG_DEBUG(("Broadcasting received Notify packet"));
+ silc_server_packet_send(server, server->router->connection, packet->type,
+ packet->flags | SILC_PACKET_FLAG_BROADCAST,
+ packet->buffer->data, packet->buffer->len, FALSE);
}
- /* If the destination belongs to our server we don't have to route
- the message anywhere but to send it to the local destination. */
- client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
- if (client) {
- /* It exists, now deliver the message to the destination */
- dst_sock = (SilcSocketConnection)client->connection;
+ payload = silc_notify_payload_parse(packet->buffer);
+ if (!payload)
+ return;
- /* If we are router and the client has router then the client is in
- our cell but not directly connected to us. */
- if (server->server_type == SILC_ROUTER && client->router) {
- /* We are of course in this case the client's router thus the real
- "router" of the client is the server who owns the client. Thus
- we will send the packet to that server. */
- router = (SilcServerEntry)client->router;
- idata = (SilcIDListData)router;
+ type = silc_notify_get_type(payload);
+ args = silc_notify_get_args(payload);
+ if (!args)
+ goto out;
- silc_server_send_private_message(server, router->connection,
- idata->send_key,
- idata->hmac,
- packet);
- return;
+ switch(type) {
+ case SILC_NOTIFY_TYPE_JOIN:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
+ SILC_LOG_DEBUG(("JOIN notify"));
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp)
+ goto out;
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id)
+ goto out;
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
}
+ silc_free(channel_id);
- /* Seems that client really is directly connected to us */
- idata = (SilcIDListData)client;
- silc_server_send_private_message(server, dst_sock,
- idata->send_key,
- idata->hmac, packet);
- return;
- }
+ /* 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);
+ if (!client_id)
+ goto out;
- /* Destination belongs to someone not in this server. If we are normal
- server our action is to send the packet to our router. */
- if (server->server_type == SILC_SERVER && !server->standalone) {
- router = server->router;
+ /* Send to channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
- /* Send to primary route */
- if (router) {
- dst_sock = (SilcSocketConnection)router->connection;
- idata = (SilcIDListData)router;
- silc_server_send_private_message(server, dst_sock,
- idata->send_key,
- idata->hmac, packet);
+ /* If the the client is not in local list we check global list (ie. the
+ channel will be global channel) and if it does not exist then create
+ entry for the client. */
+ 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 the client the it is bogus */
+ if (server->server_type == SILC_ROUTER)
+ goto out;
+
+ 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);
+ goto out;
+ }
+ }
}
- return;
- }
- /* We are router and we will perform route lookup for the destination
- and send the message to fastest route. */
- if (server->server_type == SILC_ROUTER && !server->standalone) {
- /* Check first that the ID is valid */
- client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
- if (client) {
- dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
- router = (SilcServerEntry)dst_sock->user_data;
- idata = (SilcIDListData)router;
+ /* Do not add client to channel if it is there already */
+ if (silc_server_client_on_channel(client, channel))
+ break;
- /* Get fastest route and send packet. */
- if (router)
- silc_server_send_private_message(server, dst_sock,
- idata->send_key,
- idata->hmac, packet);
- return;
- }
- }
+ if (server->server_type == SILC_SERVER &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER)
+ /* The channel is global now */
+ channel->global_users = TRUE;
- err:
- silc_server_send_error(server, sock,
- "No such nickname: Private message not sent");
-}
+ /* JOIN the global client to the channel (local clients (if router
+ created the channel) is joined in the pending JOIN command). */
+ chl = silc_calloc(1, sizeof(*chl));
+ chl->client = client;
+ chl->channel = channel;
+ silc_list_add(channel->user_list, chl);
+ silc_list_add(client->channels, chl);
+ silc_free(client_id);
-/* Processes incoming command reply packet. The command reply packet may
- be destined to one of our clients or it may directly for us. We will
- call the command reply routine after processing the packet. */
+ break;
-void silc_server_command_reply(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcBuffer buffer = packet->buffer;
- SilcClientEntry client = NULL;
- SilcSocketConnection dst_sock;
- SilcIDListData idata;
- SilcClientID *id = NULL;
+ case SILC_NOTIFY_TYPE_LEAVE:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
+ SILC_LOG_DEBUG(("LEAVE notify"));
- SILC_LOG_DEBUG(("Start"));
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ packet->dst_id_type);
+ if (!channel_id)
+ goto out;
- /* Source must be server or router */
- if (packet->src_id_type != SILC_ID_SERVER &&
- sock->type != SILC_SOCKET_TYPE_ROUTER)
- return;
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
+ }
- if (packet->dst_id_type == SILC_ID_CHANNEL)
- return;
+ /* Get client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp) {
+ silc_free(channel_id);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id) {
+ silc_free(channel_id);
+ goto out;
+ }
- if (packet->dst_id_type == SILC_ID_CLIENT) {
- /* Destination must be one of ours */
- id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
- if (!id)
- return;
- client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ /* Send to channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
+
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
if (!client) {
- SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
- silc_free(id);
- return;
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, NULL);
+ if (!client) {
+ silc_free(client_id);
+ silc_free(channel_id);
+ goto out;
+ }
}
- }
+ silc_free(client_id);
- if (packet->dst_id_type == SILC_ID_SERVER) {
- /* For now this must be for us */
- if (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
- SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
- return;
- }
- }
+ /* Remove the user from channel */
+ silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+ break;
- /* Execute command reply locally for the command */
- silc_server_command_reply_process(server, sock, buffer);
+ case SILC_NOTIFY_TYPE_SIGNOFF:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
+ SILC_LOG_DEBUG(("SIGNOFF notify"));
- if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
- /* Relay the packet to the client */
-
- dst_sock = (SilcSocketConnection)client->connection;
- silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
- + packet->dst_id_len + packet->padlen);
-
- silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
- silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-
- idata = (SilcIDListData)client;
-
- /* Encrypt packet */
- silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf,
- buffer->len);
-
- /* Send the packet */
- silc_server_packet_send_real(server, dst_sock, TRUE);
-
- silc_free(id);
- }
-}
-
-/* Process received channel message. The message can be originated from
- client or server. */
-
-void silc_server_channel_message(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcChannelEntry channel = NULL;
- SilcChannelClientEntry chl;
- SilcChannelID *id = NULL;
- void *sender = NULL;
-
- SILC_LOG_DEBUG(("Processing channel message"));
-
- /* Sanity checks */
- if (packet->dst_id_type != SILC_ID_CHANNEL) {
- SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
- goto out;
- }
-
- /* Find channel entry */
- id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
- if (!id)
- goto out;
- channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
- if (!channel) {
- SILC_LOG_DEBUG(("Could not find channel"));
+ /* Get client ID */
+ tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!tmp)
goto out;
- }
- }
-
- /* See that this client is on the channel. If the message is coming
- from router we won't do the check as the message is from client that
- we don't know about. Also, if the original sender is not client
- (as it can be server as well) we don't do the check. */
- sender = silc_id_str2id(packet->src_id, packet->src_id_len,
- packet->src_id_type);
- if (!sender)
- goto out;
- if (sock->type != SILC_SOCKET_TYPE_ROUTER &&
- packet->src_id_type == SILC_ID_CLIENT) {
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender))
- break;
- }
- if (chl == SILC_LIST_END)
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
goto out;
- }
-
- /* Distribute the packet to our local clients. This will send the
- packet for further routing as well, if needed. */
- silc_server_packet_relay_to_channel(server, sock, channel, sender,
- packet->src_id_type,
- packet->buffer->data,
- packet->buffer->len, FALSE);
-
- out:
- if (sender)
- silc_free(sender);
- if (id)
- silc_free(id);
-}
-
-/* Received channel key packet. We distribute the key to all of our locally
- connected clients on the channel. */
-
-void silc_server_channel_key(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcBuffer buffer = packet->buffer;
- SilcChannelEntry channel;
-
- if (packet->src_id_type != SILC_ID_SERVER)
- return;
-
- /* 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, sock, channel, FALSE);
-}
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, NULL);
+ if (!client) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
-/* Received packet to replace a ID. This checks that the requested ID
- exists and replaces it with the new one. */
+ /* Get signoff message */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (tmp_len > 128)
+ tmp = NULL;
-void silc_server_replace_id(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcBuffer buffer = packet->buffer;
- unsigned char *old_id = NULL, *new_id = NULL;
- SilcIdType old_id_type, new_id_type;
- unsigned short old_id_len, new_id_len;
- void *id = NULL, *id2 = NULL;
- int ret;
+ /* Remove the client from all channels */
+ silc_server_remove_from_channels(server, NULL, client, tmp);
- if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- packet->src_id_type == SILC_ID_CLIENT)
- return;
+ /* Remove the client entry */
+ if (!silc_idlist_del_client(server->global_list, client))
+ silc_idlist_del_client(server->local_list, client);
+ break;
- SILC_LOG_DEBUG(("Replacing ID"));
+ case SILC_NOTIFY_TYPE_TOPIC_SET:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
- ret = silc_buffer_unformat(buffer,
- SILC_STR_UI_SHORT(&old_id_type),
- SILC_STR_UI16_NSTRING_ALLOC(&old_id, &old_id_len),
- SILC_STR_UI_SHORT(&new_id_type),
- SILC_STR_UI16_NSTRING_ALLOC(&new_id, &new_id_len),
- SILC_STR_END);
- if (ret == -1)
- goto out;
+ SILC_LOG_DEBUG(("TOPIC SET notify"));
- if (old_id_type != new_id_type)
- goto out;
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ packet->dst_id_type);
+ if (!channel_id)
+ goto out;
- if (old_id_len != silc_id_get_len(old_id_type) ||
- new_id_len != silc_id_get_len(new_id_type))
- goto out;
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
+ }
- id = silc_id_str2id(old_id, old_id_len, old_id_type);
- if (!id)
- goto out;
+ /* Get the topic */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp) {
+ silc_free(channel_id);
+ goto out;
+ }
- id2 = silc_id_str2id(new_id, new_id_len, new_id_type);
- if (!id2)
- goto out;
+ if (channel->topic)
+ silc_free(channel->topic);
+ channel->topic = silc_calloc(tmp_len, sizeof(*channel->topic));
+ memcpy(channel->topic, tmp, tmp_len);
- /* If we are router and this packet is not already broadcast packet
- we will broadcast it. The sending socket really cannot be router or
- the router is buggy. If this packet is coming from router then it must
- have the broadcast flag set already and we won't do anything. */
- if (!server->standalone && server->server_type == SILC_ROUTER &&
- sock->type == SILC_SOCKET_TYPE_SERVER &&
- !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received Replace ID packet"));
- silc_server_packet_send(server, server->router->connection, packet->type,
- packet->flags | SILC_PACKET_FLAG_BROADCAST,
- buffer->data, buffer->len, FALSE);
- }
+ /* Send the same notify to the channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
+ silc_free(channel_id);
+ break;
- /* Replace the old ID */
- switch(old_id_type) {
- case SILC_ID_CLIENT:
+ case SILC_NOTIFY_TYPE_NICK_CHANGE:
{
- SilcBuffer nidp, oidp;
- SilcClientEntry client = NULL;
+ /*
+ * Distribute the notify to local clients on the channel
+ */
+ unsigned char *id, *id2;
+ SILC_LOG_DEBUG(("NICK CHANGE notify"));
+
+ /* Get old client ID */
+ id = silc_argument_get_arg_type(args, 1, &tmp_len);
+ if (!id)
+ goto out;
+ client_id = silc_id_payload_parse_id(id, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Get new client ID */
+ id2 = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!id2)
+ goto out;
+ client_id2 = silc_id_payload_parse_id(id2, tmp_len);
+ if (!client_id2)
+ goto out;
+
SILC_LOG_DEBUG(("Old Client ID id(%s)",
- silc_id_render(id, SILC_ID_CLIENT)));
+ silc_id_render(client_id, SILC_ID_CLIENT)));
SILC_LOG_DEBUG(("New Client ID id(%s)",
- silc_id_render(id2, SILC_ID_CLIENT)));
+ silc_id_render(client_id2, SILC_ID_CLIENT)));
- if ((client = silc_idlist_replace_client_id(server->local_list,
- id, id2)) == NULL)
- if (server->server_type == SILC_ROUTER)
- client = silc_idlist_replace_client_id(server->global_list, id, id2);
-
- if (client) {
- oidp = silc_id_payload_encode(id, SILC_ID_CLIENT);
- nidp = silc_id_payload_encode(id2, SILC_ID_CLIENT);
+ /* Replace the Client ID */
+ client = silc_idlist_replace_client_id(server->global_list, client_id,
+ client_id2);
+ if (!client)
+ client = silc_idlist_replace_client_id(server->local_list, client_id,
+ client_id2);
+ if (client) {
/* The nickname is not valid anymore, set it NULL. This causes that
the nickname will be queried if someone wants to know it. */
if (client->nickname)
this client is joined to. */
silc_server_send_notify_on_channels(server, client,
SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
- oidp->data, oidp->len,
- nidp->data, nidp->len);
-
- silc_buffer_free(nidp);
- silc_buffer_free(oidp);
+ id, tmp_len,
+ id2, tmp_len);
}
+
+ silc_free(client_id);
+ if (!client)
+ silc_free(client_id2);
break;
}
- case SILC_ID_SERVER:
- SILC_LOG_DEBUG(("Old Server ID id(%s)",
- silc_id_render(id, SILC_ID_SERVER)));
- SILC_LOG_DEBUG(("New Server ID id(%s)",
- silc_id_render(id2, SILC_ID_SERVER)));
- 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);
- break;
-
- case SILC_ID_CHANNEL:
- SILC_LOG_DEBUG(("Old Channel ID id(%s)",
- silc_id_render(id, SILC_ID_CHANNEL)));
- SILC_LOG_DEBUG(("New Channel ID id(%s)",
- silc_id_render(id2, SILC_ID_CHANNEL)));
- if (silc_idlist_replace_channel_id(server->local_list, id, id2) == NULL)
- silc_idlist_replace_channel_id(server->global_list, id, id2);
- break;
-
- default:
- silc_free(id2);
- break;
- }
+ case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
+
+ SILC_LOG_DEBUG(("CMODE CHANGE notify"));
+
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ packet->dst_id_type);
+ if (!channel_id)
+ goto out;
- out:
- if (id)
- silc_free(id);
- if (old_id)
- silc_free(old_id);
- if (new_id)
- silc_free(new_id);
-}
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
+ }
+ /* Send the same notify to the channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
-/* Received New Client packet and processes it. Creates Client ID for the
- client. Client becomes registered after calling this functions. */
-
-SilcClientEntry silc_server_new_client(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcBuffer buffer = packet->buffer;
- SilcClientEntry client;
- SilcIDCacheEntry cache;
- SilcClientID *client_id;
- SilcBuffer reply;
- SilcIDListData idata;
- char *username = NULL, *realname = NULL, *id_string;
- int ret;
+ /* Get the mode */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp) {
+ silc_free(channel_id);
+ goto out;
+ }
- SILC_LOG_DEBUG(("Creating new client"));
+ SILC_GET32_MSB(mode, tmp);
- if (sock->type != SILC_SOCKET_TYPE_CLIENT)
- return NULL;
+ /* Change mode */
+ channel->mode = mode;
+ silc_free(channel_id);
+ break;
- /* Take client entry */
- client = (SilcClientEntry)sock->user_data;
- idata = (SilcIDListData)client;
+ case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
- /* Fetch the old client cache entry so that we can update it. */
- if (!silc_idcache_find_by_context(server->local_list->clients,
- sock->user_data, &cache)) {
- SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
- return NULL;
- }
+ SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
- /* Parse incoming packet */
- ret = silc_buffer_unformat(buffer,
- SILC_STR_UI16_STRING_ALLOC(&username),
- SILC_STR_UI16_STRING_ALLOC(&realname),
- SILC_STR_END);
- if (ret == -1) {
- if (username)
- silc_free(username);
- if (realname)
- silc_free(realname);
- return NULL;
- }
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ packet->dst_id_type);
+ if (!channel_id)
+ goto out;
- /* Create Client ID */
- silc_id_create_client_id(server->id, server->rng, server->md5hash,
- username, &client_id);
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
+ }
- /* Update client entry */
- idata->registered = TRUE;
- client->nickname = strdup(username);
- client->username = username;
- client->userinfo = realname ? realname : strdup("");
- client->id = client_id;
+ /* Get the mode */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp) {
+ silc_free(channel_id);
+ goto out;
+ }
+
+ SILC_GET32_MSB(mode, tmp);
- /* Update the cache entry */
- cache->id = (void *)client_id;
- cache->type = SILC_ID_CLIENT;
- cache->data = username;
- silc_idcache_sort_by_data(server->local_list->clients);
+ /* Get target client */
+ tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ if (!tmp)
+ goto out;
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id)
+ goto out;
+
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, NULL);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, NULL);
+ if (!client) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
- /* Notify our router about new client on the SILC network */
- if (!server->standalone)
- silc_server_send_new_id(server, (SilcSocketConnection)
- server->router->connection,
- server->server_type == SILC_ROUTER ? TRUE : FALSE,
- client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
-
- /* Send the new client ID to the client. */
- id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
- reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
- silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
- silc_buffer_format(reply,
- SILC_STR_UI_SHORT(SILC_ID_CLIENT),
- SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
- SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
- SILC_STR_END);
- silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0,
- reply->data, reply->len, FALSE);
- silc_free(id_string);
- silc_buffer_free(reply);
+ /* Get entry to the channel user list */
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+ if (chl->client == client) {
+ /* Change the mode */
+ chl->mode = mode;
+ break;
+ }
- /* Send some nice info to the client */
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Welcome to the SILC Network %s@%s",
- username, sock->hostname));
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Your host is %s, running version %s",
- server->config->server_info->server_name,
- server_version));
- if (server->server_type == SILC_ROUTER) {
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("There are %d clients on %d servers in SILC "
- "Network", server->stat.clients,
- server->stat.servers + 1));
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("There are %d clients on %d server in our cell",
- server->stat.cell_clients,
- server->stat.cell_servers + 1));
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("I have %d clients, %d channels, %d servers and "
- "%d routers",
- server->stat.my_clients,
- server->stat.my_channels,
- server->stat.my_servers,
- server->stat.my_routers));
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("%d server operators and %d router operators "
- "online",
- server->stat.my_server_ops,
- server->stat.my_router_ops));
- } else {
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("I have %d clients and %d channels formed",
- server->stat.my_clients,
- server->stat.my_channels));
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("%d operators online",
- server->stat.my_server_ops));
- }
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Your connection is secured with %s cipher, "
- "key length %d bits",
- idata->send_key->cipher->name,
- idata->send_key->cipher->key_len));
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Your current nickname is %s",
- client->nickname));
+ /* Send the same notify to the channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
+ silc_free(channel_id);
+ break;
- /* Send motd */
- silc_server_send_motd(server, sock);
+ case SILC_NOTIFY_TYPE_INVITE:
+ SILC_LOG_DEBUG(("INVITE notify (not-impl XXX)"));
+ break;
- return client;
-}
+ case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+ SILC_LOG_DEBUG(("CHANNEL CHANGE notify (not-impl XXX)"));
+ break;
-/* Create new server. This processes received New Server packet and
- saves the received Server ID. The server is our locally connected
- server thus we save all the information and save it to local list.
- This funtion can be used by both normal server and router server.
- If normal server uses this it means that its router has connected
- to the server. If router uses this it means that one of the cell's
- servers is connected to the router. */
+ case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+ SILC_LOG_DEBUG(("SERVER SIGNOFF notify (not-impl XXX)"));
+ break;
-SilcServerEntry silc_server_new_server(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- SilcBuffer buffer = packet->buffer;
- SilcServerEntry new_server;
- SilcIDCacheEntry cache;
- SilcServerID *server_id;
- SilcIDListData idata;
- unsigned char *server_name, *id_string;
- unsigned short id_len;
- int ret;
+ case SILC_NOTIFY_TYPE_KICKED:
+ /*
+ * Distribute the notify to local clients on the channel
+ */
+
+ SILC_LOG_DEBUG(("KICKED notify"));
+
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ packet->dst_id_type);
+ if (!channel_id)
+ goto out;
- SILC_LOG_DEBUG(("Creating new server"));
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_free(channel_id);
+ goto out;
+ }
+ }
+ silc_free(channel_id);
- if (sock->type != SILC_SOCKET_TYPE_SERVER &&
- sock->type != SILC_SOCKET_TYPE_ROUTER)
- return NULL;
+ /* 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);
+ if (!client_id)
+ goto out;
- /* Take server entry */
- new_server = (SilcServerEntry)sock->user_data;
- idata = (SilcIDListData)new_server;
+ /* Send to channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
- /* Fetch the old server cache entry so that we can update it. */
- if (!silc_idcache_find_by_context(server->local_list->servers,
- sock->user_data, &cache)) {
- SILC_LOG_ERROR(("Lost server's cache entry - bad thing"));
- return NULL;
- }
+ /* If the the client is not in local list we check global list */
+ 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) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
- /* Parse the incoming packet */
- ret = silc_buffer_unformat(buffer,
- SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
- SILC_STR_UI16_STRING_ALLOC(&server_name),
- SILC_STR_END);
- if (ret == -1) {
- if (id_string)
- silc_free(id_string);
- if (server_name)
- silc_free(server_name);
- return NULL;
- }
+ /* Remove the client from channel */
+ silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
- if (id_len > buffer->len) {
- silc_free(id_string);
- silc_free(server_name);
- return NULL;
- }
+ break;
- /* Get Server ID */
- server_id = silc_id_str2id(id_string, id_len, SILC_ID_SERVER);
- if (!server_id) {
- silc_free(id_string);
- silc_free(server_name);
- return NULL;
+ /* Ignore rest of the notify types for now */
+ case SILC_NOTIFY_TYPE_NONE:
+ case SILC_NOTIFY_TYPE_MOTD:
+ break;
+ default:
+ break;
}
- silc_free(id_string);
-
- /* Update client entry */
- idata->registered = TRUE;
- new_server->server_name = server_name;
- new_server->id = server_id;
-
- /* Update the cache entry */
- cache->id = (void *)server_id;
- cache->type = SILC_ID_SERVER;
- cache->data = server_name;
- silc_idcache_sort_by_data(server->local_list->servers);
-
- /* Distribute the information about new server in the SILC network
- to our router. If we are normal server we won't send anything
- since this connection must be our router connection. */
- if (server->server_type == SILC_ROUTER && !server->standalone &&
- server->router->connection != sock)
- silc_server_send_new_id(server, server->router->connection,
- TRUE, new_server->id, SILC_ID_SERVER,
- SILC_ID_SERVER_LEN);
- if (server->server_type == SILC_ROUTER)
- server->stat.cell_servers++;
-
- return new_server;
+ out:
+ silc_notify_payload_free(payload);
}
-/* Processes incoming New ID packet. New ID Payload is used to distribute
- information about newly registered clients and servers. */
-
-static void silc_server_new_id_real(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet,
- int broadcast)
+void silc_server_notify_list(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- SilcBuffer buffer = packet->buffer;
- SilcIDList id_list;
- SilcServerEntry router;
- SilcSocketConnection router_sock;
- SilcIDPayload idp;
- SilcIdType id_type;
- unsigned char *hash = NULL;
- void *id;
+ SilcPacketContext *new;
+ SilcBuffer buffer;
+ unsigned short len;
- SILC_LOG_DEBUG(("Processing new ID"));
+ SILC_LOG_DEBUG(("Processing New Notify List"));
if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- server->server_type == SILC_SERVER ||
packet->src_id_type != SILC_ID_SERVER)
return;
- idp = silc_id_payload_parse(buffer);
- if (!idp)
- return;
-
- id_type = silc_id_payload_get_type(idp);
-
- /* Normal server cannot have other normal server connections */
- if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
- goto out;
-
- id = silc_id_payload_get_id(idp);
- if (!id)
- goto out;
-
- /* If the sender of this packet is server and we are router we need to
- broadcast this packet to other routers in the network. */
- if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
- sock->type == SILC_SOCKET_TYPE_SERVER &&
- !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
- silc_server_packet_send(server, server->router->connection,
- packet->type,
- packet->flags | SILC_PACKET_FLAG_BROADCAST,
- buffer->data, buffer->len, FALSE);
- }
-
- if (sock->type == SILC_SOCKET_TYPE_SERVER)
- id_list = server->local_list;
- else
- id_list = server->global_list;
-
- router_sock = sock;
- router = sock->user_data;
-
- switch(id_type) {
- case SILC_ID_CLIENT:
- {
- SilcClientEntry entry;
-
- SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
- silc_id_render(id, SILC_ID_CLIENT),
- sock->type == SILC_SOCKET_TYPE_SERVER ?
- "Server" : "Router", sock->hostname));
-
- /* As a router we keep information of all global information in our
- global list. Cell wide information however is kept in the local
- list. The client is put to global list and we will take the hash
- value of the Client ID and save it to the ID Cache system for fast
- searching in the future. */
- hash = silc_calloc(sizeof(((SilcClientID *)id)->hash),
- sizeof(unsigned char));
- memcpy(hash, ((SilcClientID *)id)->hash,
- sizeof(((SilcClientID *)id)->hash));
- entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id,
- router, NULL);
- entry->nickname = NULL;
-
- if (sock->type == SILC_SOCKET_TYPE_SERVER)
- server->stat.cell_clients++;
- server->stat.clients++;
-
-#if 0
- /* XXX Adding two ID's with same IP number replaces the old entry thus
- gives wrong route. Thus, now disabled until figured out a better way
- to do this or when removed the whole thing. This could be removed
- because entry->router->connection gives always the most optimal route
- for the ID anyway (unless new routes (faster perhaps) are established
- after receiving this ID, this we don't know however). */
- /* Add route cache for this ID */
- silc_server_route_add(silc_server_route_hash(
- ((SilcClientID *)id)->ip.s_addr,
- server->id->port), ((SilcClientID *)id)->ip.s_addr,
- router);
-#endif
- }
- break;
-
- case SILC_ID_SERVER:
- SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
- silc_id_render(id, SILC_ID_SERVER),
- sock->type == SILC_SOCKET_TYPE_SERVER ?
- "Server" : "Router", sock->hostname));
-
- /* As a router we keep information of all global information in our global
- list. Cell wide information however is kept in the local list. */
- silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
-
- if (sock->type == SILC_SOCKET_TYPE_SERVER)
- server->stat.cell_servers++;
- server->stat.servers++;
-
-#if 0
- /* Add route cache for this ID */
- silc_server_route_add(silc_server_route_hash(
- ((SilcServerID *)id)->ip.s_addr,
- ((SilcServerID *)id)->port),
- ((SilcServerID *)id)->ip.s_addr,
- router);
-#endif
- break;
+ /* Make copy of the original packet context, except for the actual
+ data buffer, which we will here now fetch from the original buffer. */
+ new = silc_packet_context_alloc();
+ new->type = SILC_PACKET_NOTIFY;
+ new->flags = packet->flags;
+ new->src_id = packet->src_id;
+ new->src_id_len = packet->src_id_len;
+ new->src_id_type = packet->src_id_type;
+ new->dst_id = packet->dst_id;
+ new->dst_id_len = packet->dst_id_len;
+ new->dst_id_type = packet->dst_id_type;
- case SILC_ID_CHANNEL:
- SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
- break;
+ buffer = silc_buffer_alloc(1024);
+ new->buffer = buffer;
- default:
- break;
- }
+ while (packet->buffer->len) {
+ SILC_GET16_MSB(len, packet->buffer->data + 2);
+ if (len > packet->buffer->len)
+ break;
- out:
- silc_id_payload_free(idp);
-}
+ if (len > buffer->truelen) {
+ silc_buffer_free(buffer);
+ buffer = silc_buffer_alloc(1024 + len);
+ }
+ silc_buffer_pull_tail(buffer, len);
+ silc_buffer_put(buffer, packet->buffer->data, len);
-/* Processes incoming New ID packet. New ID Payload is used to distribute
- information about newly registered clients and servers. */
+ /* Process the Notify */
+ silc_server_notify(server, sock, new);
-void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- silc_server_new_id_real(server, sock, packet, TRUE);
+ silc_buffer_push_tail(buffer, len);
+ silc_buffer_pull(packet->buffer, len);
+ }
+
+ silc_buffer_free(buffer);
+ silc_free(new);
}
-/* Receoved New Id List packet, list of New ID payloads inside one
- packet. Process the New ID payloads one by one. */
+/* Received private message. This resolves the destination of the message
+ and sends the packet. This is used by both server and router. If the
+ destination is our locally connected client this sends the packet to
+ the client. This may also send the message for further routing if
+ the destination is not in our server (or router). */
-void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
- SilcPacketContext *packet)
+void silc_server_private_message(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- SilcPacketContext *new_id;
- SilcBuffer idp;
- unsigned short id_len;
+ SilcClientID *id;
+ SilcServerEntry router;
+ SilcSocketConnection dst_sock;
+ SilcClientEntry client;
+ SilcIDListData idata;
- SILC_LOG_DEBUG(("Processing New ID List"));
+ SILC_LOG_DEBUG(("Start"));
- if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- packet->src_id_type != SILC_ID_SERVER)
+ if (packet->src_id_type != SILC_ID_CLIENT ||
+ packet->dst_id_type != SILC_ID_CLIENT)
return;
- /* If the sender of this packet is server and we are router we need to
- broadcast this packet to other routers in the network. Broadcast
- this list packet instead of multiple New ID packets. */
- if (!server->standalone && server->server_type == SILC_ROUTER &&
- sock->type == SILC_SOCKET_TYPE_SERVER &&
- !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received New ID List packet"));
- silc_server_packet_send(server, server->router->connection,
- packet->type,
- packet->flags | SILC_PACKET_FLAG_BROADCAST,
- packet->buffer->data, packet->buffer->len, FALSE);
+ if (!packet->dst_id)
+ return;
+
+ /* Decode destination Client ID */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+ if (!id) {
+ SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+ return;
}
- /* Make copy of the original packet context, except for the actual
- data buffer, which we will here now fetch from the original buffer. */
- new_id = silc_packet_context_alloc();
- new_id->type = SILC_PACKET_NEW_ID;
- new_id->flags = packet->flags;
- new_id->src_id = packet->src_id;
- new_id->src_id_len = packet->src_id_len;
- new_id->src_id_type = packet->src_id_type;
- new_id->dst_id = packet->dst_id;
- new_id->dst_id_len = packet->dst_id_len;
- new_id->dst_id_type = packet->dst_id_type;
+ /* If the destination belongs to our server we don't have to route
+ the message anywhere but to send it to the local destination. */
+ client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ if (client) {
+ /* It exists, now deliver the message to the destination */
+ dst_sock = (SilcSocketConnection)client->connection;
- idp = silc_buffer_alloc(256);
- new_id->buffer = idp;
+ /* If we are router and the client has router then the client is in
+ our cell but not directly connected to us. */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ /* We are of course in this case the client's router thus the real
+ "router" of the client is the server who owns the client. Thus
+ we will send the packet to that server. */
+ router = (SilcServerEntry)client->router;
+ idata = (SilcIDListData)router;
- while (packet->buffer->len) {
- SILC_GET16_MSB(id_len, packet->buffer->data + 2);
- if ((id_len > packet->buffer->len) ||
- (id_len > idp->truelen))
- break;
+ silc_server_send_private_message(server, router->connection,
+ idata->send_key,
+ idata->hmac,
+ packet);
+ return;
+ }
- silc_buffer_pull_tail(idp, 4 + id_len);
- silc_buffer_put(idp, packet->buffer->data, 4 + id_len);
+ /* Seems that client really is directly connected to us */
+ idata = (SilcIDListData)client;
+ silc_server_send_private_message(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
- /* Process the New ID */
- silc_server_new_id_real(server, sock, new_id, FALSE);
+ /* Destination belongs to someone not in this server. If we are normal
+ server our action is to send the packet to our router. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ router = server->router;
- silc_buffer_push_tail(idp, 4 + id_len);
- silc_buffer_pull(packet->buffer, 4 + id_len);
+ /* Send to primary route */
+ if (router) {
+ dst_sock = (SilcSocketConnection)router->connection;
+ idata = (SilcIDListData)router;
+ silc_server_send_private_message(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ }
+ return;
}
- silc_buffer_free(idp);
- silc_free(new_id);
+ /* We are router and we will perform route lookup for the destination
+ and send the message to fastest route. */
+ if (server->server_type == SILC_ROUTER && !server->standalone) {
+ /* Check first that the ID is valid */
+ client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ if (client) {
+ dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+ router = (SilcServerEntry)dst_sock->user_data;
+ idata = (SilcIDListData)router;
+
+ /* Get fastest route and send packet. */
+ if (router)
+ silc_server_send_private_message(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
+ }
}
-/* Received New Channel packet. Information about new channels in the
- network are distributed using this packet. Save the information about
- the new channel. This usually comes from router but also normal server
- can send this to notify channels it has when it connects to us. */
+/* Received private message key packet.. This packet is never for us. It is to
+ the client in the packet's destination ID. Sending of this sort of packet
+ equals sending private message, ie. it is sent point to point from
+ one client to another. */
-void silc_server_new_channel(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+void silc_server_private_message_key(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- unsigned char *id;
- SilcChannelID *channel_id;
- unsigned short channel_id_len;
- char *channel_name;
- int ret;
+ SilcClientID *id;
+ SilcServerEntry router;
+ SilcSocketConnection dst_sock;
+ SilcClientEntry client;
+ SilcIDListData idata;
- SILC_LOG_DEBUG(("Processing New Channel"));
+ SILC_LOG_DEBUG(("Start"));
- if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- packet->src_id_type != SILC_ID_SERVER ||
- server->server_type == SILC_SERVER)
+ if (packet->src_id_type != SILC_ID_CLIENT ||
+ packet->dst_id_type != SILC_ID_CLIENT)
return;
- /* Parse payload */
- ret = silc_buffer_unformat(packet->buffer,
- SILC_STR_UI16_STRING_ALLOC(&channel_name),
- SILC_STR_UI16_NSTRING_ALLOC(&id, &channel_id_len),
- SILC_STR_END);
- if (ret == -1) {
- if (channel_name)
- silc_free(channel_name);
- if (id)
- silc_free(id);
+ if (!packet->dst_id)
return;
- }
-
- /* Decode the channel ID */
- channel_id = silc_id_str2id(id, channel_id_len, SILC_ID_CHANNEL);
- if (!channel_id)
+
+ /* Decode destination Client ID */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+ if (!id) {
+ SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
return;
+ }
- if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
- /* Add the server to global list as it is coming from router. It
- cannot be our own channel as it is coming from router. */
+ /* If the destination belongs to our server we don't have to route
+ the message anywhere but to send it to the local destination. */
+ client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ if (client) {
+ /* It exists, now deliver the message to the destination */
+ dst_sock = (SilcSocketConnection)client->connection;
- SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
- silc_id_render(channel_id, SILC_ID_CHANNEL),
- sock->hostname));
-
- silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id,
- server->router->connection, NULL);
+ /* If we are router and the client has router then the client is in
+ our cell but not directly connected to us. */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ /* We are of course in this case the client's router thus the real
+ "router" of the client is the server who owns the client. Thus
+ we will send the packet to that server. */
+ router = (SilcServerEntry)client->router;
+ idata = (SilcIDListData)router;
+ silc_server_send_private_message_key(server, router->connection,
+ idata->send_key,
+ idata->hmac,
+ packet);
+ return;
+ }
- server->stat.channels++;
- } else {
- /* The channel is coming from our server, thus it is in our cell
- we will add it to our local list. */
- SilcChannelEntry channel;
- SilcBuffer chk;
+ /* Seems that client really is directly connected to us */
+ idata = (SilcIDListData)client;
+ silc_server_send_private_message_key(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
- SILC_LOG_DEBUG(("New channel id(%s) from [Server] %s",
- silc_id_render(channel_id, SILC_ID_CHANNEL),
- sock->hostname));
-
- /* Check that we don't already have this channel */
- channel = silc_idlist_find_channel_by_name(server->local_list,
- channel_name, NULL);
- if (!channel)
- channel = silc_idlist_find_channel_by_name(server->global_list,
- channel_name, NULL);
+ /* Destination belongs to someone not in this server. If we are normal
+ server our action is to send the packet to our router. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ router = server->router;
- /* If the channel does not exist, then create it. We create the channel
- with the channel ID provided by the server. This creates a new
- key to the channel as well that we will send to the server. */
- if (!channel) {
- channel = silc_server_create_new_channel_with_id(server, NULL,
- channel_name,
- channel_id, FALSE);
- if (!channel)
- return;
+ /* Send to primary route */
+ if (router) {
+ dst_sock = (SilcSocketConnection)router->connection;
+ idata = (SilcIDListData)router;
+ silc_server_send_private_message_key(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ }
+ return;
+ }
- /* Send the new channel key to the server */
- chk = silc_channel_key_payload_encode(channel_id_len, id,
- 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,
- chk->data, chk->len, FALSE);
- silc_buffer_free(chk);
+ /* We are router and we will perform route lookup for the destination
+ and send the packet to fastest route. */
+ if (server->server_type == SILC_ROUTER && !server->standalone) {
+ /* Check first that the ID is valid */
+ client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ if (client) {
+ dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+ router = (SilcServerEntry)dst_sock->user_data;
+ idata = (SilcIDListData)router;
- } else {
- /* The channel exist by that name, check whether the ID's match.
- If they don't then we'll force the server to use the ID we have.
- We also create a new key for the channel. */
+ /* Get fastest route and send packet. */
+ if (router)
+ silc_server_send_private_message_key(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
+ }
+}
- if (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
- /* They don't match, send Replace ID packet to the server to
- force the ID change. */
- SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
- silc_server_send_replace_id(server, sock, FALSE,
- channel_id, SILC_ID_CHANNEL,
- SILC_ID_CHANNEL_LEN,
- channel->id, SILC_ID_CHANNEL,
- SILC_ID_CHANNEL_LEN);
- }
+/* Processes incoming command reply packet. The command reply packet may
+ be destined to one of our clients or it may directly for us. We will
+ call the command reply routine after processing the packet. */
- /* Create new key for the channel and send it to the server and
- everybody else possibly on the channel. */
+void silc_server_command_reply(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcClientEntry client = NULL;
+ SilcSocketConnection dst_sock;
+ SilcIDListData idata;
+ SilcClientID *id = NULL;
- silc_server_create_channel_key(server, channel, 0);
+ SILC_LOG_DEBUG(("Start"));
- /* Send to the channel */
- silc_server_send_channel_key(server, sock, channel, FALSE);
+ /* Source must be server or router */
+ if (packet->src_id_type != SILC_ID_SERVER &&
+ sock->type != SILC_SOCKET_TYPE_ROUTER)
+ return;
- /* Send to the server */
- chk = silc_channel_key_payload_encode(channel_id_len, id,
- 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,
- chk->data, chk->len, FALSE);
- silc_buffer_free(chk);
+ if (packet->dst_id_type == SILC_ID_CHANNEL)
+ return;
- /* Since the channel is coming from server and we also know about it
- then send the JOIN notify to the server so that it see's our
- users on the channel "joining" the channel. */
- /* XXX TODO **/
+ if (packet->dst_id_type == SILC_ID_CLIENT) {
+ /* Destination must be one of ours */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+ if (!id)
+ return;
+ client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ if (!client) {
+ SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
+ silc_free(id);
+ return;
}
}
- silc_free(id);
+ if (packet->dst_id_type == SILC_ID_SERVER) {
+ /* For now this must be for us */
+ if (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+ SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
+ return;
+ }
+ }
+
+ /* Execute command reply locally for the command */
+ silc_server_command_reply_process(server, sock, buffer);
+
+ if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
+ /* Relay the packet to the client */
+
+ dst_sock = (SilcSocketConnection)client->connection;
+ silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+ + packet->dst_id_len + packet->padlen);
+
+ silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
+ silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+
+ idata = (SilcIDListData)client;
+
+ /* Encrypt packet */
+ silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf,
+ buffer->len);
+
+ /* Send the packet */
+ silc_server_packet_send_real(server, dst_sock, TRUE);
+
+ silc_free(id);
+ }
}
-/* Received New Channel List packet, list of New Channel List payloads inside
- one packet. Process the New Channel payloads one by one. */
+/* Process received channel message. The message can be originated from
+ client or server. */
-void silc_server_new_channel_list(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+void silc_server_channel_message(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- SilcPacketContext *new;
- SilcBuffer buffer;
- unsigned short len1, len2;
+ SilcChannelEntry channel = NULL;
+ SilcChannelClientEntry chl;
+ SilcChannelID *id = NULL;
+ void *sender = NULL;
- SILC_LOG_DEBUG(("Processing New Channel List"));
+ SILC_LOG_DEBUG(("Processing channel message"));
- if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- packet->src_id_type != SILC_ID_SERVER ||
- server->server_type == SILC_SERVER)
- return;
+ /* Sanity checks */
+ if (packet->dst_id_type != SILC_ID_CHANNEL) {
+ SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
+ goto out;
+ }
- /* If the sender of this packet is server and we are router we need to
- broadcast this packet to other routers in the network. Broadcast
- this list packet instead of multiple New Channel packets. */
- if (!server->standalone && server->server_type == SILC_ROUTER &&
- sock->type == SILC_SOCKET_TYPE_SERVER &&
- !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received New Channel List packet"));
- silc_server_packet_send(server, server->router->connection,
- packet->type,
- packet->flags | SILC_PACKET_FLAG_BROADCAST,
- packet->buffer->data, packet->buffer->len, FALSE);
+ /* Find channel entry */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+ if (!id)
+ goto out;
+ channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+ if (!channel) {
+ SILC_LOG_DEBUG(("Could not find channel"));
+ goto out;
+ }
}
- /* Make copy of the original packet context, except for the actual
- data buffer, which we will here now fetch from the original buffer. */
- new = silc_packet_context_alloc();
- new->type = SILC_PACKET_NEW_CHANNEL;
- new->flags = packet->flags;
- new->src_id = packet->src_id;
- new->src_id_len = packet->src_id_len;
- new->src_id_type = packet->src_id_type;
- new->dst_id = packet->dst_id;
- new->dst_id_len = packet->dst_id_len;
- new->dst_id_type = packet->dst_id_type;
+ /* See that this client is on the channel. If the message is coming
+ from router we won't do the check as the message is from client that
+ we don't know about. Also, if the original sender is not client
+ (as it can be server as well) we don't do the check. */
+ sender = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!sender)
+ goto out;
+ if (sock->type != SILC_SOCKET_TYPE_ROUTER &&
+ packet->src_id_type == SILC_ID_CLIENT) {
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender))
+ break;
+ }
+ if (chl == SILC_LIST_END) {
+ SILC_LOG_DEBUG(("Client not on channel"));
+ goto out;
+ }
+ }
- buffer = silc_buffer_alloc(512);
- new->buffer = buffer;
+ /* If we are router and the packet came from router and private key
+ has not been set for the channel then we must encrypt the packet
+ as it was decrypted with the session key shared between us and the
+ router which sent it. This is so, because cells does not share the
+ same channel key */
+ if (server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER &&
+ !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ SilcBuffer chp;
+ unsigned int iv_len, i, data_len;
+
+ iv_len = silc_cipher_get_block_len(channel->channel_key);
+ if (channel->iv[0] == '\0')
+ for (i = 0; i < iv_len; i++) channel->iv[i] =
+ silc_rng_get_byte(server->rng);
+ else
+ silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
+
+ /* Encode new payload. This encrypts it also. */
+ SILC_GET16_MSB(data_len, packet->buffer->data);
+ chp = silc_channel_payload_encode(data_len, packet->buffer->data + 2,
+ iv_len, channel->iv,
+ channel->channel_key,
+ channel->hmac, server->rng);
+ silc_buffer_put(packet->buffer, chp->data, chp->len);
+ silc_buffer_free(chp);
+ }
- while (packet->buffer->len) {
- SILC_GET16_MSB(len1, packet->buffer->data);
- if ((len1 > packet->buffer->len) ||
- (len1 > buffer->truelen))
- break;
+ /* Distribute the packet to our local clients. This will send the
+ packet for further routing as well, if needed. */
+ silc_server_packet_relay_to_channel(server, sock, channel, sender,
+ packet->src_id_type,
+ packet->buffer->data,
+ packet->buffer->len, FALSE);
- SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
- if ((len2 > packet->buffer->len) ||
- (len2 > buffer->truelen))
- break;
+ out:
+ if (sender)
+ silc_free(sender);
+ if (id)
+ silc_free(id);
+}
- silc_buffer_pull_tail(buffer, 4 + len1 + len2);
- silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2);
+/* Received channel key packet. We distribute the key to all of our locally
+ connected clients on the channel. */
- /* Process the New Channel */
- silc_server_new_channel(server, sock, new);
+void silc_server_channel_key(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer;
+ SilcChannelEntry channel;
- silc_buffer_push_tail(buffer, 4 + len1 + len2);
- silc_buffer_pull(packet->buffer, 4 + len1 + len2);
- }
+ if (packet->src_id_type != SILC_ID_SERVER)
+ return;
- silc_buffer_free(buffer);
- silc_free(new);
+ /* 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, sock, channel, FALSE);
}
-/* Received new channel user packet. Information about new users on a
- channel are distributed between routers using this packet. The
- router receiving this will redistribute it and also sent JOIN notify
- to local clients on the same channel. Normal server sends JOIN notify
- to its local clients on the channel. */
+/* Received New Client packet and processes it. Creates Client ID for the
+ client. Client becomes registered after calling this functions. */
-static void silc_server_new_channel_user_real(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet,
- int broadcast)
+SilcClientEntry silc_server_new_client(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- unsigned char *tmpid1, *tmpid2;
- SilcClientID *client_id = NULL;
- SilcChannelID *channel_id = NULL;
- unsigned short channel_id_len;
- unsigned short client_id_len;
+ SilcBuffer buffer = packet->buffer;
SilcClientEntry client;
- SilcChannelEntry channel;
- SilcChannelClientEntry chl;
- SilcBuffer clidp;
+ SilcIDCacheEntry cache;
+ SilcClientID *client_id;
+ SilcBuffer reply;
+ SilcIDListData idata;
+ char *username = NULL, *realname = NULL, *id_string;
int ret;
- SILC_LOG_DEBUG(("Start"));
-
- if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- server->server_type != SILC_ROUTER ||
- packet->src_id_type != SILC_ID_SERVER)
- return;
-
- /* Parse payload */
- ret = silc_buffer_unformat(packet->buffer,
- SILC_STR_UI16_NSTRING_ALLOC(&tmpid1,
- &channel_id_len),
- SILC_STR_UI16_NSTRING_ALLOC(&tmpid2,
- &client_id_len),
- SILC_STR_END);
- if (ret == -1) {
- if (tmpid1)
- silc_free(tmpid1);
- if (tmpid2)
- silc_free(tmpid2);
- return;
- }
+ SILC_LOG_DEBUG(("Creating new client"));
- /* Decode the channel ID */
- channel_id = silc_id_str2id(tmpid1, channel_id_len, SILC_ID_CHANNEL);
- if (!channel_id)
- goto out;
+ if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+ return NULL;
- /* Decode the client ID */
- client_id = silc_id_str2id(tmpid2, client_id_len, SILC_ID_CLIENT);
- if (!client_id)
- goto out;
+ /* Take client entry */
+ client = (SilcClientEntry)sock->user_data;
+ idata = (SilcIDListData)client;
- /* If we are router and this packet is not already broadcast packet
- we will broadcast it. */
- if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
- !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received New Channel User packet"));
- silc_server_packet_send(server, server->router->connection, packet->type,
- packet->flags | SILC_PACKET_FLAG_BROADCAST,
- packet->buffer->data, packet->buffer->len, FALSE);
+ /* Fetch the old client cache entry so that we can update it. */
+ if (!silc_idcache_find_by_context(server->local_list->clients,
+ sock->user_data, &cache)) {
+ SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
+ return NULL;
}
- /* Find the channel */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list,
- channel_id, NULL);
- if (!channel)
- goto out;
+ /* Parse incoming packet */
+ ret = silc_buffer_unformat(buffer,
+ SILC_STR_UI16_STRING_ALLOC(&username),
+ SILC_STR_UI16_STRING_ALLOC(&realname),
+ SILC_STR_END);
+ if (ret == -1) {
+ if (username)
+ silc_free(username);
+ if (realname)
+ silc_free(realname);
+ return NULL;
}
- /* Get client entry */
- 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)
- goto out;
+ if (!username) {
+ silc_free(username);
+ if (realname)
+ silc_free(realname);
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete client information");
+ return NULL;
}
- /* 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. */
- chl = silc_calloc(1, sizeof(*chl));
- chl->client = client;
- chl->channel = channel;
- silc_list_add(channel->user_list, chl);
- silc_list_add(client->channels, chl);
+ /* Create Client ID */
+ silc_id_create_client_id(server->id, server->rng, server->md5hash,
+ username, &client_id);
- server->stat.chanclients++;
+ /* Update client entry */
+ idata->registered = TRUE;
+ client->nickname = strdup(username);
+ client->username = username;
+ client->userinfo = realname ? realname : strdup(" ");
+ client->id = client_id;
- /* Send JOIN notify to local clients on the channel. As we are router
- it is assured that this is sent only to our local clients and locally
- connected servers if needed. */
- clidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
- silc_server_send_notify_to_channel(server, sock, channel, FALSE,
- SILC_NOTIFY_TYPE_JOIN,
- 1, clidp->data, clidp->len);
- silc_buffer_free(clidp);
+ /* Update the cache entry */
+ cache->id = (void *)client_id;
+ cache->type = SILC_ID_CLIENT;
+ cache->data = username;
+ silc_idcache_sort_by_data(server->local_list->clients);
- client_id = NULL;
+ /* Notify our router about new client on the SILC network */
+ if (!server->standalone)
+ silc_server_send_new_id(server, (SilcSocketConnection)
+ server->router->connection,
+ server->server_type == SILC_ROUTER ? TRUE : FALSE,
+ client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+
+ /* Send the new client ID to the client. */
+ id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
+ reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
+ silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
+ silc_buffer_format(reply,
+ SILC_STR_UI_SHORT(SILC_ID_CLIENT),
+ SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
+ SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
+ SILC_STR_END);
+ silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0,
+ reply->data, reply->len, FALSE);
+ silc_free(id_string);
+ silc_buffer_free(reply);
- out:
- if (client_id)
- silc_free(client_id);
- if (channel_id)
- silc_free(channel_id);
- silc_free(tmpid1);
- silc_free(tmpid2);
-}
+ /* Send some nice info to the client */
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Welcome to the SILC Network %s@%s",
+ username, sock->hostname));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your host is %s, running version %s",
+ server->config->server_info->server_name,
+ server_version));
+ if (server->server_type == SILC_ROUTER) {
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("There are %d clients on %d servers in SILC "
+ "Network", server->stat.clients,
+ server->stat.servers + 1));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("There are %d clients on %d server in our cell",
+ server->stat.cell_clients,
+ server->stat.cell_servers + 1));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("I have %d clients, %d channels, %d servers and "
+ "%d routers",
+ server->stat.my_clients,
+ server->stat.my_channels,
+ server->stat.my_servers,
+ server->stat.my_routers));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("%d server operators and %d router operators "
+ "online",
+ server->stat.my_server_ops,
+ server->stat.my_router_ops));
+ } else {
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("I have %d clients and %d channels formed",
+ server->stat.my_clients,
+ server->stat.my_channels));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("%d operators online",
+ server->stat.my_server_ops));
+ }
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your connection is secured with %s cipher, "
+ "key length %d bits",
+ idata->send_key->cipher->name,
+ idata->send_key->cipher->key_len));
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your current nickname is %s",
+ client->nickname));
-/* Received new channel user packet. Information about new users on a
- channel are distributed between routers using this packet. The
- router receiving this will redistribute it and also sent JOIN notify
- to local clients on the same channel. Normal server sends JOIN notify
- to its local clients on the channel. */
+ /* Send motd */
+ silc_server_send_motd(server, sock);
-void silc_server_new_channel_user(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
-{
- silc_server_new_channel_user_real(server, sock, packet, TRUE);
+ return client;
}
-/* Received New Channel User List packet, list of New Channel User payloads
- inside one packet. Process the payloads one by one. */
+/* Create new server. This processes received New Server packet and
+ saves the received Server ID. The server is our locally connected
+ server thus we save all the information and save it to local list.
+ This funtion can be used by both normal server and router server.
+ If normal server uses this it means that its router has connected
+ to the server. If router uses this it means that one of the cell's
+ servers is connected to the router. */
-void silc_server_new_channel_user_list(SilcServer server,
+SilcServerEntry silc_server_new_server(SilcServer server,
SilcSocketConnection sock,
SilcPacketContext *packet)
{
- SilcPacketContext *new;
- SilcBuffer buffer;
- unsigned short len1, len2;
+ SilcBuffer buffer = packet->buffer;
+ SilcServerEntry new_server;
+ SilcIDCacheEntry cache;
+ SilcServerID *server_id;
+ SilcIDListData idata;
+ unsigned char *server_name, *id_string;
+ unsigned short id_len;
+ int ret;
- SILC_LOG_DEBUG(("Processing New Channel User List"));
+ SILC_LOG_DEBUG(("Creating new server"));
- if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- packet->src_id_type != SILC_ID_SERVER ||
- server->server_type == SILC_SERVER)
- return;
+ if (sock->type != SILC_SOCKET_TYPE_SERVER &&
+ sock->type != SILC_SOCKET_TYPE_ROUTER)
+ return NULL;
- /* If we are router and this packet is not already broadcast packet
- we will broadcast it. Brodcast this list packet instead of multiple
- New Channel User packets. */
- if (!server->standalone && server->server_type == SILC_ROUTER &&
- !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received New Channel User List packet"));
- silc_server_packet_send(server, server->router->connection, packet->type,
- packet->flags | SILC_PACKET_FLAG_BROADCAST,
- packet->buffer->data, packet->buffer->len, FALSE);
+ /* Take server entry */
+ new_server = (SilcServerEntry)sock->user_data;
+ idata = (SilcIDListData)new_server;
+
+ /* Fetch the old server cache entry so that we can update it. */
+ if (!silc_idcache_find_by_context(server->local_list->servers,
+ sock->user_data, &cache)) {
+ SILC_LOG_ERROR(("Lost server's cache entry - bad thing"));
+ return NULL;
}
- /* Make copy of the original packet context, except for the actual
- data buffer, which we will here now fetch from the original buffer. */
- new = silc_packet_context_alloc();
- new->type = SILC_PACKET_NEW_CHANNEL_USER;
- new->flags = packet->flags;
- new->src_id = packet->src_id;
- new->src_id_len = packet->src_id_len;
- new->src_id_type = packet->src_id_type;
- new->dst_id = packet->dst_id;
- new->dst_id_len = packet->dst_id_len;
- new->dst_id_type = packet->dst_id_type;
+ /* Parse the incoming packet */
+ ret = silc_buffer_unformat(buffer,
+ SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
+ SILC_STR_UI16_STRING_ALLOC(&server_name),
+ SILC_STR_END);
+ if (ret == -1) {
+ if (id_string)
+ silc_free(id_string);
+ if (server_name)
+ silc_free(server_name);
+ return NULL;
+ }
- buffer = silc_buffer_alloc(256);
- new->buffer = buffer;
+ if (id_len > buffer->len) {
+ silc_free(id_string);
+ silc_free(server_name);
+ return NULL;
+ }
- while (packet->buffer->len) {
- SILC_GET16_MSB(len1, packet->buffer->data);
- if ((len1 > packet->buffer->len) ||
- (len1 > buffer->truelen))
- break;
+ /* Get Server ID */
+ server_id = silc_id_str2id(id_string, id_len, SILC_ID_SERVER);
+ if (!server_id) {
+ silc_free(id_string);
+ silc_free(server_name);
+ return NULL;
+ }
+ silc_free(id_string);
- SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
- if ((len2 > packet->buffer->len) ||
- (len2 > buffer->truelen))
- break;
+ /* Update client entry */
+ idata->registered = TRUE;
+ new_server->server_name = server_name;
+ new_server->id = server_id;
- silc_buffer_pull_tail(buffer, 4 + len1 + len2);
- silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2);
+ /* Update the cache entry */
+ cache->id = (void *)server_id;
+ cache->type = SILC_ID_SERVER;
+ cache->data = server_name;
+ silc_idcache_sort_by_data(server->local_list->servers);
- /* Process the New Channel User */
- silc_server_new_channel_user_real(server, sock, new, FALSE);
+ /* Distribute the information about new server in the SILC network
+ to our router. If we are normal server we won't send anything
+ since this connection must be our router connection. */
+ if (server->server_type == SILC_ROUTER && !server->standalone &&
+ server->router->connection != sock)
+ silc_server_send_new_id(server, server->router->connection,
+ TRUE, new_server->id, SILC_ID_SERVER,
+ SILC_ID_SERVER_LEN);
- silc_buffer_push_tail(buffer, 4 + len1 + len2);
- silc_buffer_pull(packet->buffer, 4 + len1 + len2);
- }
+ if (server->server_type == SILC_ROUTER)
+ server->stat.cell_servers++;
- silc_buffer_free(buffer);
- silc_free(new);
+ return new_server;
}
-/* Received Remove Channel User packet to remove a user from a channel.
- Routers notify other routers that user has left a channel. Client must
- not send this packet. Normal server may send this packet but must not
- receive it. */
+/* Processes incoming New ID packet. New ID Payload is used to distribute
+ information about newly registered clients and servers. */
-void silc_server_remove_channel_user(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+static void silc_server_new_id_real(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet,
+ int broadcast)
{
SilcBuffer buffer = packet->buffer;
- unsigned char *tmp1 = NULL, *tmp2 = NULL;
- unsigned short tmp1_len, tmp2_len;
- SilcClientID *client_id = NULL;
- SilcChannelID *channel_id = NULL;
- SilcChannelEntry channel;
- SilcClientEntry client;
- int ret;
+ SilcIDList id_list;
+ SilcServerEntry router;
+ SilcSocketConnection router_sock;
+ SilcIDPayload idp;
+ SilcIdType id_type;
+ unsigned char *hash = NULL;
+ void *id;
- SILC_LOG_DEBUG(("Removing user from channel"));
+ SILC_LOG_DEBUG(("Processing new ID"));
if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- packet->src_id_type != SILC_ID_SERVER ||
- server->server_type == SILC_SERVER)
+ server->server_type == SILC_SERVER ||
+ packet->src_id_type != SILC_ID_SERVER)
return;
- ret = silc_buffer_unformat(buffer,
- SILC_STR_UI16_NSTRING_ALLOC(&tmp1, &tmp1_len),
- SILC_STR_UI16_NSTRING_ALLOC(&tmp2, &tmp2_len),
- SILC_STR_END);
- if (ret == -1)
+ idp = silc_id_payload_parse(buffer);
+ if (!idp)
+ return;
+
+ id_type = silc_id_payload_get_type(idp);
+
+ /* Normal server cannot have other normal server connections */
+ if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
goto out;
- client_id = silc_id_str2id(tmp1, tmp1_len, SILC_ID_CLIENT);
- channel_id = silc_id_str2id(tmp2, tmp2_len, SILC_ID_CHANNEL);
- if (!client_id || !channel_id)
+ id = silc_id_payload_get_id(idp);
+ if (!id)
goto out;
- /* If we are router and this packet is not already broadcast packet
- we will broadcast it. The sending socket really cannot be router or
- the router is buggy. If this packet is coming from router then it must
- have the broadcast flag set already and we won't do anything. */
- if (!server->standalone && server->server_type == SILC_ROUTER &&
+ /* If the sender of this packet is server and we are router we need to
+ broadcast this packet to other routers in the network. */
+ if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
sock->type == SILC_SOCKET_TYPE_SERVER &&
!(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received Remove Channel User packet"));
- silc_server_packet_send(server, server->router->connection, packet->type,
- packet->flags | SILC_PACKET_FLAG_BROADCAST,
+ SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
+ silc_server_packet_send(server, server->router->connection,
+ packet->type,
+ packet->flags | SILC_PACKET_FLAG_BROADCAST,
buffer->data, buffer->len, FALSE);
}
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list,
- channel_id, NULL);
- if (!channel)
- goto out;
- }
+ if (sock->type == SILC_SOCKET_TYPE_SERVER)
+ id_list = server->local_list;
+ else
+ id_list = server->global_list;
- /* Get client entry */
- 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)
- goto out;
- }
+ router_sock = sock;
+ router = sock->user_data;
+
+ switch(id_type) {
+ case SILC_ID_CLIENT:
+ {
+ SilcClientEntry entry;
+
+ SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
+ silc_id_render(id, SILC_ID_CLIENT),
+ sock->type == SILC_SOCKET_TYPE_SERVER ?
+ "Server" : "Router", sock->hostname));
+
+ /* As a router we keep information of all global information in our
+ global list. Cell wide information however is kept in the local
+ list. The client is put to global list and we will take the hash
+ value of the Client ID and save it to the ID Cache system for fast
+ searching in the future. */
+ hash = silc_calloc(sizeof(((SilcClientID *)id)->hash),
+ sizeof(unsigned char));
+ memcpy(hash, ((SilcClientID *)id)->hash,
+ sizeof(((SilcClientID *)id)->hash));
+ entry = silc_idlist_add_client(id_list, hash, NULL, NULL, id,
+ router, NULL);
+ entry->nickname = NULL;
+
+ if (sock->type == SILC_SOCKET_TYPE_SERVER)
+ server->stat.cell_clients++;
+ server->stat.clients++;
+
+#if 0
+ /* XXX Adding two ID's with same IP number replaces the old entry thus
+ gives wrong route. Thus, now disabled until figured out a better way
+ to do this or when removed the whole thing. This could be removed
+ because entry->router->connection gives always the most optimal route
+ for the ID anyway (unless new routes (faster perhaps) are established
+ after receiving this ID, this we don't know however). */
+ /* Add route cache for this ID */
+ silc_server_route_add(silc_server_route_hash(
+ ((SilcClientID *)id)->ip.s_addr,
+ server->id->port), ((SilcClientID *)id)->ip.s_addr,
+ router);
+#endif
+ }
+ break;
+
+ case SILC_ID_SERVER:
+ SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
+ silc_id_render(id, SILC_ID_SERVER),
+ sock->type == SILC_SOCKET_TYPE_SERVER ?
+ "Server" : "Router", sock->hostname));
+
+ /* As a router we keep information of all global information in our global
+ list. Cell wide information however is kept in the local list. */
+ silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
+
+ if (sock->type == SILC_SOCKET_TYPE_SERVER)
+ server->stat.cell_servers++;
+ server->stat.servers++;
+
+#if 0
+ /* Add route cache for this ID */
+ silc_server_route_add(silc_server_route_hash(
+ ((SilcServerID *)id)->ip.s_addr,
+ ((SilcServerID *)id)->port),
+ ((SilcServerID *)id)->ip.s_addr,
+ router);
+#endif
+ break;
+
+ case SILC_ID_CHANNEL:
+ SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
+ break;
- /* Remove user from channel */
- silc_server_remove_from_one_channel(server, sock, channel, client, TRUE);
+ default:
+ break;
+ }
out:
- if (tmp1)
- silc_free(tmp1);
- if (tmp2)
- silc_free(tmp2);
- if (client_id)
- silc_free(client_id);
- if (channel_id)
- silc_free(channel_id);
+ silc_id_payload_free(idp);
}
-/* Received notify packet. Server can receive notify packets from router.
- Server then relays the notify messages to clients if needed. */
-void silc_server_notify(SilcServer server,
- SilcSocketConnection sock,
+/* Processes incoming New ID packet. New ID Payload is used to distribute
+ information about newly registered clients and servers. */
+
+void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
SilcPacketContext *packet)
{
- SilcNotifyPayload payload;
- SilcNotifyType type;
- SilcArgumentPayload args;
- SilcChannelID *channel_id;
- SilcClientID *client_id, *client_id2;
- SilcChannelEntry channel;
- SilcClientEntry client;
- unsigned char *tmp;
- unsigned int tmp_len;
+ silc_server_new_id_real(server, sock, packet, TRUE);
+}
- SILC_LOG_DEBUG(("Start"));
+/* Receoved New Id List packet, list of New ID payloads inside one
+ packet. Process the New ID payloads one by one. */
- if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- packet->src_id_type != SILC_ID_SERVER)
- return;
+void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcPacketContext *new_id;
+ SilcBuffer idp;
+ unsigned short id_len;
- /* 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. */
- if (server->server_type != SILC_SERVER &&
- sock->type != SILC_SOCKET_TYPE_ROUTER)
- return;
+ SILC_LOG_DEBUG(("Processing New ID List"));
- payload = silc_notify_payload_parse(packet->buffer);
- if (!payload)
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+ packet->src_id_type != SILC_ID_SERVER)
return;
- type = silc_notify_get_type(payload);
- args = silc_notify_get_args(payload);
- if (!args)
- goto out;
-
- switch(type) {
- case SILC_NOTIFY_TYPE_JOIN:
- /*
- * Distribute the notify to local clients on the channel
- */
- SILC_LOG_DEBUG(("JOIN notify"));
-
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- packet->dst_id_type);
- if (!channel_id)
- goto out;
-
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- silc_free(channel_id);
- goto out;
- }
-
- /* Get client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp) {
- silc_free(channel_id);
- goto out;
- }
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id) {
- silc_free(channel_id);
- goto out;
- }
+ /* If the sender of this packet is server and we are router we need to
+ broadcast this packet to other routers in the network. Broadcast
+ this list packet instead of multiple New ID packets. */
+ if (!server->standalone && server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_SERVER &&
+ !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+ SILC_LOG_DEBUG(("Broadcasting received New ID List packet"));
+ silc_server_packet_send(server, server->router->connection,
+ packet->type,
+ packet->flags | SILC_PACKET_FLAG_BROADCAST,
+ packet->buffer->data, packet->buffer->len, FALSE);
+ }
- /* Send to channel */
- silc_server_packet_send_to_channel(server, NULL, channel, packet->type,
- FALSE, packet->buffer->data,
- packet->buffer->len, FALSE);
+ /* Make copy of the original packet context, except for the actual
+ data buffer, which we will here now fetch from the original buffer. */
+ new_id = silc_packet_context_alloc();
+ new_id->type = SILC_PACKET_NEW_ID;
+ new_id->flags = packet->flags;
+ new_id->src_id = packet->src_id;
+ new_id->src_id_len = packet->src_id_len;
+ new_id->src_id_type = packet->src_id_type;
+ new_id->dst_id = packet->dst_id;
+ new_id->dst_id_len = packet->dst_id_len;
+ new_id->dst_id_type = packet->dst_id_type;
- /* If the the client is not in local list we check global list (ie. the
- channel will be global channel) and if it does not exist then create
- entry for the client. */
- client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
- if (!client) {
- SilcChannelClientEntry chl;
+ idp = silc_buffer_alloc(256);
+ new_id->buffer = idp;
- client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
- if (!client) {
- client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
- client_id, sock->user_data, NULL);
- if (!client) {
- silc_free(channel_id);
- silc_free(client_id);
- goto out;
- }
- }
+ while (packet->buffer->len) {
+ SILC_GET16_MSB(id_len, packet->buffer->data + 2);
+ if ((id_len > packet->buffer->len) ||
+ (id_len > idp->truelen))
+ break;
- /* The channel is global now */
- channel->global_users = TRUE;
+ silc_buffer_pull_tail(idp, 4 + id_len);
+ silc_buffer_put(idp, packet->buffer->data, 4 + id_len);
- /* Now actually JOIN the global client to the channel */
- chl = silc_calloc(1, sizeof(*chl));
- chl->client = client;
- chl->channel = channel;
- silc_list_add(channel->user_list, chl);
- silc_list_add(client->channels, chl);
- } else {
- silc_free(client_id);
- }
- break;
+ /* Process the New ID */
+ silc_server_new_id_real(server, sock, new_id, FALSE);
- case SILC_NOTIFY_TYPE_LEAVE:
- /*
- * Distribute the notify to local clients on the channel
- */
- SILC_LOG_DEBUG(("LEAVE notify"));
+ silc_buffer_push_tail(idp, 4 + id_len);
+ silc_buffer_pull(packet->buffer, 4 + id_len);
+ }
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- packet->dst_id_type);
- if (!channel_id)
- goto out;
+ silc_buffer_free(idp);
+ silc_free(new_id);
+}
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- silc_free(channel_id);
- goto out;
- }
+/* Received New Channel packet. Information about new channels in the
+ network are distributed using this packet. Save the information about
+ the new channel. This usually comes from router but also normal server
+ can send this to notify channels it has when it connects to us. */
- /* Get client ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp) {
- silc_free(channel_id);
- goto out;
- }
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id) {
- silc_free(channel_id);
- goto out;
- }
+void silc_server_new_channel(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ unsigned char *id;
+ SilcChannelID *channel_id;
+ unsigned short channel_id_len;
+ char *channel_name;
+ int ret;
- /* Send to channel */
- silc_server_packet_send_to_channel(server, NULL, channel, packet->type,
- FALSE, packet->buffer->data,
- packet->buffer->len, FALSE);
+ SILC_LOG_DEBUG(("Processing New Channel"));
- /* Get client entry */
- client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
- if (!client) {
- client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
- if (!client) {
- silc_free(client_id);
- silc_free(channel_id);
- goto out;
- }
- }
- silc_free(client_id);
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+ packet->src_id_type != SILC_ID_SERVER ||
+ server->server_type == SILC_SERVER)
+ return;
- /* Remove the user from channel */
- silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
- break;
+ /* Parse payload */
+ ret = silc_buffer_unformat(packet->buffer,
+ SILC_STR_UI16_STRING_ALLOC(&channel_name),
+ SILC_STR_UI16_NSTRING_ALLOC(&id, &channel_id_len),
+ SILC_STR_END);
+ if (ret == -1) {
+ if (channel_name)
+ silc_free(channel_name);
+ if (id)
+ silc_free(id);
+ return;
+ }
+
+ /* Decode the channel ID */
+ channel_id = silc_id_str2id(id, channel_id_len, SILC_ID_CHANNEL);
+ if (!channel_id)
+ return;
- case SILC_NOTIFY_TYPE_SIGNOFF:
- /*
- * Distribute the notify to local clients on the channel
- */
- SILC_LOG_DEBUG(("SIGNOFF notify"));
+ if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+ /* Add the server to global list as it is coming from router. It
+ cannot be our own channel as it is coming from router. */
- /* 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);
- if (!client_id)
- goto out;
+ SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
+ silc_id_render(channel_id, SILC_ID_CHANNEL),
+ sock->hostname));
+
+ silc_idlist_add_channel(server->global_list, channel_name, 0, channel_id,
+ server->router->connection, NULL, NULL);
- /* Get client entry */
- client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
- if (!client) {
- client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
- if (!client) {
- silc_free(client_id);
- goto out;
- }
- }
- silc_free(client_id);
+ server->stat.channels++;
+ } else {
+ /* The channel is coming from our server, thus it is in our cell
+ we will add it to our local list. */
+ SilcChannelEntry channel;
+ SilcBuffer chk;
- /* Remove the client from all channels */
- silc_server_remove_from_channels(server, NULL, client);
+ SILC_LOG_DEBUG(("New channel id(%s) from [Server] %s",
+ silc_id_render(channel_id, SILC_ID_CHANNEL),
+ sock->hostname));
+
+ /* Check that we don't already have this channel */
+ channel = silc_idlist_find_channel_by_name(server->local_list,
+ channel_name, NULL);
+ if (!channel)
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_name, NULL);
- /* Remove the client entry */
- silc_idlist_del_client(server->global_list, client);
- break;
+ /* If the channel does not exist, then create it. We create the channel
+ with the channel ID provided by the server. This creates a new
+ key to the channel as well that we will send to the server. */
+ if (!channel) {
+ channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
+ channel_name,
+ channel_id, FALSE);
+ if (!channel)
+ return;
- case SILC_NOTIFY_TYPE_NICK_CHANGE:
- {
- /*
- * Distribute the notify to local clients on the channel
- */
- unsigned char *id, *id2;
+ /* Send the new channel key to the server */
+ chk = silc_channel_key_payload_encode(channel_id_len, id,
+ 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,
+ chk->data, chk->len, FALSE);
+ silc_buffer_free(chk);
- SILC_LOG_DEBUG(("NICK CHANGE notify"));
-
- /* Get old client ID */
- id = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!id)
- goto out;
- client_id = silc_id_payload_parse_id(id, tmp_len);
- if (!client_id)
- goto out;
-
- /* Get new client ID */
- id2 = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!id2)
- goto out;
- client_id2 = silc_id_payload_parse_id(id2, tmp_len);
- if (!client_id2)
- goto out;
-
- SILC_LOG_DEBUG(("Old Client ID id(%s)",
- silc_id_render(client_id, SILC_ID_CLIENT)));
- SILC_LOG_DEBUG(("New Client ID id(%s)",
- silc_id_render(client_id2, SILC_ID_CLIENT)));
+ } else {
+ /* The channel exist by that name, check whether the ID's match.
+ If they don't then we'll force the server to use the ID we have.
+ We also create a new key for the channel. */
- /* Replace the Client ID */
- client = silc_idlist_replace_client_id(server->global_list, client_id,
- client_id2);
- if (!client)
- client = silc_idlist_replace_client_id(server->local_list, client_id,
- client_id2);
+ if (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
+ /* They don't match, send CHANNEL_CHANGE notify to the server to
+ force the ID change. */
+ SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
+ silc_server_send_notify_channel_change(server, sock, FALSE,
+ channel_id,
+ channel->id,
+ SILC_ID_CHANNEL_LEN);
+ }
- if (client)
- /* Send the NICK_CHANGE notify type to local clients on the channels
- this client is joined to. */
- silc_server_send_notify_on_channels(server, client,
- SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
- id, tmp_len,
- id2, tmp_len);
+ /* Create new key for the channel and send it to the server and
+ everybody else possibly on the channel. */
- silc_free(client_id);
- if (!client)
- silc_free(client_id2);
- break;
- }
- case SILC_NOTIFY_TYPE_TOPIC_SET:
- /*
- * Distribute the notify to local clients on the channel
- */
- SILC_LOG_DEBUG(("TOPIC SET notify (not-impl XXX)"));
- break;
+ silc_server_create_channel_key(server, channel, 0);
- case SILC_NOTIFY_TYPE_CMODE_CHANGE:
- /*
- * Distribute the notify to local clients on the channel
- */
- SILC_LOG_DEBUG(("CMODE CHANGE notify (not-impl XXX)"));
- break;
+ /* Send to the channel */
+ silc_server_send_channel_key(server, sock, channel, FALSE);
- case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
- /*
- * Distribute the notify to local clients on the channel
- */
- SILC_LOG_DEBUG(("CUMODE CHANGE notify (not-impl XXX)"));
- break;
+ /* Send to the server */
+ chk = silc_channel_key_payload_encode(channel_id_len, id,
+ 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,
+ chk->data, chk->len, FALSE);
+ silc_buffer_free(chk);
- /* Ignore rest notify types for now */
- case SILC_NOTIFY_TYPE_NONE:
- case SILC_NOTIFY_TYPE_INVITE:
- case SILC_NOTIFY_TYPE_MOTD:
- default:
- break;
+ /* Since the channel is coming from server and we also know about it
+ then send the JOIN notify to the server so that it see's our
+ users on the channel "joining" the channel. */
+ /* XXX TODO **/
+ }
}
- out:
- silc_notify_payload_free(payload);
+ silc_free(id);
}
-/* Processes incoming REMOVE_ID packet. The packet is used to notify routers
- that certain ID should be removed. After that the ID will become invalid. */
+/* Received New Channel List packet, list of New Channel List payloads inside
+ one packet. Process the New Channel payloads one by one. */
-void silc_server_remove_id(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+void silc_server_new_channel_list(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- SilcIDList id_list;
- SilcIDPayload idp;
- SilcIdType id_type;
- void *id, *id_entry;
+ SilcPacketContext *new;
+ SilcBuffer buffer;
+ unsigned short len1, len2;
- SILC_LOG_DEBUG(("Start"));
+ SILC_LOG_DEBUG(("Processing New Channel List"));
if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- server->server_type == SILC_SERVER ||
- packet->src_id_type != SILC_ID_SERVER)
- return;
-
- idp = silc_id_payload_parse(packet->buffer);
- if (!idp)
+ packet->src_id_type != SILC_ID_SERVER ||
+ server->server_type == SILC_SERVER)
return;
- id_type = silc_id_payload_get_type(idp);
-
- id = silc_id_payload_get_id(idp);
- if (!id)
- goto out;
-
/* If the sender of this packet is server and we are router we need to
- broadcast this packet to other routers in the network. */
+ broadcast this packet to other routers in the network. Broadcast
+ this list packet instead of multiple New Channel packets. */
if (!server->standalone && server->server_type == SILC_ROUTER &&
sock->type == SILC_SOCKET_TYPE_SERVER &&
!(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received Remove ID packet"));
+ SILC_LOG_DEBUG(("Broadcasting received New Channel List packet"));
silc_server_packet_send(server, server->router->connection,
packet->type,
packet->flags | SILC_PACKET_FLAG_BROADCAST,
packet->buffer->data, packet->buffer->len, FALSE);
}
- if (sock->type == SILC_SOCKET_TYPE_SERVER)
- id_list = server->local_list;
- else
- id_list = server->global_list;
+ /* Make copy of the original packet context, except for the actual
+ data buffer, which we will here now fetch from the original buffer. */
+ new = silc_packet_context_alloc();
+ new->type = SILC_PACKET_NEW_CHANNEL;
+ new->flags = packet->flags;
+ new->src_id = packet->src_id;
+ new->src_id_len = packet->src_id_len;
+ new->src_id_type = packet->src_id_type;
+ new->dst_id = packet->dst_id;
+ new->dst_id_len = packet->dst_id_len;
+ new->dst_id_type = packet->dst_id_type;
- /* Remove the ID */
- switch (id_type) {
- case SILC_ID_CLIENT:
- id_entry = silc_idlist_find_client_by_id(id_list, (SilcClientID *)id,
- NULL);
- if (id_entry) {
- /* Remove from channels */
- silc_server_remove_from_channels(server, NULL, id_entry);
-
- /* Remove the client entry */
- silc_idlist_del_client(id_list, (SilcClientEntry)id_entry);
- server->stat.clients--;
- if (sock->type == SILC_SOCKET_TYPE_SERVER &&
- server->server_type == SILC_ROUTER)
- server->stat.cell_clients--;
-
- SILC_LOG_DEBUG(("Removed client id(%s) from [%s] %s",
- silc_id_render(id, SILC_ID_CLIENT),
- sock->type == SILC_SOCKET_TYPE_SERVER ?
- "Server" : "Router", sock->hostname));
- }
- break;
+ buffer = silc_buffer_alloc(512);
+ new->buffer = buffer;
- case SILC_ID_SERVER:
- id_entry = silc_idlist_find_server_by_id(id_list, (SilcServerID *)id,
- NULL);
- if (id_entry) {
- silc_idlist_del_server(id_list, (SilcServerEntry)id_entry);
- server->stat.servers--;
- if (sock->type == SILC_SOCKET_TYPE_SERVER &&
- server->server_type == SILC_ROUTER)
- server->stat.cell_servers--;
-
- SILC_LOG_DEBUG(("Removed server id(%s) from [%s] %s",
- silc_id_render(id, SILC_ID_SERVER),
- sock->type == SILC_SOCKET_TYPE_SERVER ?
- "Server" : "Router", sock->hostname));
- }
- break;
+ while (packet->buffer->len) {
+ SILC_GET16_MSB(len1, packet->buffer->data);
+ if ((len1 > packet->buffer->len) ||
+ (len1 > buffer->truelen))
+ break;
- case SILC_ID_CHANNEL:
- id_entry = silc_idlist_find_channel_by_id(id_list, (SilcChannelID *)id,
- NULL);
- if (id_entry) {
- silc_idlist_del_channel(id_list, (SilcChannelEntry)id_entry);
- server->stat.channels--;
- if (sock->type == SILC_SOCKET_TYPE_SERVER &&
- server->server_type == SILC_ROUTER)
- server->stat.cell_channels--;
-
- SILC_LOG_DEBUG(("Removed channel id(%s) from [%s] %s",
- silc_id_render(id, SILC_ID_CHANNEL),
- sock->type == SILC_SOCKET_TYPE_SERVER ?
- "Server" : "Router", sock->hostname));
- }
- break;
+ SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
+ if ((len2 > packet->buffer->len) ||
+ (len2 > buffer->truelen))
+ break;
- default:
- break;
+ silc_buffer_pull_tail(buffer, 4 + len1 + len2);
+ silc_buffer_put(buffer, packet->buffer->data, 4 + len1 + len2);
+
+ /* Process the New Channel */
+ silc_server_new_channel(server, sock, new);
+
+ silc_buffer_push_tail(buffer, 4 + len1 + len2);
+ silc_buffer_pull(packet->buffer, 4 + len1 + len2);
}
- out:
- silc_id_payload_free(idp);
+ silc_buffer_free(buffer);
+ silc_free(new);
}
-/* Processes received SET_MODE packet. The packet is used to distribute
- the information about changed channel's or client's channel modes. */
+/* Received key agreement packet. This packet is never for us. It is to
+ the client in the packet's destination ID. Sending of this sort of packet
+ equals sending private message, ie. it is sent point to point from
+ one client to another. */
-void silc_server_set_mode(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+void silc_server_key_agreement(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
{
- SilcSetModePayload payload = NULL;
- SilcArgumentPayload args = NULL;
- unsigned short mode_type;
- unsigned int mode_mask;
- unsigned char *tmp, *tmp2;
- unsigned int tmp_len, tmp_len2;
- unsigned char mode[4];
- SilcClientID *client_id;
- SilcChannelID *channel_id = NULL;
+ SilcClientID *id;
+ SilcServerEntry router;
+ SilcSocketConnection dst_sock;
SilcClientEntry client;
- SilcChannelEntry channel;
- SilcChannelClientEntry chl;
-
- if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
- packet->src_id_type == SILC_ID_CLIENT)
- return;
+ SilcIDListData idata;
SILC_LOG_DEBUG(("Start"));
- /* If we are router and this packet is not already broadcast packet
- we will broadcast it. The sending socket really cannot be router or
- the router is buggy. If this packet is coming from router then it must
- have the broadcast flag set already and we won't do anything. */
- if (!server->standalone && server->server_type == SILC_ROUTER &&
- sock->type == SILC_SOCKET_TYPE_SERVER &&
- !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
- SILC_LOG_DEBUG(("Broadcasting received Set Mode packet"));
- silc_server_packet_send(server, server->router->connection, packet->type,
- packet->flags | SILC_PACKET_FLAG_BROADCAST,
- packet->buffer->data, packet->buffer->len, FALSE);
- }
-
- /* Parse Set Mode payload */
- payload = silc_set_mode_payload_parse(packet->buffer);
- if (!payload)
+ if (packet->src_id_type != SILC_ID_CLIENT ||
+ packet->dst_id_type != SILC_ID_CLIENT)
return;
- mode_type = silc_set_mode_get_type(payload);
- args = silc_set_mode_get_args(payload);
- if (!args)
- goto out;
+ if (!packet->dst_id)
+ return;
- mode_mask = silc_set_mode_get_mode(payload);
- SILC_PUT32_MSB(mode_mask, mode);
+ /* Decode destination Client ID */
+ id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+ if (!id) {
+ SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+ return;
+ }
- switch (mode_type) {
- case SILC_MODE_TYPE_CHANNEL:
- /* Get Channel ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!channel_id)
- goto out;
+ /* If the destination belongs to our server we don't have to route
+ the message anywhere but to send it to the local destination. */
+ client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ if (client) {
+ /* It exists, now deliver the message to the destination */
+ dst_sock = (SilcSocketConnection)client->connection;
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list,
- channel_id, NULL);
- if (!channel)
- goto out;
+ /* If we are router and the client has router then the client is in
+ our cell but not directly connected to us. */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ /* We are of course in this case the client's router thus the real
+ "router" of the client is the server who owns the client. Thus
+ we will send the packet to that server. */
+ router = (SilcServerEntry)client->router;
+ idata = (SilcIDListData)router;
+ silc_server_send_key_agreement(server, router->connection,
+ idata->send_key,
+ idata->hmac,
+ packet);
+ return;
}
- /* Get Client ID payload */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- /* Send CMODE_CHANGE notify to local channel */
- silc_server_send_notify_to_channel(server, sock, channel, FALSE,
- SILC_NOTIFY_TYPE_CMODE_CHANGE,
- 2, tmp, tmp_len,
- mode, sizeof(mode));
-
- /* Change the mode */
- channel->mode = mode_mask;
- break;
+ /* Seems that client really is directly connected to us */
+ idata = (SilcIDListData)client;
+ silc_server_send_key_agreement(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
+ }
- case SILC_MODE_TYPE_UCHANNEL:
- /* Get Channel ID */
- tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
- if (!tmp)
- goto out;
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!channel_id)
- goto out;
+ /* Destination belongs to someone not in this server. If we are normal
+ server our action is to send the packet to our router. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ router = server->router;
- /* Get channel entry */
- channel = silc_idlist_find_channel_by_id(server->local_list,
- channel_id, NULL);
- if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list,
- channel_id, NULL);
- if (!channel)
- goto out;
+ /* Send to primary route */
+ if (router) {
+ dst_sock = (SilcSocketConnection)router->connection;
+ idata = (SilcIDListData)router;
+ silc_server_send_key_agreement(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
}
+ return;
+ }
- /* Get Client ID payload */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp)
- goto out;
-
- /* Get target Client ID */
- tmp2 = silc_argument_get_arg_type(args, 3, &tmp_len2);
- if (!tmp2)
- goto out;
- client_id = silc_id_payload_parse_id(tmp2, tmp_len2);
- if (!client_id)
- goto out;
+ /* We are router and we will perform route lookup for the destination
+ and send the packet to fastest route. */
+ if (server->server_type == SILC_ROUTER && !server->standalone) {
+ /* Check first that the ID is valid */
+ client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ if (client) {
+ dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+ router = (SilcServerEntry)dst_sock->user_data;
+ idata = (SilcIDListData)router;
- /* Get target client entry */
- client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
- if (!client) {
- client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
- if (!client) {
- silc_free(client_id);
- goto out;
- }
+ /* Get fastest route and send packet. */
+ if (router)
+ silc_server_send_key_agreement(server, dst_sock,
+ idata->send_key,
+ idata->hmac, packet);
+ return;
}
- silc_free(client_id);
-
- /* Send CUMODE_CHANGE notify to local channel */
- silc_server_send_notify_to_channel(server, sock, channel, FALSE,
- SILC_NOTIFY_TYPE_CUMODE_CHANGE, 2,
- tmp, tmp_len,
- mode, sizeof(mode),
- tmp2, tmp_len2);
-
- /* Get entry to the channel user list */
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
- if (chl->client == client) {
- /* Change the mode */
- chl->mode = mode_mask;
- break;
- }
-
- break;
-
- default:
- break;
}
-
- out:
- if (channel_id)
- silc_free(channel_id);
- if (args)
- silc_argument_payload_free(args);
- if (payload)
- silc_set_mode_payload_free(payload);
}